实操学习——文章和评论的设计

news2024/11/25 11:24:37

实操学习——文章和评论的设计

  • 1.文章表的设计
  • 2.文章表接口演示
    • 基础权限
    • 创建文章
    • 修改文章
    • 删除文章
    • 浏览所有文章
  • 3.评论表的设计
  • 4.评论表接口演示
    • 1. 查询指定文章下的所有评论

1.文章表的设计

  1. 创建一个community的app
    在这里插入图片描述
  2. 在settings中 完成注册
    在这里插入图片描述
  3. 定义模型
    创建文章表
from django.db import models
from work.models import Label
from django.contrib.auth.models import User
# Create your models here.
from utils.modelsMixin import ModelSetMixin

class Article(ModelSetMixin):
    STATUS_CHOICES = (
        (0,'未发布'),
        (1,'发布'),
    )
    title = models.CharField(max_length=100,verbose_name='标题')
    digest = models.CharField(max_length=300,verbose_name='摘要')
    content = models.TextField(verbose_name='文章内容')
    page_view = models.IntegerField(verbose_name='浏览量',default=0)
    priority = models.IntegerField(verbose_name='优先级',default=0)
    status = models.IntegerField(verbose_name='状态',default=0,choices=STATUS_CHOICES)

    label = models.ForeignKey(Label,on_delete=models.CASCADE)
    user = models.ForeignKey(User,on_delete=models.CASCADE)

    class Meta:
        # 按照priority、page_view、create_time进行倒叙排序
        ordering = ["-priority","-page_view","-create_time"]
        db_table = 'article' #定义生成的mysql表名
        verbose_name = '文章'
        verbose_name_plural = verbose_name

  1. 创建序列化器
from rest_framework.serializers import ModelSerializer


#有对应的模型,可以继承ModelSerializer
class ArticleSerializer(ModelSerializer): 

    class Meta:
        model = Article
        exclude = ['is_delete']

  1. 创建视图
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
from .serializers import *
# Create your views here.
class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.filter(is_delete=False)
    serializer_class = ArticleSerializer
  1. 创建路由
from rest_framework.routers import DefaultRouter

from .views import *

urlpatterns = []
router = DefaultRouter()
router.register('article', ArticleViewSet)
urlpatterns += router.urls

  1. 创建主路由
    在这里插入图片描述

2.文章表接口演示

基础操作分析:
创建文章:登录。自动获取当前登录的用户,作为文章的作者
修改文章:创建者
删除文章:创建者
浏览所有文章:登录
查看指定文章:登录

基础权限

由上面分析得知,最基础的权限是登录,所以我们在视图中增加基础权限的配置
在这里插入图片描述

创建文章

分析:获取当前登录用户为文章的创建者

  1. 重写create方法
    #@auto_user自定义装饰器将当前登录的用户作为创建者,在前端就不需要传入user,后端会自动拿到当前登录的user作为文章的创建者
    @auto_user 
    def create(self,request,*args,**kwargs):
        return ModelViewSet.create(self,request,*args,**kwargs)
  1. 序列化器的优化,对返回的数据进行优化
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import *

#有对应的模型,可以继承ModelSerializer
class ArticleSerializer(ModelSerializer): 
    user_name = serializer.CharField(source='user.username',read_only=True)
    user_avatar = serializer.CharField(source='user.userdetail.avatar',read_only=True)
    label_name = serializer.CharField(source='label.name',read_only=True)
    class Meta:
        model = Article
        exclude = ['is_delete']

  1. 补充:permission.py中的权限代码如下,后续修改文章、查询文章等涉及到权限的操作都会使用到本文件

from functools import update_wrapper

from django.contrib.auth.models import Group
from rest_framework.permissions import BasePermission


class TeacherPermission(BasePermission):
    def has_permission(self, request, view):
        user = request.user  # 当前请求的用户信息
        # 判断身份,查询用户在不在老师这个分组里面
        group = Group.objects.filter(name='老师').first()
        user_groups = user.groups.all()
        return user.is_superuser or group in user_groups


class ActiveUserPermission(BasePermission):
    def has_permission(self, request, view):
        # 操作的用户必须是当前登陆的用户
        user = request.user
        return user.id == int(view.kwargs['pk'])

