Django-Vue搭建个人博客:用户管理
7564 views, 2021/03/18 updated Go to Comments
上一章搞定了 JWT 登录,本章接着来实现用户信息的增删改查。
用户管理
用户管理涉及到对密码的操作,因此新写一个序列化器,覆写 def create(...)
和 def update(...)
方法:
# user_info/serializers.py ... class UserRegisterSerializer(serializers.ModelSerializer): 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)
- 注意
def update(...)
时,密码需要单独拿出来通过set_password()
方法加密后存入数据库,而不能以明文的形式保存。 - 超链接字段的参数有一条
lookup_field
,这是指定了解析超链接关系的字段。直观来说,将其配置为username
后,用户详情接口的地址表示为用户名而不是主键。
用户管理同样涉及的权限问题,因此新建 permissions.py
,写入代码:
# user_info/permissions.py 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
这个权限类和之前写过的类似,确保非安全方法只能由本人操作。
前面写过一个类似的权限类,请读者自己试着合并代码吧。
铺垫工作做好了,最后就是写视图集:
# user_info/views.py from django.contrib.auth.models import User from rest_framework import viewsets from rest_framework.permissions import AllowAny, IsAuthenticatedOrReadOnly from user_info.serializers import UserRegisterSerializer from user_info.permissions import IsSelfOrReadOnly class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserRegisterSerializer lookup_field = 'username' def get_permissions(self): if self.request.method == 'POST': self.permission_classes = [AllowAny] else: self.permission_classes = [IsAuthenticatedOrReadOnly, IsSelfOrReadOnly] return super().get_permissions()
- 注册用户的 POST 请求是允许所有人都可以操作的,但其他类型的请求(比如修改、删除)就必须是本人才行了,因此可以覆写
def get_permissions(...)
定义不同情况下所允许的权限。permission_classes
接受列表,因此可以同时定义多个权限,权限之间是 and 关系。 - 注意这里的
lookup_field
属性,和序列化器中对应起来。
接着注册路由:
# drf_vue_blog/urls.py ... from user_info.views import UserViewSet router.register(r'user', UserViewSet) ...
用户的增删改查就完成了。可见 DRF 封装层级很高,常规功能完全隐藏在框架之中了。
试着发送一个 get 请求:
PS C:\Users\Dusai> http get http://127.0.0.1:8000/api/user/ HTTP/1.1 200 OK ... { "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "url": "http://127.0.0.1:8000/api/user/dusai/", "username": "dusai" }, { "id": 2, "url": "http://127.0.0.1:8000/api/user/Obama/", "username": "Obama" }, { "id": 4, "url": "http://127.0.0.1:8000/api/user/diudiu/", "username": "diudiu" } ] }
可以看到详情地址不是主键值而是用户名了,这就是 lookup_field
发挥的作用。
其他测试工作就由读者自己完成了。
自定义动作
视图集除了默认的增删改查外,还可以有其他的自定义动作。
为了测试,首先写一个信息更加丰富的用户序列化器:
# user_info/serializers.py ... class UserDetailSerializer(serializers.ModelSerializer): class Meta: model = User fields = [ 'id', 'username', 'last_name', 'first_name', 'email', 'last_login', 'date_joined' ]
接着就可以在视图集中新增代码,自定义动作了:
# user_info/views.py ... from rest_framework.decorators import action from rest_framework.response import Response from user_info.serializers import UserDetailSerializer class UserViewSet(viewsets.ModelViewSet): ... @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) @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)
魔法都在装饰器 @action
里,它的参数可以定义是否为详情的动作、请求类型、url 地址、url 解析名等常规需求。
发两个请求试试:
> http get http://127.0.0.1:8000/api/user/dusai/info/ HTTP/1.1 200 OK ... { "date_joined": "2020-06-15T09:23:27.417440Z", "email": "dusaiphoto@foxmail.com", "first_name": "du", "id": 1, "last_login": "2020-12-29T07:20:34.588221Z", "last_name": "sai", "username": "dusai" } > http get http://127.0.0.1:8000/api/user/sorted/ HTTP/1.1 200 OK ... { "count": 3, "next": null, "previous": null, "results": [ { "id": 1, "url": "http://127.0.0.1:8000/api/user/dusai/", "username": "dusai" }, { "id": 4, "url": "http://127.0.0.1:8000/api/user/diudiu/", "username": "diudiu" }, { "id": 2, "url": "http://127.0.0.1:8000/api/user/Obama/", "username": "Obama" } ] }
默认情况下,方法名就是此动作的路由路径。返回的 Json 也正确显示为方法中所封装的数据。
关于自定义动作详见官方文档。