2019-05-19 Django高級實(shí)戰(zhàn) 開發(fā)企業(yè)級問答網(wǎng)站 第五章

第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è)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容