class ActiveUserPPermission(BasePermission):
    def has_permission(self, request, view):
        # 操作的用户必须是当前登陆的用户
        user = request.user
        return user.id == int(request.data.get('user'))


class RootPermission(BasePermission):
    def has_permission(self, request, view):
        user = request.user
        return user.is_superuser


# 更改权限装饰器
def wrap_permisssion(*permissions, validate_permisssion=True):
    def decorator(func):
        def wrapper(self, request, *args, **kwargs):
            self.permission_classes = permissions
            if validate_permisssion:
                self.check_permissions(request)
            return func(self, request, *args, **kwargs)

        return update_wrapper(wrapper, func)

    return decorator


def auto_user(func):
    def wrapper(self, request, *args, **kwargs):
        request.POST._mutable = True  # 让请求参数可以被修改
        request.data['user'] = request.user.id

        return func(self, request, *args, **kwargs)

    return wrapper

修改文章

分析:文章的发布者才能修改文章。这里需要在定义一个装饰器判断该用户是否是该文章的发布者。
使用装饰器的场景:在原有功能的基础上,再增加额外的功能。

  1. 在permission.py中增加装饰器update_auto_user
def update_auto_user(func):
    def wrapper(self, request, *args, **kwargs):
        # 判断当前操作的用户是不是这个数据的创建者
        # 设:文章的id为1,作者的id为3,当前登录的用户id为5
        # 所有的没有被逻辑删除的文章数据集里面的id等于1,并且user用户为5,查不到作者id为3的数据
        # self.get_queryset()可以应用于所有的视图集
        res = self.get_queryset().filter(id=kwargs['pk'],user=request.user)
        if not res:
            return Response({'detail':'您没有修改的权限'})
        # 修改不可以更改作者,只能是当前的登录用户
        request.POST._mutable = True  # 让请求参数可以被修改
        request.data['user'] = request.user.id
        return func(self, request, *args, **kwargs)

    return wrapper
  1. 重写update方法
    @update_auto_user
    def update(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)

删除文章

分析:只有作者才能删除文章

  1. 在permission.py中增加装饰器destory_auto_user
def destory_auto_user(func):
    def wrapper(self, request, *args, **kwargs):
        res = self.get_queryset().filter(id=kwargs['pk'],user=request.user)
        if not res:
            return Response({'detail':'您没有删除的权限'})
        return func(self, request, *args, **kwargs)

    return wrapper
  1. 重写destory方法
    @destory_auto_user
    def destory(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)

浏览所有文章

分析:针对不同的操作,查询集不同,比如,用户查看文章不能查看到草稿的数据,作者查看文章可以查看到自己的已发布的和未发布的数据

  1. 配置分页器
from rest_framework.pagination import PageNumberPagination
class ArticlePaginationPageNumber(PageNumberPagination):
    page_size = 10  # 默认每页多少条,如果不传size,默认3条
    #page_size_param  = 'page' #定义传入页数的参数,默认为page
    page_size_query_param = 'size'  # 规定哪个参数为分页大小参数,参数可以自己定义,这里定义为size,则前端传入参数size = 10,每页展示10条,但是不会超过设置的最大每页条数100
    max_page_size = 100  # 最大每页多少条


class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.filter(is_delete=False)
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticated]
    #在视图中增加分页器类
    pagination_class = ArticlePaginationPageNumber
  1. 根据不同的操作设置不同的查询集。重写get_queryset方法
