Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion yatube/about/urls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.urls import path
from . import views
from . import views # импорты не по pep8


app_name = 'about'
Expand Down
2 changes: 1 addition & 1 deletion yatube/about/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.views.generic.base import TemplateView


# не должно оставаться учебных комментариев. засоряется код
class AboutAuthorView(TemplateView):
# В переменной template_name обязательно указывается имя шаблона,
# на основе которого будет создана возвращаемая страница
Expand Down
3 changes: 3 additions & 0 deletions yatube/core/context_processors/year.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Для времени в джанге используй `django.utils.timezone`, тогда будет учитываться часовой пояс. Время и разница в часовых поясах это извечная проблема приложений, работающих с пользователями в различных часовых поясах. На первый взгляд подобная мелочь может вызвать довольно много ошибок.
# https://docs.djangoproject.com/en/4.1/ref/utils/#module-django.utils.timezone
# https://stackoverflow.com/questions/10783864/django-1-4-timezone-now-vs-datetime-datetime-now
from datetime import datetime


Expand Down
2 changes: 1 addition & 1 deletion yatube/core/tests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.test import TestCase, Client


# если это метод у тестированию, то он должен быть в ViewTestClass
def setUp(self) -> None:
self.guest_client = Client()

Expand Down
7 changes: 6 additions & 1 deletion yatube/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ def page_not_found(request, exception):
# выводить её в шаблон пользовательской страницы 404 мы не станем
return render(request, 'core/404.html', {'path': request.path}, status=404)


# добавить сттус 403
def csrf_failure(request, reason=''):
return render(request, 'core/403csrf.html')

# должны быть еще вьюхи:
# 1. на обработку 500 ошибки, server_error
# 2. на обработку 403 ошибки, permission_denied
# соответственно, на обе вьюхи кастомные шаблоны
4 changes: 2 additions & 2 deletions yatube/posts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class PostAdmin(admin.ModelAdmin):
empty_value_display = '-пусто-'


admin.site.register(Post, PostAdmin)
admin.site.register(Post, PostAdmin) # Чтобы не выносить отдельно регистрацию модели, можно использовать декоратор- это предпочтительный способ. Особенно, когда моделей много. https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#django.contrib.admin.register


class GroupAdmin(admin.ModelAdmin):
Expand All @@ -27,7 +27,7 @@ class CommentAdmin(admin.ModelAdmin):
list_display = ('pk', 'post', 'author', 'text', 'created',)
search_fields = ('text',)
list_filter = ('author',)
search_fields = ('author', 'created')
search_fields = ('author', 'created') # дубль на 28 строке


admin.site.register(Comment, CommentAdmin)
Expand Down
2 changes: 2 additions & 0 deletions yatube/posts/migrations/0002_auto_20230228_1135.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Миграции стоит называть осознанно(не xxxx_auto....), иначе в разросшихся миграциях будет непонятно, что и где было- будут сплошные авто-миграции. Если меняется нейминг миграций, уже примененных на БД, надо будет быть аккуратным- порядок миграций хранится в таблице (django_migrations) + в самих миграциях есть в зависимость на предыдущей миграции. Хорошая практика- 1 миграция на одну наработку.
# https://nextlinklabs.com/insights/naming-django-migrations-improving-projects 
# Generated by Django 2.2.9 on 2023-02-28 11:35

from django.db import migrations, models
Expand Down
5 changes: 3 additions & 2 deletions yatube/posts/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# импорты не по pep8
from django.db import models

from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -33,13 +34,13 @@ def __str__(self):
return self.text[:15]

class Meta:
ordering = ['-pub_date']
ordering = ['-pub_date'] # Для неизменяемых последовательностей лучше использовать `tuple` https://www.programiz.com/python-programming/list-vs-tuples
default_related_name = 'posts'


class Group(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=50, null=False, unique=True)
slug = models.SlugField(max_length=50, null=False, unique=True) # не нужно указывать max_length для SlugField, по умолчанию он уже 50 символов. Указываем, только если нужно имзенить. `null=False,` уже идет по умолчанию. Лишний флаг
description = models.TextField()

