教程来源 杜塞-django-vue系列
博客链接 传送门
评论是博客作者和读者沟通的重要方式,也是墨客作者检验自身文章质量的手段,同时让网站实现交互。
虽然有很多方式可以将评论功能托管给第三方(本人推荐这么做),不过本着学习的目的吗,接下来就实现自己简单的文章评论接口。
准备工作
评论功能可以集成在article app
内,但是为了下一张的二级评论和评论的评论,我们把评论独立出来:
# 创建评论模块并注册到配置文件:
(venv) E:\drf\drf_vue_blog>python manage.py startapp comment
#drf_vue_blog/settings.py
INSTALLED_APPS = [
···
'comment'
]
创建评论模型:
# comment/models.py
from django.db import models
from django.utils import timezone
from article.models import Article
from django.contrib.auth.models import User
class Comment(models.Model):
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='comment'
)
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_name='comment'
)
comment = models.TextField()
created = models.DateTimeField(default=timezone.now)
class Meta:
ordering=['-created']
def __str__(self):
return self.comment[:20]
模型包含一对一的作者外键,一对多的文章外键,评论内容,创建时间等四个字段。
数据迁移!!!
试图和序列化
视图集和之前章节没有太大区别:
from rest_framework import viewsets
from comment.models import Comment
from comment.serializers import CommentSerialize
from comment.permissions import IsOwnerOrReadOnly
class CommentViewSet(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerialize
permission_classes = [IsOwnerOrReadOnly]
def perform_create(self, serializer):
serializer.save(author = self.request.user)
接下来是写评论的权限。
评论对于用户的要求比文章松弛,非安全请求只需要是本人操作就好了。
因此定义一个所有人看,进本人可以更改的权限:
# comment/permissions.py
from rest_framework.permissions import BasePermission,SAFE_METHODS
class IsOwnerOrReadOnly(BasePermission):
message = "You must be the owner to update."
def has_permission(self, request, view):
if request.method == SAFE_METHODS:
return True
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.author == request.user
运行非安全请求时,由于需要验证当前评论的作者和当前登录用户是否为同一用户,这里会自动调用def has_object_permission(...)
方法,方法参数中的obj
及评论模型的实例。
看起来只需要实现def has_object_permission(...)
就可以了,但还有个小问题就是,此方法是晚于
def perform_created
执行的。如果用户未登录新建评论,由于用户不存在,接口会抛出500错误。
本着出错就抛出错误提醒的原则,增加def has_permission(···)
方法。此方法早于def perform_created
执行,因此都能够对用户
登陆状态进行预检测。
功能这样就实现了,但是代码会重复,接下来就优化代码:
把权限类还改为以下:
# comment/permissions.py
from rest_framework.permissions import BasePermission, SAFE_METHODS
class IsOwnerOrReadOnly(BasePermission):
message = "You must be the owner to update."
def safe_methods_or_owner(self, request, func):
if request.method in SAFE_METHODS:
return True
return func()
def has_permission(self, request, view):
return self.safe_methods_or_owner(
request,
lambda: request.user.is_authenticated
)
def has_object_permission(self, request, view, obj):
return self.safe_methods_or_owner(
request,
lambda: obj.author == request.user
)
用匿名函数将有函数体(闭包)作为参数,传递到 def safe_methods_or_owner(…) 方法里执行,效果和之前是完全一样的。
接下来就是注册路由和序列化器了。
路由:
# drf_vue_blog/urls.py
···
from comment.views import CommentViewSet
router.register(r'comment', CommentViewSet)
序列化器
# comment/serializers.py
from rest_framework import serializers
from comment.models import Comment
from user_info.serializers import UserDescSerializer
class CommentSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail')
author = UserDescSerializer(read_only=True)
class Meta:
model = Comment
fields = "__all__"
extra_kwargs={'created':{'read_only':True}}
还是跟之前一样,url
超链接字段让接口的跳转方便,author
嵌套序列化器让其显示内容更丰富。
最后让评论通过文章接口显示出来:
# article/serializers.py
from comment.serializers import CommentSerializer
# 继承ArticleBaseSerializer
class ArticleDetailSerializer(ArticleBaseSerializer):
id = serializers.IntegerField(read_only=True)
comments = CommentSerializer(many=True,read_only=True)
这就完成了,代码量很少就完成了新功能。
测试
发送几个请求测试逻辑接口是否正确,
未登录状态新建评论:
>http POST http://127.0.0.1:8000/api/comment/ article=8 content="new comment"
HTTP/1.1 403 Forbidden
···
{
"detail": "Authentication credentials were not provided."
}
用之前的Obama
用户创建文章评论:、
>http -a Obama:admin123456 POST http://127.0.0.1:8000/api/comment/ article=8 content="new comment"
HTTP/1.1 201 Created
···
{
"article": 8,
"author": {
"date_joined": "2021-06-13T06:47:00",
"id": 2,
"last_login": null,
"username": "Obama"
},
"content": "new comment",
"created": "2021-06-20T14:05:51.275911",
"id": 1,
"url": "http://127.0.0.1:8000/api/comment/1/"
}
用非本人用户xianwei
用户更新评论:
>http -a xianwei:admin123456 PATCH http://127.0.0.1:8000/api/comment/1/ content="API"
HTTP/1.1 403 Forbidden
···
{
"detail": "You must be the owner to update."
}
用Obama
删除评论:
>http -a Obama:admin123456 DELETE http://127.0.0.1:8000/api/comment/1/
HTTP/1.1 204 No Content
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Length: 0
Date: Sun, 20 Jun 2021 06:09:08 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.4
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
非本人无法对资源进行更改,很好的符合了预期逻辑。