第6章 首頁功能- ListView/DeleteView完成動態(tài)功能-源碼和MRO算法
- 本章在實(shí)現(xiàn)需求的同時(shí),穿插講解Django的高級知識,如ORM中多對多外鍵。先使用通用類視圖ListView/DeleteView開發(fā)功能,再講解框架的源碼,然后是Python語言中多繼承的MRO算法,按業(yè)務(wù)→框架→源碼→Python底層算法順序由淺入深講解。 ...
6-1 動態(tài)功能models.py設(shè)計(jì)
- pipenv run python manage.py startapp new
- news/models.py
#!/usr/bin/python3
from __future__ import unicode_literals
import uuid
from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible
from django.db import models
# Create your models here.
@python_2_unicode_compatible
class News(models.Model):
uuid_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="publisher", on_delete=models.SET_NULL, verbose_name='用戶')
parent = models.ForeignKey("self", blank=True, null=True, on_delete=models.CASCADE, related_name="thread", verbose_name='自關(guān)聯(lián)')
content = models.TextField(verbose_name='動態(tài)內(nèi)容')
liked = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="liked_news", verbose_name='點(diǎn)贊用戶')
reply = models.BooleanField(default=False, verbose_name='是否為評論')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='創(chuàng)建時(shí)間')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新時(shí)間')
class Meta:
verbose_name = "首頁"
verbose_name_plural = verbose_name
ordering = ("-created_at",)
def __str__(self):
return self.content
def switch_like(self, user):
"""點(diǎn)贊或取消贊"""
if user in self.liked.all(): # 如果用戶已經(jīng)贊過,則取消贊
self.liked.remove(user)
else: # 如果用戶沒有贊過,則添加贊
self.liked.add(user)
# 通知樓主
# notification_handler(user, self.user, 'L', self, id_value=str(self.uuid_id), key='social_update')
def get_parent(self):
"""返回自關(guān)聯(lián)中的上級記錄或本身"""
if self.parent:
return self.parent
else:
return self
def reply_this(self, user, text):
"""
回復(fù)首頁的動態(tài)
:param user: 登錄的用戶
:param text: 回復(fù)的內(nèi)容
:return: None
"""
parent = self.get_parent()
reply_news = News.objects.create(
user=user,
content=text,
reply=True,
parent=parent
)
def get_thread(self):
"""關(guān)聯(lián)到當(dāng)前記錄的所有記錄"""
self.refresh_from_db()
parent = self.get_parent()
return parent.thread.all()
def comment_count(self):
"""評論數(shù)"""
return self.get_thread().count()
def count_likers(self):
"""點(diǎn)贊數(shù)"""
return self.liked.count()
def get_likers(self):
"""所有點(diǎn)贊用戶"""
return self.liked.all()
- news/app.py
from django.apps import AppConfig
class NewsConfig(AppConfig):
name = 'dacall.news'
verbose_name = "打call"
- setting/base.py
LOCAL_APPS = [
"dacall.users.apps.UsersAppConfig",
"news.apps.NewsConfig",
# Your stuff: custom apps go here
]
6-2 完成動態(tài)列表頁開發(fā)
6-3 通用類視圖ListView源碼詳解
6-4 理解Python中的多繼承-MRO
6-5 新式類的MRO算法-C3線性化算法
6-6 用戶發(fā)表動態(tài)
views
@login_required
@ajax_required
@require_http_methods(["POST"])
def post_news(request):
"""發(fā)送動態(tài),AJAX POST請求"""
post = request.POST['post'].strip()
if post:
posted = News.objects.create(user=request.user, content=post)
html = render_to_string('news/news_single.html', {'news': posted, 'request': request})
return HttpResponse(html)
else:
return HttpResponseBadRequest("內(nèi)容不能為空!")
zanhu\news\urls.py
urlpatterns = [
path('', views.NewsListView.as_view(), name='list'),
path('delete/<pk>/', views.NewsDeleteView.as_view(), name='delete_news'),
path('post-news/', views.post_news, name='post_news'),
zhan/helpers.py
#!/usr/bin/python3
# -*- coding:utf-8 -*-
# __author__ = '__Jack__'
from functools import wraps
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseBadRequest
from django.views.generic import View
def ajax_required(f):
"""驗(yàn)證是否為AJAX請求"""
@wraps(f)
def wrap(request, *args, **kwargs):
# request.is_ajax() 方法判斷是否是 ajax 請求
# 參考:https://code.ziqiangxuetang.com/django/django-ajax.html
if not request.is_ajax():
return HttpResponseBadRequest("不是AJAX請求!")
return f(request, *args, **kwargs)
return wrap
6-7 用戶刪除動態(tài)
zanhu/news/views.py
class NewsDeleteView(LoginRequiredMixin, AuthorRequiredMixin, DeleteView):
"""繼承DeleteView重寫delete方法,使用AJAX響應(yīng)請求"""
model = News
template_name = 'news/news_confirm_delete.html'
success_url = reverse_lazy("news:list") # 在項(xiàng)目的URLConf未加載前使用
news/url.py
app_name = "news"
urlpatterns = [
path("", views.NewsListView.as_view(), name="list"),
path('post-news/', views.post_news, name='post_news'),
path('delete/<pk>/', views.NewsDeleteView.as_view(), name='delete_news'),
]
zanhu/helpers.py
from django.core.exceptions import PermissionDenied
from django.views.generic import View
class AuthorRequiredMixin(View):
"""
驗(yàn)證是否為原作者,用于狀態(tài)刪除、文章編輯;
個(gè)人中心模塊中更新信息不要驗(yàn)證是否為原作者,因?yàn)閁serUpdateView返回的是當(dāng)前登錄用戶的form
"""
def dispatch(self, request, *args, **kwargs):
# 狀態(tài)和文章實(shí)例有user屬性
if self.get_object().user.username != self.request.user.username:
raise PermissionDenied
return super(AuthorRequiredMixin, self).dispatch(request, *args, **kwargs)
6-8 通用類視圖DeleteView源碼詳解
- 繼承單個(gè)對象.
6-9 Django通用類視圖源碼詳解
6-10 用戶給動態(tài)點(diǎn)贊
zanhu/news/views.py
@login_required
@ajax_required
@require_http_methods(["POST"])
def like(request):
"""點(diǎn)贊,AJAX POST請求"""
news_id = request.POST['news']
news = News.objects.get(pk=news_id)
# 取消或者添加贊
news.switch_like(request.user)
# 返回贊的數(shù)量
return JsonResponse({"likes": news.count_likers()})
url
from django.urls import path
from dacall.news import views
app_name = "news"
urlpatterns = [
path("", views.NewsListView.as_view(), name="list"),
path('post-news/', views.post_news, name='post_news'),
path('delete/<pk>/', views.NewsDeleteView.as_view(), name='delete_news'),
path('like/', views.like, name='like_post'),
]
6-11 用戶評論動態(tài)
@login_required
@ajax_required
@require_http_methods(["POST"])
def post_comment(request):
"""評論,AJAX POST請求"""
post = request.POST['reply']
parent_id = request.POST['parent']
parent = News.objects.get(pk=parent_id)
post = post.strip()
if post:
parent.reply_this(request.user, post)
return JsonResponse({'comments': parent.comment_count()})
else: # 評論為空返回400.html
return HttpResponseBadRequest("內(nèi)容不能為空!")
@login_required
@ajax_required
@require_http_methods(["POST"])
def update_interactions(request):
"""更新互動信息"""
data_point = request.POST['id_value']
news = News.objects.get(pk=data_point)
data = {'likes': news.count_likers(), 'comments': news.comment_count()}
return JsonResponse(data)
6-12 模型類的測試用例test_models.py
6-13 視圖的測試用例test_views.py
6-14 本章總結(jié)與課后作業(yè)