动态指定序列化类--动态指定权限

动态指定序列化类–动态指定权限–具体操作在views里

  • signals–django的信号机制–我们这里用django的信号机制,而不是从写ModelSerializer里的create方法,是因为这样分离性更强

    # post_save Django中的model对象保存后,自动触发
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from django.contrib.auth import get_user_model
    
    # 获取用户模型类
    UserProfile = get_user_model()
    
    
    # sender谁传递过来的 接受UserProfile传递过来的
    @receiver(post_save, sender=UserProfile)
    # 在传递过来时它会不是是新建的, 因为update也会传递过来
    # 这里的instance就是我们的UserProfile的对象
    def create_auth_token(sender, instance=None, created=False, **kwargs):
        if created:
            # created为True就代表是新建的
            password = instance.password
            instance.set_password(password)
            instance.save()
    
  • serializer

    from rest_framework import serializers
    # 导入rest_framework的验证器
    from rest_framework.validators import UniqueValidator
    
    from django.contrib.auth import get_user_model
    
    from .models import VerifyCode
    
    UserProfile = get_user_model()
    
    
    class UserDetailSerializer(serializers.ModelSerializer):
        class Meta:
            model = UserProfile
            fields = (
                "name",
                "birthday",
                "gender",
                "email",
                "mobile"
            )
    
    
    class UserRegSerializer(serializers.ModelSerializer):
        # 因为UserProfile里面没有code字段, 所以我们要加个code字段
        # help_text = "验证码" 提示是验证码
        # label 一个简短的文本字符串,可用作HTML表单字段或其他描述性元素中字段的名称。
        # write_only 将其设置True为确保在更新或创建实例时可以使用该字段,但在序列化表示时不包括该字段。 默认为 False
        code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label="验证码",
                                     error_messages={
                                         # 设置每种错误的错误提示
                                         # blank是指为空
                                         "blank": "请输入验证码",
                                         # required是键都没有才会报这个错误
                                         "required": "请输入验证码",
                                         "max_length": "验证码过长",
                                         "min_length": "验证码过短"
                                     },
                                     help_text="验证码")
    
        # allow_blank=False表示不能为空
        username = serializers.CharField(required=True, allow_blank=False, label="用户名", help_text="用户名",
                                         validators=[UniqueValidator(queryset=UserProfile.objects.all(), message="用户已经存在")])
    
        # 这里的style把密码设置为密文的, 就像input标签type属性设置为password一样
        # write_only 将其设置True为确保在更新或创建实例时可以使用该字段,但在序列化表示时不包括该字段。 默认为 False
        password = serializers.CharField(
            style={'input_type': 'password'}, label="密码", write_only=True, help_text="密码",
        )
    
        # def create(self, validated_data):
        # ModelSerializer序列化好了的数据都放在validated_data里---序列化后的数据
        #     # 获取到用户对象, 这个对象是继承了AbstractUser的用户对象
        #     user = super(UserRegSerializer, self).create(validated_data=validated_data)
        #     # 所以这里有set_password方法
        #     user.set_password(validated_data["password"])
        #     # ModelSerializer是有save()方法的, save()方法会调用create函数, 我们在这里重置了create函数, 我们在这里加入里密码设置
        #     user.save()
        #     return user
    
        def validate_code(self, code):
            """
            校验验证码
            :param code: 验证码
            :return: 验证码
            """
    
            # 在ModelSerializer前端传递过来的值都会放在initial_data里---原始数据
            # ModelSerializer序列化好了的数据都放在validated_data里---序列化后的数据
    
            # 校验验证码是否存在, 这里一定要排序, 这样才能获取最后的那条记录 这里的username就是mobile
            verify_records = VerifyCode.objects.filter(code=code, mobile=self.initial_data["username"]).order_by("-add_time")
            if verify_records:
                # 获取到最后一条记录
                last_record = verify_records[0]
    
                # 校验验证码是否过期
                # 获取5分钟前的时间  验证码有效期为5分钟
                five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
    
                if five_mintes_ago > last_record.add_time:
                    raise serializers.ValidationError("验证码过期")
    
                if last_record.code != code:
                    raise serializers.ValidationError("验证码错误")
    
                # 因为code只做验证, 所以这里不用吧code return回来
            else:
                raise serializers.ValidationError("验证码错误")
    
        def validate(self, attrs):
            """
            校验所有的字段
            :param attrs:  attrs是所有字段组成的字典
            :return: attrs
            """
            print("attrs", attrs, type(attrs), attrs["username"])
            # 因为mobile值就是username的值, 所以我们把username的值赋值给mobile
            attrs["mobile"] = attrs["username"]
            # 因为我们不需要code字段了, 所以这里可以直接删除
            del attrs["code"]
            return attrs
    
        class Meta:
            model = UserProfile
            # username必填, 因为UserProfile继承了AbstractUser
            # 模型类写了必填, 你序列化的字段写了这个字段的话, 那么前端必须传这个字段的数据
            fields = (
                "username",
                "code",
                "mobile",
                "password"
            )
    
  • views

    from django.contrib.auth import get_user_model
    
    from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
    from rest_framework import viewsets
    # 返回的响应体
    from rest_framework.response import Response
    # 状态码
    from rest_framework import status
    # IsAuthenticated判断用户是否登入
    from rest_framework.permissions import IsAuthenticated
    
    # jwt_payload_handler 是生成payload的
    from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework.authentication import SessionAuthentication
    
    from .serializers import UserRegSerializer, UserDetailSerializer
    
    # 获取用户模型类
    UserProfile = get_user_model()
    
    
    class UserViewSet(CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, viewsets.GenericViewSet):
        """
        create:
            用户注册
        retrieve:
            获取用户信息
        update:
            修改用户信息
        """
        # 会调用ModelSerializer的create或者update方法,
        # 如果继承的是Serializer,就需要自己重载Serializer中的create或者update方法,或者修改mixins的create等方法的逻辑进行保存
        # serializer_class = UserRegSerializer
        queryset = UserProfile.objects.all()
    
        # json token的验证  局部大于全局, 就算你在全局设置过, 但是你只要在这里加上authentication_classes 就会替换全局设置
        authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    
        # 动态指定序列化类
        def get_serializer_class(self):
            # action是访问的方法
            if self.action == "create":
                return UserRegSerializer
            elif self.action == "retrieve":
                return UserDetailSerializer
            elif self.action == "update":
                return UserDetailSerializer
            return UserDetailSerializer
    
        # IsAuthenticated==要获取或修改当前用户信息, 必须登入
        # permission_classes = (IsAuthenticated, ) 但是这里注册的话就不行了
        # 所以重写permissions方法
        # 来动态指定permission
        def get_permissions(self):
            # action是访问的方法
            if self.action == "create":
                return []
            elif self.action == "retrieve":
                return [IsAuthenticated()]
            elif self.action == "update":
                return [IsAuthenticated()]
            # 其他情况
            return []
    
        # 因为我们是用户注册以后就直接帮用户登入, 所以我们要返回个token, 所以我们要重写create
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            # 我们这里要重载perform_create方法, 因为perform_create没有返回值
            user = self.perform_create(serializer)
    
            # 因为Response返回的是serializer.data, 所以我们应该把token放进去
            re_dict = serializer.data
    
            # 生成payload
            payload = jwt_payload_handler(user)
            # 生成user--JWT的token 并把token添加到serializer.data里, 这样我们Response返回就应该返回re_dict了
            # jwt_encode_handler(payload)生成token
            re_dict["token"] = jwt_encode_handler(payload)
    
            # 因为前端还需要个name
            re_dict["name"] = user.name if user.name else user.username
    
            headers = self.get_success_headers(serializer.data)
            return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
    
        def get_object(self):
            # 返回一个具体的对象
            # 这个方法, 只会在RetrieveModelMixin, UpdateModelMixin会执行
            # 返回当前用户信息
            return self.request.user
    
        def perform_create(self, serializer):
            # 返回的serializer关联的model对象
            return serializer.save()
    
    
  • url

    from rest_framework.routers import DefaultRouter
    
    # 生成一个注册器实例对象
    router = DefaultRouter()
    
    # 注册用户
    router.register(r'users', UserViewSet, base_name="users")
    
    urlpatterns = [
        # 自动生成url
        url(r"^", include(router.urls)),
    ]
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只因为你温柔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值