From 65c941f8acf839b7e693abf89596096d5a91a333 Mon Sep 17 00:00:00 2001 From: lovely90133 Date: Thu, 30 Apr 2026 15:25:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(=E5=8D=9A=E5=AE=A2):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=96=87=E7=AB=A0=E8=8D=89=E7=A8=BF=E5=92=8C=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E7=8A=B6=E6=80=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现文章草稿和发布状态管理功能,包括: - 修改博客模型状态选项为草稿和发布 - 添加更新时间和状态字段 - 实现发布和转为草稿的视图 - 在文章列表和表单中添加状态管理UI - 更新相关视图和模板以支持新状态 --- .../0019_blog_updated_at_alter_blog_status.py | 23 ++++++++ blog/models.py | 9 +-- blog/views.py | 22 ++++--- dashboard/urls.py | 2 + dashboard/views.py | 54 ++++++++++++++---- db.sqlite3 | Bin 241664 -> 262144 bytes src/urls.py | 1 - templates/dashboard/post/all_post.html | 13 ++++- templates/dashboard/post/create_post.html | 7 +++ templates/dashboard/post/edit_post.html | 7 +++ 10 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 blog/migrations/0019_blog_updated_at_alter_blog_status.py diff --git a/blog/migrations/0019_blog_updated_at_alter_blog_status.py b/blog/migrations/0019_blog_updated_at_alter_blog_status.py new file mode 100644 index 00000000..7e6b0d1a --- /dev/null +++ b/blog/migrations/0019_blog_updated_at_alter_blog_status.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2 on 2026-04-30 07:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0018_reply'), + ] + + operations = [ + migrations.AddField( + model_name='blog', + name='updated_at', + field=models.DateTimeField(auto_now=True), + ), + migrations.AlterField( + model_name='blog', + name='status', + field=models.CharField(choices=[('draft', 'Draft'), ('published', 'Published')], default='draft', max_length=20), + ), + ] diff --git a/blog/models.py b/blog/models.py index d7e4f090..2358dd7e 100644 --- a/blog/models.py +++ b/blog/models.py @@ -27,9 +27,9 @@ def __str__(self): # Blog model class Blog(models.Model): - status = ( - ('active','active'), - ('pending','pending') + STATUS_CHOICES = ( + ('draft', 'Draft'), + ('published', 'Published'), ) title = models.CharField(max_length=200, null=True) @@ -38,13 +38,14 @@ class Blog(models.Model): #catagories = models.ManyToManyField(Catagory) catagories = models.ForeignKey(Catagory,on_delete=models.DO_NOTHING, null=True) tags = models.ManyToManyField(Tag, blank=True) - status = models.CharField(max_length=20, choices=status, default='pending') + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft') #show_hide = models.CharField(max_length=5,choices=visibility, default='show') author = models.ForeignKey(Author, on_delete=models.CASCADE) featured = models.BooleanField(default=False) visit_count = models.IntegerField(default=0) visible = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name_plural = 'Blog' diff --git a/blog/views.py b/blog/views.py index 18b3f934..e97e39ba 100644 --- a/blog/views.py +++ b/blog/views.py @@ -38,8 +38,8 @@ def get(self,request,*args,**kwargs): # } ''' - featured_obj = Blog.objects.all().filter(status='active', visible=True, featured=True).order_by('catagories','-created_at')[:5] - post_obj = Blog.objects.all().filter(status='active', visible=True).order_by('catagories','-created_at') + featured_obj = Blog.objects.all().filter(status='published', visible=True, featured=True).order_by('catagories','-created_at')[:5] + post_obj = Blog.objects.all().filter(status='published', visible=True).order_by('catagories','-created_at') # As per Templates Views first_post = featured_obj.first() s_post = featured_obj[1] @@ -58,9 +58,17 @@ def get(self,request,*args,**kwargs): class SingleBlogView(View): def get(self,request,id,*args,**kwargs): post_obj = get_object_or_404(Blog, id=id) + + # 检查文章状态,如果是草稿,只有作者才能查看 + if post_obj.status == 'draft': + if not request.user.is_authenticated or request.user.author != post_obj.author: + # 非作者用户尝试访问草稿,显示404或重定向 + messages.warning(request, 'This post is not available') + return redirect('home') + post_obj.visit_count = post_obj.visit_count + 1 post_obj.save() - releted_post = Blog.objects.filter(author=post_obj.author).exclude(id=id).order_by('-id')[:4] + releted_post = Blog.objects.filter(author=post_obj.author, status='published', visible=True).exclude(id=id).order_by('-id')[:4] # As per templates views first_post = releted_post.first() last_post = releted_post[1:] @@ -79,10 +87,10 @@ def get(self,request,slug,*args,**kwargs): catagory_obj = get_object_or_404(Catagory, slug=slug) #post = catagory_obj.blog_set.all().order_by('-id') post = Blog.objects.filter(catagories= catagory_obj,\ - status='active',visible=True)\ + status='published',visible=True)\ .order_by('-created_at') popular = Blog.objects.filter(catagories= catagory_obj,\ - status='active',visible=True)\ + status='published',visible=True)\ .annotate(post_count=Count('visit_count'))\ .order_by('-visit_count') # as Per templates views @@ -104,7 +112,7 @@ def get(self,request,slug,*args,**kwargs): class TagView(View): def get(self,request,id,*args,**kwargs): tag_obj = get_object_or_404(Tag, id=id) - post = tag_obj.blog_set.all().order_by('-id') + post = tag_obj.blog_set.filter(status='published', visible=True).order_by('-id') tag_count = post.count() context={ 'tag':tag_obj, @@ -131,7 +139,7 @@ def post(self, request,*args,**kwargs): class SearchView(View): def get(self,request,*args,**kwargs): search = request.GET['q'] - post = Blog.objects.filter(status='active',visible=True) + post = Blog.objects.filter(status='published', visible=True) if len(search) > 100: posts = post.none() else: diff --git a/dashboard/urls.py b/dashboard/urls.py index 5ab637fb..0640c9fb 100644 --- a/dashboard/urls.py +++ b/dashboard/urls.py @@ -13,6 +13,8 @@ path('post_listing_pending/', views.PostListingPending.as_view(), name='post_listing_pending'), path('visible/post/', views.VisiblePost.as_view(), name='visible'), path('hidden/post/', views.HidePost.as_view(),name='hidden'), + path('publish/post/', views.PublishPost.as_view(), name='publish'), + path('draft/post/', views.MakeDraft.as_view(), name='draft'), # Author path('profile/',views.AuthorProfile.as_view(), name='profile'), path('profile/edit/', views.EditAuthor.as_view(), name="edit"), diff --git a/dashboard/views.py b/dashboard/views.py index 4e235b0b..703499d1 100644 --- a/dashboard/views.py +++ b/dashboard/views.py @@ -24,20 +24,20 @@ def get(self,request,*args,**kwargs): user = request.user post = user.author.blog_set.all() post_count = post.count() - post_active = user.author.blog_set.filter(status='active') - post_active_count = post_active.count() - post_pending = user.author.blog_set.filter(status='pending') - post_pending_count = post_pending.count() + post_published = user.author.blog_set.filter(status='published') + post_published_count = post_published.count() + post_draft = user.author.blog_set.filter(status='draft') + post_draft_count = post_draft.count() # showing the sum of visit count of spacific users post_visit_count = post.aggregate(Sum('visit_count'))['visit_count__sum'] context= { 'user':user, 'post':post, 'post_count':post_count, - 'post_active':post_active, - 'post_pending':post_pending, - 'post_active_count':post_active_count, - 'post_pending_count':post_pending_count, + 'post_published':post_published, + 'post_draft':post_draft, + 'post_published_count':post_published_count, + 'post_draft_count':post_draft_count, 'count':post_visit_count } @@ -157,7 +157,7 @@ def dispatch(self,request,*args,**kwargs): def get(self,request,*args,**kwargs): user = request.user - post_active = user.author.blog_set.filter(status='active').order_by('-id') + post_active = user.author.blog_set.filter(status='published').order_by('-id') context={ 'post_active':post_active } @@ -171,7 +171,7 @@ def dispatch(self,request,*args,**kwargs): def get(self,request,*args,**kwargs): user = request.user - post_pending = user.author.blog_set.filter(status='Pending').order_by('-id') + post_pending = user.author.blog_set.filter(status='draft').order_by('-id') context={ 'post_pending':post_pending } @@ -310,8 +310,9 @@ def post(self,request): #tag_obj = Tag.objects.get(name=tag) category = request.POST.get('category') cat_obj = Catagory.objects.get(name=category) + status = request.POST.get('status', 'draft') - post_obj = Blog(author=author,title=title, detail=detail,image=image,catagories=cat_obj) + post_obj = Blog(author=author,title=title, detail=detail,image=image,catagories=cat_obj, status=status) post_obj.save(post_obj) messages.success(request,'created Post Successfully') return redirect('all_post') @@ -367,6 +368,8 @@ def post(self,request,id): obj.image = request.FILES.get('image') category = request.POST.get('category') obj.cat_obj = Catagory.objects.get(name=category) + status = request.POST.get('status', obj.status) + obj.status = status obj.save() messages.success(request,'Post has been Updated') return redirect('all_post') @@ -400,6 +403,35 @@ def get(self,request,id): # Redirect To the Same Page return HttpResponseRedirect(request.META.get('HTTP_REFERER')) +# Publish Post +class PublishPost(View): + @method_decorator(login_required(login_url='login')) + def dispatch(self,request,*args,**kwargs): + return super().dispatch(request,*args,**kwargs) + + def get(self,request,id): + obj = Blog.objects.get(id=id) + obj.status = 'published' + obj.save() + messages.success(request,'Post has been Published') + # Redirect To the Same Page + return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + +# Make Draft +class MakeDraft(View): + @method_decorator(login_required(login_url='login')) + def dispatch(self,request,*args,**kwargs): + return super().dispatch(request,*args,**kwargs) + + def get(self,request,id): + obj = Blog.objects.get(id=id) + obj.status = 'draft' + obj.save() + messages.success(request,'Post is now a Draft') + + # Redirect To the Same Page + return HttpResponseRedirect(request.META.get('HTTP_REFERER')) + # Delete Posts class DeletePost(View): diff --git a/db.sqlite3 b/db.sqlite3 index 6ed20ac7298a081bac999fcfbf2814b67a21ba5b..4d352cdad4aa81c6a86372df08ae08fd4370ba97 100644 GIT binary patch delta 1358 zcma)+e`p(J7{}iqce!hlyY~)>TYu&{=B74D-^(>=n^~JO<}8BDAH$W2X=-a*X|*ot ztUqRxF-j+-u7viT?dk%938F=b_5@Mt{*gJ9PGo=0(oK*JtuRp0D(IUlRR0*nKb|kg z`{Vn3p67kI%$l0XXs4?6bpSw|M;i~3$Jf&VOI!z`-0vUs&JrcXmwbl8wLzT7OWHvs zP85^c-Qq)?4I=P$b@U89-%qL8nkaoFMn`&+>Av1XGMz{crTdO-uBOt-^k^zf!_gpZ z3R={l%`vknW`#pxlSZ1&;$`0hZpBO9BUi}d>IUgoe<;?ETv78Iq32*AKiD`68;;W@ z3Iz@8;3Sjc@VJoG49U*LVcIXL7l4j00;YDu?OxI?yhHrzD!vFW*nw_13puZZ*ySx|KEq^IE7;T9?~a*y<#_>tHmETR{A$&-SR zb!9bn=nR^`FJ}q6$13;s^I1HuZ}QOW-J3Dwz0$8f*&Fdq^# zNFzbB#V~1%w#KLxGQ-W`a8y*3+fERTG+B}4)9JyZeN)eBD%d@r>Z~X^qmD}3^#ZPw z*u7)8E>P}r2^WdyfOlOyS2PVK>QnnA71Ym1Htjg5eeFT~my4|ja8(nQaXH5cO;o|~ z$z%H+oKm6nUqEX6_u?7E-W^6&jaVk-OdKq@3&)-yFhH1sDNQ_28memq%jbgdFT&}5?R28(&r=HjX9{1rp?K`?~2HJ;*P!QV7 zDg2bcasyalg#ldq|GElCf&3L5<@kdf|Dd9R_POz6TS-3m<4XJ6NAN8dyL1TGnq@Jm zmp5Q8wP(xW$sbX53G+)=8^63cK${J)VrDo*BNmN@k?bqyd_IF|(a5Zk>B4Tk^n~%=kNfNz1Aitu`~Q)+@w)pT{@GVH delta 1884 zcmZuxeM}Q)7=J%{y=%+$ih{^MsE$bxTJEDQrOW{{PT8X3SJ*OKJ!mNv+9FqkxG<RxvAoQ?0Dvm8=C7}hmr2xJ;BI*Fx*yy2yOnT#rxSO!&T5 zwi^yZxLUo}440~Z455SSMHS_%ZNq59RFVSg)c#SF51&c@A0I{a>h1&_%S&;!bWU+G zOzld_P3_^P_CzGuEGOkiqEowE3lY+`w!poNcBBSA2cdNW-o(6S8iST`+mA%ppWGep z7w*bLX8&iLdL@W#YiSBYh$+K549gkTu3N+!Hp9x@0t`0=xWBmT+$@PPO(OlkO_9`% zTeIvpFaWVb-INW)-ljI>F|`n?D=$OdvPI2Az5jW6*<&GmaK$Y4+Fu@rcNaf{XXMc+n~N0$zVW@Vb3INfiAQ?loC~wQci*Qk2?!jf^VzK>BzA_+?+v^r2Uh??4 zX>bN?D%G`Fpqe`coqa?akrCD-5q0uoLb~FiPPscMheE+*g4DA!8Qj;lrz0r1JK|3W zk#sPRe!D9Lk0^>H`=G77B%Sz8A-8k0yuY)neLJ>-l4{nVu%oq)NYmAklsOZ6o*yNX zPCH5O!lXTlrMCi`sawWq4#sE~h9>9~V|+Hcg)#bvF*=ILu#&&PL6&CeKz|pys9CTS z9L4r2e3kl~>dCmlG}E^;cG{-R7tD-pz4Z(B7T1S9MLb*rjtpmtqF>#_D>2%d$g z`W1C$0=iT-0k_+&dw@O1oU)j82%w!ljede?e^RX)La$p)3Y}9}2;Xc_FAkye#%yV1 zwnRvkuwR00YQq^+gi}w|Z8QA2h-(8yIhzV$ z$`y@=!ac^gHKmjkMW^D#hDl zGu%DkB74io(hrSdvR0E>nxvi@D5Y7ja>QF_l+D6y4tJ+(CQUZr_b9FYRlFO{&C&Tr zoS#V_>Y`W>JJOpjzlAX-Pjm0WLul2)a{8TuM6fD2Vto1$=+*u$$7TmjT?5oLW_m() ze247R{d7DFnPbwu`Xs00{Xf42OK9*AIQNN-Yvtx-bYJ-4?CH1xkq<}}ZeL{u?~yRW z4V29(opWDQsW}};9dE=dGIYWSo&(Gsc#beV1((v#gMk3cg6flfz4I(C72;fNbT7VV zo_9XhgI72PvWfq?lSzqdjVF!Rzt4M(VqRMpM9A1}ap+BO+K zcu09e>%3kt1HcTkaZq;r?ikqOU_tSSGUtf24>sZg+OsHRMZn{6ixu8VA4xVi@UAzL q1$Czj>vHwe*#foc8hnU;dVwpc6CWU_3op3^u|gDm6w99N$NYb$K=M@p diff --git a/src/urls.py b/src/urls.py index 47030be8..158d8a9a 100644 --- a/src/urls.py +++ b/src/urls.py @@ -1,7 +1,6 @@ from django.contrib import admin from django.urls import path, include -from django.conf.urls import url from django.conf import settings from django.conf.urls.static import static from dashboard import views diff --git a/templates/dashboard/post/all_post.html b/templates/dashboard/post/all_post.html index 278ab3a6..706f5b12 100644 --- a/templates/dashboard/post/all_post.html +++ b/templates/dashboard/post/all_post.html @@ -54,8 +54,19 @@ {{ p.title }} {{ p.catagories }} {{ p.visit_count }} - {{ p.status }} + + {% if p.status == 'published' %} + Published + {% else %} + Draft + {% endif %} + + {% if p.status == 'draft' %} + + {% else %} + + {% endif %} {% if p.visible == False %} {% else %} diff --git a/templates/dashboard/post/create_post.html b/templates/dashboard/post/create_post.html index e4540615..b752e58b 100644 --- a/templates/dashboard/post/create_post.html +++ b/templates/dashboard/post/create_post.html @@ -47,6 +47,13 @@
Add Blog Post
{% endfor %} + +
+ +
diff --git a/templates/dashboard/post/edit_post.html b/templates/dashboard/post/edit_post.html index c6523eb1..8d379306 100644 --- a/templates/dashboard/post/edit_post.html +++ b/templates/dashboard/post/edit_post.html @@ -61,6 +61,13 @@
Edit : {{obj.title}}
{% endfor %} +
+ + +
From 55345d5b4ecaf75e03936e67f7d8ec888d8815e9 Mon Sep 17 00:00:00 2001 From: lovely90133 Date: Thu, 30 Apr 2026 16:26:57 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(=E9=A6=96=E9=A1=B5):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=89=B9=E8=89=B2=E5=8D=9A=E5=AE=A2=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当没有特色博客时,避免模板渲染错误。将查询结果转换为列表并添加空值检查,确保模板安全渲染。 --- blog/views.py | 7 ++++--- templates/home/index.html | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/blog/views.py b/blog/views.py index e97e39ba..787a472e 100644 --- a/blog/views.py +++ b/blog/views.py @@ -39,11 +39,12 @@ def get(self,request,*args,**kwargs): # } ''' featured_obj = Blog.objects.all().filter(status='published', visible=True, featured=True).order_by('catagories','-created_at')[:5] + featured_list = list(featured_obj) post_obj = Blog.objects.all().filter(status='published', visible=True).order_by('catagories','-created_at') # As per Templates Views - first_post = featured_obj.first() - s_post = featured_obj[1] - last_post = featured_obj[2:] + first_post = featured_list[0] if len(featured_list) > 0 else None + s_post = featured_list[1] if len(featured_list) > 1 else None + last_post = featured_list[2:] if len(featured_list) > 2 else [] context={ 'post':post_obj, 'f_post':featured_obj, diff --git a/templates/home/index.html b/templates/home/index.html index abec1a12..cb5a1aa9 100644 --- a/templates/home/index.html +++ b/templates/home/index.html @@ -3,7 +3,7 @@ {% block title %} MultiBlogs {% endblock %} {% block body %} - +{% if first %}
@@ -21,9 +21,10 @@

{{ first.title }}

+{% endif %} - +{% if s_post %}
@@ -77,6 +78,7 @@

+{% endif %}