用户管理,JWT验证,评论回复

master
barney 2 years ago
parent a2d714d85f
commit 8e1a25a9c0
  1. 6
      article/models.py
  2. 9
      article/urls.py
  3. 19
      comment/migrations/0002_comment_parent.py
  4. 7
      comment/models.py
  5. 1
      comment/permissions.py
  6. 28
      comment/serializers.py
  7. BIN
      db.sqlite3
  8. 12
      drf_vue_blog/settings.py
  9. 11
      drf_vue_blog/urls.py
  10. 10
      user_info/permissions.py
  11. 41
      user_info/serializers.py
  12. 45
      user_info/views.py

@ -41,9 +41,13 @@ class Article(models.Model):
# 作者
author = models.ForeignKey(
User,
User, # 父表
null=True,
on_delete=models.CASCADE,
# 一个作者可以对应多篇文章,作者是文章的一个外键
# 访问一个作者的所有文章可以用user.article_set.all()
# 也可以用到下面的related_name, user.articles.all(),和上面的语句作用相同
# 两者不能同时使用
related_name='articles'
)

@ -1,9 +0,0 @@
from django.urls import path
from article import views
app_name = 'article'
urlpatterns = [
path('', views.ArticleList.as_view(), name='list'),
path('<int:pk>/', views.ArticleDetail.as_view(), name='detail')
]

@ -0,0 +1,19 @@
# Generated by Django 4.1.1 on 2022-09-24 18:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('comment', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='comment',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='comment.comment'),
),
]

@ -5,6 +5,13 @@ from django.contrib.auth.models import User
class Comment(models.Model):
parent = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='children',
)
# 评论人
author = models.ForeignKey(
User,

@ -24,3 +24,4 @@ class IsOwnerOrReadOnly(BasePermission):
lambda: obj.author == request.user # 验证当前评论的作者和当前登录的用户是否为同一个人
)

@ -3,13 +3,41 @@ from user_info.serializers import UserDescSerializer
from comment.models import Comment
class CommentChildrenSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='comment-detail')
author = UserDescSerializer(read_only=True)
class Meta:
model = Comment
exclude = [ # 不需要的字段
'parent',
'article',
]
class CommentSerializer(serializers.ModelSerializer):
"""评论的序列化器"""
# HyperlinkedIdentityField用于自身
url = serializers.HyperlinkedIdentityField(view_name='comment-detail')
author = UserDescSerializer(read_only=True)
# 设置为超链接,HyperlinkedRelatedField用于对外键关系
article = serializers.HyperlinkedRelatedField(view_name='article-detail', read_only=True)
# 需要知道是哪篇文章的评论
article_id = serializers.IntegerField(write_only=True, allow_null=False, required=True)
# parent为父评论
parent = CommentChildrenSerializer(read_only=True)
# 本身是父评论则不需要该字段
parent_id = serializers.IntegerField(write_only=True, allow_null=True, required=False)
class Meta:
model = Comment
fields = '__all__'
extra_kwargs = {'created': {'read_only': True}}
# 父评论只能在创建时被关联,后续不能更改
def update(self, instance, validated_data):
validated_data.pop('parent_id', None) # 更新时忽略parent_id参数
validated_data.pop('article_id', None) # 更新时忽略article_id参数
return super(CommentSerializer, self).update(instance, validated_data)

Binary file not shown.

@ -1,5 +1,7 @@
from pathlib import Path
import os
from datetime import timedelta
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@ -120,6 +122,9 @@ REST_FRAMEWORK = {
'PAGE_SIZE': 5,
# 使用django-filter后端过滤引擎
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
# 'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_simplejwt.authentication.JWTAuthentication',
# ),
}
# 媒体文件
@ -127,3 +132,10 @@ MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
#
# SIMPLE_JWT = {
# 'ACCESS_TOKEN_LIFETIME': timedelta(days=1), # 令牌有效时间为1天
# 'REFRESH_TOKEN_LIFETIME': timedelta(days=10), # 令牌每10天刷新一次
# }

@ -5,6 +5,11 @@ from article import views
from django.conf import settings
from django.conf.urls.static import static
from comment.views import CommentViewSet
from user_info.views import UserViewSet
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
router = DefaultRouter()
router.register(r'article', views.ArticleViewSet)
@ -12,12 +17,14 @@ router.register(r'category', views.CategoryViewSet)
router.register(r'tag', views.TagViewSet)
router.register(r'avatar', views.AvatarViewSet)
router.register(r'comment', CommentViewSet)
router.register(r'user', UserViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('api/', include(router.urls))
# path('api/article/', include('article.urls', namespace='article')),
path('api/', include(router.urls)),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # 获取token
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # 刷新token
]
# 把媒体文件的路由注册了

@ -0,0 +1,10 @@
from rest_framework.permissions import BasePermission, SAFE_METHODS
class IsSelfOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
"""确保非安全方法只能由本人操作"""
return obj == request.user

@ -13,3 +13,44 @@ class UserDescSerializer(serializers.ModelSerializer):
# 'last_login',
# 'date_joined'
]
class UserRegisterSerializer(serializers.ModelSerializer):
# lookup_field字段将作为url中的地址
url = serializers.HyperlinkedIdentityField(view_name='user-detail', lookup_field='username')
class Meta:
model = User
fields = [
'url',
'id',
'username',
'password',
]
extra_kwargs = {
'password': {'write_only': True}
}
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
def update(self, instance, validated_data):
if 'password' in validated_data:
password = validated_data.pop('password') # 对密码加密后再存入数据库
instance.set_password(password)
return super().update(instance, validated_data)
class UserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'username',
'last_name',
'first_name',
'email',
'last_login',
'date_joined',
]

@ -1,3 +1,44 @@
from django.shortcuts import render
from django.contrib.auth.models import User
from rest_framework import viewsets
from rest_framework.permissions import AllowAny, IsAuthenticatedOrReadOnly
# Create your views here.
from user_info.serializers import UserRegisterSerializer
from user_info.permissions import IsSelfOrReadOnly
from rest_framework.decorators import action
from rest_framework.response import Response
from user_info.serializers import UserDetailSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserRegisterSerializer
lookup_field = 'username' # 和UserRegisterSerializer中的url中lookup_field对应
# http://127.0.0.1:8000/api/user/admin/info/
@action(detail=True, methods=['get'])
def info(self, request, username=None):
queryset = User.objects.get(username=username)
serializer = UserDetailSerializer(queryset, many=False)
return Response(serializer.data)
# http://127.0.0.1:8000/api/user/sorted/
@action(detail=False)
def sorted(self, request):
users = User.objects.all().order_by('-username')
# 是否分页
page = self.paginate_queryset(users)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(users, many=True)
return Response(serializer.data)
def get_permissions(self):
# 注册用户的POST请求是允许所有人都可以操作的
if self.request.method == 'POST':
self.permission_classes = [AllowAny]
else:
self.permission_classes = [IsAuthenticatedOrReadOnly, IsSelfOrReadOnly]
return super(UserViewSet, self).get_permissions()

Loading…
Cancel
Save