feat(backend): add blog publishing platform
Some checks failed
Backend CI/CD / test (push) Has been cancelled
Backend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-06-08 21:31:06 +03:30
parent b7b21a6cc6
commit 954e78d0cb
14 changed files with 1334 additions and 278 deletions

View File

@@ -4,7 +4,7 @@ from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from simplemde.widgets import SimpleMDEEditor
from apps.blog.models import Category, Tag, Post, Comment, Like
from apps.blog.models import Category, Tag, Post, PostAsset, Comment, Like, SavedPost
from apps.blog.resources import PostResource, CategoryResource
from core.admin import SoftDeleteListFilter, BaseModelAdmin
@@ -72,7 +72,7 @@ class PostAdminForm(forms.ModelForm):
class PostAdmin(BaseModelAdmin, ImportExportModelAdmin):
form = PostAdminForm
resource_class = PostResource
list_display = ('title', 'author', 'status', 'category', 'is_featured', 'published_at', 'created_at')
list_display = ('title', 'author', 'status', 'category', 'is_featured', 'submitted_at', 'published_at', 'created_at')
list_filter = ('status', 'is_featured', 'category', 'tags', 'created_at', 'published_at', SoftDeleteListFilter)
search_fields = ('title', 'content', 'author__username')
prepopulated_fields = {'slug': ('title',)}
@@ -83,8 +83,11 @@ class PostAdmin(BaseModelAdmin, ImportExportModelAdmin):
('Content', {
'fields': ('title', 'slug', 'content', 'excerpt', 'featured_image')
}),
('SEO', {
'fields': ('seo_title', 'seo_description', 'canonical_url', 'og_title', 'og_description', 'og_image', 'noindex', 'focus_keyword', 'reading_time')
}),
('Metadata', {
'fields': ('author', 'category', 'tags', 'status', 'is_featured', 'published_at')
'fields': ('author', 'category', 'tags', 'status', 'is_featured', 'submitted_at', 'reviewed_at', 'reviewed_by', 'review_note', 'published_at', 'published_by')
}),
('Soft Delete', {
'fields': ('is_deleted', 'deleted_at'),
@@ -92,12 +95,22 @@ class PostAdmin(BaseModelAdmin, ImportExportModelAdmin):
}),
)
readonly_fields = ('deleted_at',)
readonly_fields = ('deleted_at', 'content_html', 'reading_time')
actions = BaseModelAdmin.actions + ['make_published', 'make_draft', 'make_featured', 'restore_posts']
actions = BaseModelAdmin.actions + ['submit_for_review', 'request_changes', 'make_published', 'make_draft', 'make_featured', 'restore_posts']
def submit_for_review(self, request, queryset):
queryset.update(status='submitted')
self.message_user(request, f"Submitted {queryset.count()} posts for review.")
submit_for_review.short_description = "Submit selected posts for review"
def request_changes(self, request, queryset):
queryset.update(status='changes_requested', reviewed_by=request.user)
self.message_user(request, f"Requested changes on {queryset.count()} posts.")
request_changes.short_description = "Request changes on selected posts"
def make_published(self, request, queryset):
queryset.update(status='published')
queryset.update(status='published', published_by=request.user)
self.message_user(request, f"Published {queryset.count()} posts.")
make_published.short_description = "Mark selected posts as published"
@@ -129,7 +142,7 @@ class CommentAdmin(BaseModelAdmin):
'fields': ('post', 'author', 'content')
}),
('Metadata', {
'fields': ('is_approved', 'created_at', 'updated_at')
'fields': ('is_approved', 'hidden_by', 'hidden_at', 'moderation_note', 'created_at', 'updated_at')
}),
('Soft Delete', {
'fields': ('is_deleted', 'deleted_at'),
@@ -153,7 +166,22 @@ class CommentAdmin(BaseModelAdmin):
disapprove_comments.short_description = "Disapprove selected comments"
@admin.register(Like)
class LikeAdmin(BaseModelAdmin):
class LikeAdmin(admin.ModelAdmin):
list_display = ('user', 'post', 'created_at')
list_filter = ('created_at', 'post')
search_fields = ('user__username', 'post__title')
@admin.register(SavedPost)
class SavedPostAdmin(admin.ModelAdmin):
list_display = ('user', 'post', 'created_at')
list_filter = ('created_at', 'post')
search_fields = ('user__username', 'post__title')
@admin.register(PostAsset)
class PostAssetAdmin(BaseModelAdmin):
list_display = ('title', 'post', 'file_type', 'mime_type', 'size', 'uploaded_by', 'created_at')
list_filter = ('file_type', 'mime_type', 'created_at')
search_fields = ('title', 'caption', 'alt_text', 'post__title', 'uploaded_by__username')
readonly_fields = ('size', 'mime_type', 'created_at', 'updated_at', 'deleted_at')