class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.filter(is_delete=False)
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticated]
    pagination_class = ArticlePaginationPageNumber

    # 重写get_queryset方法的场景:根据不同的操作场景设置不同的查询集
    def get_queryset(self):
        if self.action in ['list','retrieve']: #如果是查询,则只能返回已发布的文章数据
            return Article.objects.filter(is_delete=False,status=1)
        return self.queryset

    #@auto_user自定义装饰器将当前登录的用户作为创建者,在前端就不需要传入user,后端会自动拿到当前登录的user作为文章的创建者
    @auto_user 
    def create(self,request,*args,**kwargs):
        return ModelViewSet.create(self,request,*args,**kwargs)

    @update_auto_user
    def update(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)

    @destory_auto_user
    def destory(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)
  1. 查看作者自己的所有文章数据:包括已发布的和未发布的
    @action(methods=['get'],detail=False)
    def my(self,request):
        """
        得到用户自己的数据,包括未发布数据
        """
        # 用户为当前登录的用户
        data = self.get_queryset().filter(user=request.user) 
        serializer = self.get_serializer(data,many=True)
        return Response(serializer.data)
  1. 浏览量增加接口
    分析:点击进入文章,浏览量+1。
    重写retrieve方法
    def retrieve(self, request, *args, **kwargs):
        """
        重写retrieve方法,点击文章,增加浏览量
        """
        # F查询可以拿到这个字段的值
        self.get_queryset().filter(id=kwargs['pk']).update(page_view=F('page_view')+1)
        return ModelViewSet.update(self, request, *args, **kwargs)

补充:如果不想被刷浏览量,前端写延时器,10s之后调用接口。增加额外的增加浏览量的接口,retrieve可以记录客户端传入的id,然后判断时间。

3.评论表的设计

  1. 在community的models文件下创建评论模型
# 评论表
class Comment(ModelSetMixin):
    content = models.TextField(verbose_name='评论的内容')
    level = models.IntegerField(verbose_name='评论等级',default=1)
    # 因为在文章下的评论没有父级评论,所以null=True允许为空
    # 父级评论的id
    parent_comment = models.IntegerField(verbose_name='父级评论',null=True)
    # 回复评论的id,因为有可能是回复父级评论下面的子评论,所以要增加这个字段
    reply_comment = models.IntegerField(verbose_name='回复评论',null=True)

    # 一篇文章可以有多条评论
    article = models.ForeignKey(Article,on_delete=models.CASCADE)
    # 一个用户可以评论多条评论
    user = models.ForeignKey(User,on_delete=models.CASCADE)

    class Meta:
        # 按照create_time进行倒叙排序
        ordering = ["-create_time"]
        db_table = 'comment' #定义生成的mysql表名
        verbose_name = '评论'
        verbose_name_plural = verbose_name
  1. 定义序列化器
class CommentSerializer(ModelSerializer): 
    class Meta:
        model = Comment
        exclude = ['is_delete']
  1. 编写视图
    分析:评论不需要更新和查询所有,所以继承GenericViewSet
class CommentViewSet(GenericViewSet,CreateModelMixin,DestroyModelMixin):
    queryset = Comment.objects.filter(is_delete=False)
    serializer_class = CommentSerializer
    permission_classes = [IsAuthenticated]

    @auto_user 
    def create(self,request,*args,**kwargs):
        return ModelViewSet.create(self,request,*args,**kwargs)

    @destory_auto_user
    def destory(self, request, *args, **kwargs):
        return ModelViewSet.update(self, request, *args, **kwargs)
  1. 增加路由
from rest_framework.routers import DefaultRouter

from .views import *

urlpatterns = []
router = DefaultRouter()
router.register('article', ArticleViewSet)
router.register('comment', CommentViewSet)
urlpatterns += router.urls

  1. 在文章表中增加__str__方法
    在这里插入图片描述
    这样在评论表中的article外键就会展示出对应的内容
    在这里插入图片描述

4.评论表接口演示

1. 查询指定文章下的所有评论

分析:
/article/1/comment
给文章视图集编写一个额外的功能,返回该文章的所有评论
在ArticleViewSet中增加接口如下:

    @action(methods=['get'],detail=True)
    def comment(self,request,pk):
        # article_id=pk联表查询
        # 查看所有的评论中文章id为传入的pk,等级为1的评论
        comments = Comment.objects.filter(article_id=pk,level=1)
        serializer = CommentSerializer(comments,many=True)
        return Response(serializer.data)

优化返回结果,修改序列化器:

# 定义子评论的序列化器
class SonCommentSerializer(ModelSerializer):
    user_name = serializer.CharField(source='user.username',read_only=True)
    user_avatar = serializer.CharField(source='user.userdetail.avatar',read_only=True)
    reply_username = SerializerMethodField()

        model = Comment
        exclude = ['is_delete']

    def get_reply_username(self,comment):
        # 查询出被回复评论的用户的用户名
        return Comment.objects.get(id=comment.reply_comment).user.username

