泽恩小站-教程区 Help

用户管理

上一章搞定了 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 也正确显示为方法中所封装的数据。

Last modified: 06 January 2025