2019-05-19 Django高级实战 开发企业级问答网站 第五章

第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//', 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//', 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//', 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 本章总结与课后作业

你可能感兴趣的:(2019-05-19 Django高级实战 开发企业级问答网站 第五章)