class CommentSerializer(ModelSerializer): 
    user_name = serializer.CharField(source='user.username',read_only=True)
    user_avatar = serializer.CharField(source='user.userdetail.avatar',read_only=True)
    # 返回当前评论的子评论
    sonComment = SerializerMethodField()

    class Meta:
        model = Comment
        exclude = ['is_delete']

    # get_+自定义的roleDetail名字  comment保存当前要操作的模型对象
    def get_sonComment(self, comment):
        serializer = SonCommentSerializer(Comment.objects.filter(parent_comment=comment.id),many=True)
        data = serializer.data
        # 将获取的子评论进行反转,第一个评论在第一位,而不是时间最新的在最前面
        # reverse对列表做反转
        data.reverse()
        return data


返回结果如下:
在这里插入图片描述
前端效果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2163967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024 Redis 全部

1. 单机部署 1.1 检查环境,创建目录。 # 本地运行,不需要考虑安装的原因,可以卸载防火墙 # 关闭防火墙 systemctl stop firewalld.service# 查看防火强状态 firewall-cmd --state# redis 是基于gcc 环境的,查看是否有 gcc 环境 …

线程基础、状态及常用方法

多线程基础 进程和线程 什么是程序? 程序是包含有计算机指令和数据的文件。 什么是进程? 进程是程序的一次执行过程,是计算机分配资源的最小单位。 什么是线程? 一个进程中会包含若干个线程,每个线程都是一个独立的…

OpenCV图像文件读写(2) 检查 OpenCV 是否支持某种图像格式的写入功能函数haveImageWriter()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 haveImageWriter 函数用于检查 OpenCV 是否支持某种图像格式的写入功能。这个函数可以帮助开发者在编写代码时确定是否可以成功地将图像写入特定…

【Linux】Linux基本命令

目录 文件和目录操作: ls cd pwd cp mv rm mkdir rmdir touch clear history which/whereis 文件查看和编辑: cat less head tail vi 或 vim sz/rz echo 系统信息和管理: su uname hostname df free top ps ki…

fopen与fwrite在C语言中写文件及open、write的对比

fopen与fwrite在C语言中写文件及open、write的对比 一、fopen与fwrite写文件1. fopen函数介绍2. fwrite函数介绍3. 示例代码4. 注意事项二、open与write写文件1. open函数介绍2. write函数介绍3. 示例代码4. 注意事项三、fopen/fwrite与open/write的对比1. 跨平台性2. 功能与灵…

android和ios双端应用性能的测试工具

1.工具介绍 基于日常工作的需要,开发了一款新的android和ios端应用性能测试工具,本工具在数据测试方面与所流行的工具没有区别。欢迎下载使用体验。 本工具为筋斗云,工具说明 本工具无侵入,不需要root,低延迟…

统一建模语言(UML)在软件研发过程中常用图接受:类图、用例图、时序图、状态图、活动图、流程图、顺序图