def __str__(self):
Expand Down
3 changes: 3 additions & 0 deletions yatube/posts/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# импорты не по pep8
from django.urls import path
from . import views

app_name = 'posts'


# удали учебные комментарии
urlpatterns = [
# Главная страница
path('', views.index, name='index'),
Expand Down
44 changes: 26 additions & 18 deletions yatube/posts/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# импорты не по зуз8
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator

Expand All @@ -8,39 +9,40 @@


def index(request):
post_list = Post.objects.all().select_related('author')
post_list = Post.objects.all().select_related('author') # стоит также сделать связь с `group`, чтобы избежать лишних хинтов в БД
page_obj = paginator(request, post_list)
title = 'Главная страница сайта Yatube'
title = 'Главная страница сайта Yatube' # Не стоит превращать view в передачу статичных аргументов в шаблон. Используй `block title` для переопределения нужного блока. https://docs.djangoproject.com/en/4.0/ref/templates/language/#templates
context = {
'page_obj': page_obj,
'title': title
}
template = 'posts/index.html'
template = 'posts/index.html' # Нет необходимости выносить в отдельную переменную template, можно сразу прокинуть шаблон в render() вторым аргументом- читабельность не потеряется.
return render(request, template, context)


def group_posts(request, slug):
group = get_object_or_404(Group, slug=slug)
posts = group.posts.all()[:10]
posts = group.posts.all()[:10] # обрезка постов должна быть в отпагинированном объекте `page_obj`. Сейчас же в пагинацию не попадет никогда больше 10 постов группы
page_obj = paginator(request, posts)
context = {
'group': group,
'posts': posts,
'posts': posts, # вот это лишнее. Все объекты передаем в `page_obj`, отпагинированными.
'page_obj': page_obj
}
template = 'posts/group_list.html'
return render(request, template, context)


def profile(request, username):
author = User.objects.get(username=username)
post_list = author.posts.all()
author = User.objects.get(username=username) # Используй `get_object_or_404(...)`, чтобы в случае отсутствия модели была 404 страничка. https://docs.djangoproject.com/en/4.0/topics/http/shortcuts/#get-object-or-404
post_list = author.posts.all() # выборку принято называть с префиксом `queryset` `posts_queryset`, например. Тут же не список постов. Было бы здорово использовать `select_related`
page_obj = paginator(request, post_list)
title = f'Профайл пользователя {username}'
all_posts = post_list.count()
title = f'Профайл пользователя {username}' # убрать в block tittle
all_posts = post_list.count() # дай подходящий нейминг. тут хранится `количество` постов

following = (
request.user.is_authenticated
and Follow.objects.filter(
and Follow.objects.filter( # выборка `Follow` должна проверять: подписан ли `текущий пользователь` на `автора`. Должно быть комплексное условие по двум полям
author__following__user=request.user
).exists()
)
Expand All @@ -58,7 +60,7 @@ def post_detail(request, post_id):
post = get_object_or_404(Post, pk=post_id)
posts_count = post.author.posts.count()
context = {
'title': post.text,
'title': post.text, # пост уже передается, не нужно отдельно вытягивать его атрибуты во вьюхе. Это можно сделать прямо в б=шаблоне
'post_count': posts_count,
'post': post,
'comments': post.comments.all(),
Expand All @@ -78,14 +80,20 @@ def post_create(request):
return redirect('posts:profile', username=post.author)
context = {
'form': form,
'is_edit': False
'is_edit': False # не обязательно передавать. Ошибки в шаблоне не будет, если переменной нет.
}
return render(request, 'posts/create_post.html', context)


@login_required
def post_edit(request, post_id):
post = get_object_or_404(Post, pk=post_id)
# Тут происходит лишний хинт в БД. Есть несколько вариантов избежать этого:
#
# - Использовать `select_related`, подгружая нужные поля заранее;
# - Сравнивать не модели, а их `id` `request.user !=edit_post.author_id`
#
# https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related
if request.user != post.author:
return redirect('posts:post_detail', post_id)
form = PostForm(request.POST or None,
Expand All @@ -101,9 +109,9 @@ def post_edit(request, post_id):
}
return render(request, 'posts/create_post.html', context)


# утилитам самое место в отдельном файле, например, `utils.py`. Во `views.py` только вьюхи
def paginator(request, posts):
paginator = Paginator(posts, 10)
paginator = Paginator(posts, 10) # 10 вынеси в константу в settings.py, например
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return page_obj
Expand All @@ -124,7 +132,7 @@ def add_comment(request, post_id):
@login_required
def follow_index(request):
post_list = Post.objects.filter(
author__following__user=request.user or None
author__following__user=request.user or None # or None в выборке никак не поможет. Тут формируется queryset, а не форма
).all()
page_obj = paginator(request, post_list)
return render(
Expand All @@ -137,16 +145,16 @@ def follow_index(request):

@login_required
def profile_follow(request, username):
author = User.objects.get(username=username)
author = User.objects.get(username=username) # get_object_or_404
if request.user != author:
Follow.objects.get_or_create(user=request.user, author=author)
return redirect('posts:profile', username=author.username)


@login_required
def profile_unfollow(request, username):
author = User.objects.get(username=username)
author = User.objects.get(username=username) # get_object_or_404
following = Follow.objects.filter(user=request.user, author=author)
if following:
if following: # exists(), чтобы просто проверить существование объектов, а не тянуть их из БД
following.delete()
return redirect('posts:profile', username=author.username)
3 changes: 2 additions & 1 deletion yatube/templates/about/author.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
{% block content %}
<h1>Привет, я автор этого проекта</h1>
<div>
{#Поработай над порядком в верстке. Принцип такой: каждый вложенный тег имеет на 1 табуляцию больше, чем родительский. Закрывающий тег на уровне с открывающим(по вертикали).#}
{% load static %}
<img src="{% static 'img/self.png' %}"
</div>
Expand All @@ -11,7 +12,7 @@ <h1>Привет, я автор этого проекта</h1>
<h3>Меня зовут Ляховицкая Наталья.<h3>
</p>
</div>
<p>
<p>
По образованию я магистр изящных искусств.<br>
По жизни разработчик<br>
По факту просто хороший человек.<br>
Expand Down
1 change: 1 addition & 0 deletions yatube/templates/about/tech.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{% comment %} Колонки с отступом сверху и снизу {% endcomment %}
<div class="row">
<h1>Многое из того, что сделано, делалось долго.</h1>
{#Оставлять комментарии в коде- плохой тон. Код стоит держать в чистоте.#}
{% comment %}
Боковой блок со списком технологий
Займет всю ширину блока на мобильном
Expand Down
8 changes: 4 additions & 4 deletions yatube/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Загружаем фав-иконки -->
<link rel="icon" href="img/fav/fav.ico" type="image">
<link rel="apple-touch-icon" sizes="180x180" href="img/fav/apple-touch-icon.png">
<link rel="apple-touch-icon" sizes="180x180" href="img/fav/apple-touch-icon.png"> {# ссылки луче формировать через {% static ... $} https://docs.djangoproject.com/en/4.1/howto/static-files/ #}
<link rel="icon" type="image/png" sizes="32x32" href="img/fav/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="img/fav/favicon-16x16.png">
<meta name="msapplication-TileColor" content="#000">
<meta name="theme-color" content="#ffffff">
<!-- Подключен файл со стандартными стилями бустрап -->
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<title>{{ title }}</title>
<title>{{ title }}</title> {# вот тут самое место для нового блока, block title #}
</head>
<body>
<header>
<header> {#тег уже есть в includes/header.html#}
{% include 'includes/header.html' %}
</header>
<main>
Expand All @@ -32,7 +32,7 @@
<!-- border-top: создаёт тонкую линию сверху блока -->
<!-- text-center: выравнивает текстовые блоки внутри блока по центру -->
<!-- py-3: контент внутри размещается с отступом сверху и снизу -->
<footer class="border-top text-center py-3">
<footer class="border-top text-center py-3"> {#тег уже есть в includes/footer.html#}
{% include 'includes/footer.html' %}
</footer>
</body>
Expand Down
2 changes: 1 addition & 1 deletion yatube/templates/includes/footer.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<footer class="border-top text-center py-3">
<p>© {{ year }} Copyright <span style="color:red">Ya</span>tube</p>
<p>© {{ year }} Copyright <span style="color:red">Ya</span>tube</p> {#Забыла закрывающий тег для footer#}
2 changes: 1 addition & 1 deletion yatube/templates/includes/post.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
{% thumbnail post.image "960x339" crop="center" upscale=True as im %}
<img class="card-img my-2" src="{{ im.url }}">
{% endthumbnail %}
<p>{{ post.text }}</p>
<p>{{ post.text }}</p> {# Вероятно тут должна быть обрезка текста, если далее идет детализация? #}
<a href="{% url 'posts:post_detail' post.id %}"> Полный текст.
</a>
<br>
Expand Down
2 changes: 1 addition & 1 deletion yatube/templates/posts/create_post.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block title %}{% if is_edit %}Редактировать пост{% else %}Новый пост{% endif %}{% endblock %}
{% block header %}{% if is_edit %}Редактировать запись{% else %}Создание новой записи{% endif %}{% endblock %}№
{% block header %}{% if is_edit %}Редактировать запись{% else %}Создание новой записи{% endif %}{% endblock %}№ {# такого блока нет в базовом шаблоне #}
{% block content %}
{% load user_filters %}
<div class="row justify-content-center">
Expand Down
2 changes: 1 addition & 1 deletion yatube/templates/posts/follow.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load thumbnail %}
{% block title %}Подписки{% endblock %}
{% block header %}Подписки{% endblock %}
{% block header %}Подписки{% endblock %} {# такого блока нет в базовом шаблоне #}
{% load cache %}
{% block content %}
<div class="container py-5">
Expand Down
2 changes: 1 addition & 1 deletion yatube/templates/posts/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
{% endfor %}
{% endcache %}
</div>
{% include 'posts/includes/paginator.html' %}
{% include 'posts/includes/paginator.html' %} {# стоит заключить в тот же div #}
{% endblock %}
4 changes: 3 additions & 1 deletion yatube/templates/posts/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
{% block content %}
<div class="mb-5">
<h1>{{ title }}</h1>
<h3>Всего постов:{{ all_posts }}</h3>
<h3>Всего постов:{{ all_posts }}</h3>

{# стоит добавить условия: 1. отображать кнопки только авторизованным пользователям 2. Отображать только пользователям, находящимся НЕ НА СВОЕМ ПРОФИЛЕ #}
{% if following %}
<a
class="btn btn-lg btn-light"
Expand Down
1 change: 1 addition & 0 deletions yatube/users/admin.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# удали пустой файл
# Register your models here.
2 changes: 1 addition & 1 deletion yatube/users/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@


class CreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
class Meta(UserCreationForm.Meta): # тут не обязательно прописывать `(UserCreationForm.Meta)` для `Meta`
model = User
fields = ('first_name', 'last_name', 'username', 'email')
1 change: 1 addition & 0 deletions yatube/users/models.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# удали пустой файл
# Create your models here.
1 change: 1 addition & 0 deletions yatube/users/tests.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# удали пустой файл
# Create your tests here.
1 change: 1 addition & 0 deletions yatube/users/urls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# импорты не по pep8
from django.contrib.auth.views import LoginView, LogoutView

from django.urls import path
Expand Down
1 change: 1 addition & 0 deletions yatube/users/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# импорты не по pep8
from django.views.generic import CreateView

from django.urls import reverse_lazy
Expand Down
20 changes: 19 additions & 1 deletion yatube/yatube/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,25 @@


# Application definition

# Сгруппируй приложения. Так код будет более читабелен:
#
# ```
# [
# 1. Дефолтные
# # пустая строка
# 2. Твои приложения(posts, users, и тп)
# # пустая строка
# 3. Внешние приложения, вроде 'sorl.thumbnail'.
# ]
# ```
#
# А еще лучше, разбей на отдельные константы, каждая из которыех будет содержать отдельные пакеты.
#
# `DJANGO_APPS`: джанговские встроенные приложения
# `THIRD_PARTY_APPS`: внешние приложения
# `LOCAL_APPS`: разрабатываемые приложения
#
# `INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS`
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
Expand Down