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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容