Feat/issue 117 backend blog page#143
Feat/issue 117 backend blog page#143sergeikuz wants to merge 5 commits intohexlet-volunteers:mainfrom
Conversation
maxjamchuk
left a comment
There was a problem hiding this comment.
Отличная работа: блог как отдельный Django-сервис оформлен понятно (модели/админка/урлы/вьюхи/подключение). Перед мёржжем обязательно исправить замечания с CAUTION и WARNING:
CAUTION: починить BlogListView.as_view() (сейчас синтаксическая ошибка);
CAUTION: не отдавать author.email в публичный payload;
WARNING: определиться с политикой публикации (is_published).
Остальные комментарии — рекомендательные (TIP/NOTE).
| from .views import BlogDetailView, BlogListView | ||
|
|
||
| urlpatterns = [ | ||
| path("", BlogListView .as_view(), name="blog_list"), |
There was a problem hiding this comment.
Caution
BlogListView .as_view() — пробел перед точкой делает выражение синтаксически невалидным в Python (будет SyntaxError). Нужно BlogListView.as_view().
There was a problem hiding this comment.
Блокирующий синтаксис → чинить до мёрджа
| 'is_published': post.is_published, | ||
| 'duration_minutes': post.duration_minutes, | ||
| 'category': post.category.name, | ||
| 'author': post.author.email, |
There was a problem hiding this comment.
Warning
BlogDetailView отдаётся post.author.email. Это утечка персональных данных (публичная страница блога не должна раздавать email). Лучше отдавать first_name/username/display_name.
There was a problem hiding this comment.
Warning
в списке и деталке нет фильтра по is_published а это значит, что потенциально можно показывать черновики и скрытые посты. Обычно делаем так: qs = qs.filter(is_published=True) и в detail: get_object_or_404(BlogPost, pk=pk, is_published=True).
| page_obj = paginator.get_page(page_number) | ||
|
|
||
| posts = list(page_obj.object_list.values( | ||
| 'id', 'title', 'content_short', 'content_full', |
There was a problem hiding this comment.
Tip
в списке отдаётся content_full для каждого поста — это может быть жирно по трафику. Для списка обычно достаточно content_short, а content_full — только в детальном представлении.
| Q(title__icontains=q) | Q(content_full__icontains=q) | ||
| ) | ||
|
|
||
| paginator = Paginator(qs, 6) |
There was a problem hiding this comment.
Tip
стоит добавить стабильную сортировку перед пагинацией (order_by('-created_at')), иначе порядок страниц может «прыгать».
| 'duration_minutes': post.duration_minutes, | ||
| 'category': post.category.name, | ||
| 'author': post.author.email, | ||
| 'tags': list(post.tags.values_list('name', flat=True)), |
There was a problem hiding this comment.
Tip
можно оптимизировать: select_related('category','author').prefetch_related('tags'), чтобы не ловить лишние запросы на связанных данных.
| readonly_fields = ("created_at", "updated_at") | ||
|
|
||
| def get_tags(self, obj): | ||
| return ", ".join([tag.name for tag in obj.tags.all()]) |
There was a problem hiding this comment.
Tip
get_tags сейчас делает obj.tags.all() и собирает имена. На списке админки это может дать проблему N+1. Опционально: переопределить get_queryset() и сделать prefetch_related('tags'), а в get_tags использовать values_list.
| class BlogPostAdmin(admin.ModelAdmin): | ||
| list_display = ("id", "title", "author", "category", "created_at", | ||
| "is_published") | ||
| list_filter = ("author", "category", "title", "tags") |
There was a problem hiding this comment.
Tip
list_filter = ("author", "category", "title", "tags") — фильтрация по заголовку ("title") часто не очень полезна и может раздувать список значений; обычно title отправляем в search_fields.
Issue #117: Implement blog system for /blog page
Created models: BlogPost, BlogCategory, Tag.
Implemented views: BlogListView and BlogDetailView (with InertiaJS support).
Added filtering by category (via GET parameter).
Implemented search by title and full_content (via q GET parameter).
Added pagination using Django’s Paginator (6 items per page).