UML具有许多不同类型的图表,包括: 静态图:用例图、类图、对象图、组件图、部署图动态图:活动图、状态图、时序图(又叫顺序图、序列图)、协作图 软件工程(软件工程中的各种图一般用于以下三个阶段…

线上报名小程序怎么做

在这个数字化、智能化的时代,信息技术的发展正以前所未有的速度改变着我们的生活。无论是学习、工作还是娱乐,互联网都成为了我们不可或缺的一部分。而在线上报名这一领域,小程序的出现更是为广大用户带来了前所未有的便捷与高效。今天&#…

# 高可用的并发解决方案nginx+keepalived(四)

高可用的并发解决方案nginxkeepalived(四) 一、Keepalived安装 1、keepalived 介绍 Keepalived 是一种高性能的服务器高可用或热备解决方案,Keepalived 可以用来防止服务器单点故障的发生,通过配合 Nginx 可以实现 web 前端服务…

SOMEIP_ETS_134: SD_Option_Length_ends_past_Options_Array_Var_A

测试目的: 验证DUT能够处理一个其选项长度超出所指示的选项数组长度的SubscribeEventgroup消息,并以SubscribeEventgroupNAck作为响应或完全忽略该请求。 描述 在变体A中,通过将SOME/IP头部中指示的总长度从60字节略微减少到48字节&#x…

Tomcat后台弱口令部署war包

1.环境搭建 cd /vulhub/tomcat/tomcat8 docker-compose up -d 一键启动容器 2.访问靶场 点击Manager App tomcat8的默认用户名和密码都是tomcat进行登录 3.制作war包 先写一个js的一句话木马 然后压缩成zip压缩包 最后修改后缀名为war 4.在网站后台上传war文件 上传war文件…

MySQL基础篇 - SQL

01 SQL通用语法 02 SQL分类 03 DDL语句 04 DML语句 05 DQL语句(单表查询) 05_01 学习总览 05_02 基本查询 05_03 条件查询 【应用实例】: 05_04 聚合函数 05_05 分组查询 05_06 排序查询 05_07 分页查询 【boss题目】: 05_08 执行顺序 06 DCL语句 【概…

Google发布改进型Gemini 1.5 系列AI模型 并将API价格降低50%

Google今天发布了两款可投入生产环境的更新版Gemini 1.5 型号:Gemini-1.5-Pro-002 和 Gemini-1.5-Flash-002。与五月份发布的原版 Gemini 1.5 型号相比,这两款新产品略有改进。 更新后的 Gemini 1.5 系列模型在 MMLU-Pro 基准成绩提高了约 7%&#xff0c…

UNI-SOP认证系统的定位

目前市面上的认证开发框架比较多,比如SA-TOKEN、SHIRO以及SPRING集成的Oauth2等都是比较优秀的认证授权框架,还有像若依这样比较完整的带有后台一些常用功能的开发框架,诸如这些认证授权开发框架都集成了用户的登录授权功能,他们可…

C语言长度受限制的字符串函数:(strncpy,strncat,strncmp)

strncpy 重点&#xff1a;1.拷贝num个字符从源字符串到目标空间 2.如果源字符串的长度小于num&#xff0c;则拷贝完源字符串之后&#xff0c;在目标的后边追加0&#xff0c;直到num个 3.这个函数不会拷贝\0。 列子&#xff1a; #include<stdio.h> #include<string…

vcruntime140_1.dll无法继续执行代码怎么办,总结6种解决方法

在计算机编程和软件开发中&#xff0c;我们经常会遇到各种错误和问题。其中&#xff0c;vcruntime140_1.dll无法继续执行代码是一个常见的问题。这个问题可能会导致程序崩溃&#xff0c;影响我们的工作进度。因此&#xff0c;了解这个问题的原因以及如何解决它是非常重要的。 …

点云与Open3D入门

文章目录 点云数据介绍点云 Open3D点云基础操作RGBD转换为PCDPLY转PCD 点云空间搜索K-DTree原理搜索算法 OCTree 点云滤波体素下采样点云体素化复杂点云体素化统计滤波半径滤波 点云特征提取法线估计 点云分割DBSCAN 聚类分割&#xff08;运行时间较长&#xff09;PANSNC平面分…

Tableau|二 如何利用功能区创建视图

一 认识 Tableau 数据 1.数据角色 维度和度量是Tableau的一种数据角色划分&#xff0c;离散和连续是另一种划分方式。 1.维度和度量 维度往往是一些分类、时间方面的定性字段&#xff0c;将其拖放到功能区时&#xff0c;Tableau不会对其进行计算&#xff0c;而是对视图区进行分…

什么开放式耳机好用?2024五款地表最强机型推荐!

随着耳机使用的普及&#xff0c;选择一款合适的耳机变得尤为重要。入耳式耳机虽然普遍&#xff0c;但长时间佩戴可能会带来不适&#xff0c;甚至可能影响耳道健康。开放式耳机提供了一个折中的选择&#xff0c;它们不堵塞耳道&#xff0c;允许用户在享受音乐的同时&#xff0c;…

Android线程使用总结

Android线程使用总结 1. Threading Performance 在程序开发的实践当中&#xff0c;为了让程序表现得更加流畅&#xff0c;我们肯定会需要使用到多线程来提升程序的并发执行性能。但是编写多线程并发的代码一直以来都是一个相对棘手的问题&#xff0c;所以想要获得更佳的程序性…