第6章 首页功能- ListView/DeleteView完成动态功能-源码和MRO算法
- 本章在实现需求的同时,穿插讲解Django的高级知识,如ORM中多对多外键。先使用通用类视图ListView/DeleteView开发功能,再讲解框架的源码,然后是Python语言中多继承的MRO算法,按业务→框架→源码→Python底层算法顺序由浅入深讲解。 ...
6-1 动态功能models.py设计
- 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='自关联')
content = models.TextField(verbose_name='动态内容')
liked = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="liked_news", verbose_name='点赞用户')
reply = models.BooleanField(default=False, verbose_name='是否为评论')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
verbose_name = "首页"
verbose_name_plural = verbose_name
ordering = ("-created_at",)
def __str__(self):
return self.content
def switch_like(self, user):
"""点赞或取消赞"""
if user in self.liked.all(): # 如果用户已经赞过,则取消赞
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):
"""返回自关联中的上级记录或本身"""
if self.parent:
return self.parent
else:
return self
def reply_this(self, user, text):
"""
回复首页的动态
:param user: 登录的用户
:param text: 回复的内容
:return: None
"""
parent = self.get_parent()
reply_news = News.objects.create(
user=user,
content=text,
reply=True,
parent=parent
)
def get_thread(self):
"""关联到当前记录的所有记录"""
self.refresh_from_db()
parent = self.get_parent()
return parent.thread.all()
def comment_count(self):
"""评论数"""
return self.get_thread().count()
def count_likers(self):
"""点赞数"""
return self.liked.count()
def get_likers(self):
"""所有点赞用户"""
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 完成动态列表页开发
6-3 通用类视图ListView源码详解
6-4 理解Python中的多继承-MRO
6-5 新式类的MRO算法-C3线性化算法
6-6 用户发表动态
views
@login_required
@ajax_required
@require_http_methods(["POST"])
def post_news(request):
"""发送动态,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("内容不能为空!")
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):
"""验证是否为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 用户删除动态
zanhu/news/views.py
class NewsDeleteView(LoginRequiredMixin, AuthorRequiredMixin, DeleteView):
"""继承DeleteView重写delete方法,使用AJAX响应请求"""
model = News
template_name = 'news/news_confirm_delete.html'
success_url = reverse_lazy("news:list") # 在项目的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):
"""
验证是否为原作者,用于状态删除、文章编辑;
个人中心模块中更新信息不要验证是否为原作者,因为UserUpdateView返回的是当前登录用户的form
"""
def dispatch(self, request, *args, **kwargs):
# 状态和文章实例有user属性
if self.get_object().user.username != self.request.user.username:
raise PermissionDenied
return super(AuthorRequiredMixin, self).dispatch(request, *args, **kwargs)
6-8 通用类视图DeleteView源码详解
- 继承单个对象.
6-9 Django通用类视图源码详解
6-10 用户给动态点赞
zanhu/news/views.py
@login_required
@ajax_required
@require_http_methods(["POST"])
def like(request):
"""点赞,AJAX POST请求"""
news_id = request.POST['news']
news = News.objects.get(pk=news_id)
# 取消或者添加赞
news.switch_like(request.user)
# 返回赞的数量
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 用户评论动态
@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("内容不能为空!")
@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 本章总结与课后作业