Day 29 jwt自定义登录+多表操作

Day 29 jwt自定义登录+多表操作

一、基于JWT的多方式登录

1 手机号+密码 用户名+密码 邮箱+密码

2 流程分析(post请求):

​ -路由:自动生成
​ -视图类:ViewSet(ViewSetMixin, views.APIView)
​ -序列化类:重写validate方法,在这里面对用户名和密码进行校验

3 代码实现

路由

path('login/', views.LoginViewSet.as_view({
    
    'post':'create'})),

视图

class LoginViewSet(ViewSet):
    def create(self, request, *args, **kwargs):
        # 实例化得到一个序列化类的对象
        # ser=LoginSerializer(data=request.data,context={'request':request})
        ser = LoginSerializer(data=request.data)
        # 序列化类的对象的校验方法
        ser.is_valid(raise_exception=True)  # 字段自己的校验,局部钩子校验,全局钩子校验
        # 如果通过,表示登录成功,返回手动签发的token
        token = ser.context.get('token')
        username = ser.context.get('username')
        return APIResponse(token=token, username=username)
        # 如果失败,不用管了

序列化类

from rest_framework import serializers
from app01.models import UserInfo
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
from rest_framework_jwt.views import obtain_jwt_token

class LoginSerializer(serializers.ModelSerializer):
    username = serializers.CharField()

    class Meta:
        model = UserInfo
        fields = ['username', 'password']

    def validate(self, attrs):
        # username可能是邮箱,手机号,用户名
        username = attrs.get('username')
        password = attrs.get('password')
        # 如果是手机号
        if re.match('^1[3-9]\d{9}$', username):
            # 以手机号登录
            user = UserInfo.objects.filter(phone=username).first()
        elif re.match('^.+@.+$', username):
            # 以邮箱登录
            user = UserInfo.objects.filter(email=username).first()
        else:
            # 以用户名登录
            user = UserInfo.objects.filter(username=username).first()
        # 如果user有值并且密码正确
        if user and user.check_password(password):
            # 登录成功,生成token
            # drf-jwt中有通过user对象生成token的方法
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            # token是要在视图类中使用,现在我们在序列化类中
            # self.context.get('request')
            # 视图类和序列化类之间通过context这个字典来传递数据
            self.context['token'] = token
            self.context['username'] = user.username
            return attrs

        else:
            raise ValidationError('用户名或密码错误')

二、自定义user表,签发token,认证类

表模型

class MyUser(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    phone = models.CharField(max_length=32)
    email = models.EmailField()

路由

path('login2/', views.MyLoginView.as_view()),

视图

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework_jwt.views import obtain_jwt_token


class MyLoginView(APIView):
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        # 如果是手机号
        if re.match('^1[3-9]\d{9}$', username):
            # 以手机号登录
            user = MyUser.objects.filter(phone=username).first()
        elif re.match('^.+@.+$', username):
            # 以邮箱登录
            user = MyUser.objects.filter(email=username).first()
        else:
            # 以用户名登录
            user = MyUser.objects.filter(username=username).first()
        # 如果user有值并且密码正确
        if user and user.password == password:
            # 登录成功,生成token
            # drf-jwt中有通过user对象生成token的方法
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return APIResponse(token=token, username=user.username)
        else:
            return APIResponse(code=101, msg='用户名或密码错误')

三、多表的增删查改

1 路由

urlpatterns = [
    
    path('book/', views.BookView.as_view()),
    re_path('^book/(?P<pk>\d+)', views.BookView.as_view()),
    ]

2 表模型

#抽象出一个基表(不再数据库生成,abstract=True),只用来继承

class Base(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        # 基表必须设置abstract,基表就是给普通Model类继承使用的,
        # 设置了abstract就不会完成数据库迁移完成建表
        abstract = True  # 抽象=True


class Book(Base):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
    # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    
    def publish_name(self):
        return self.publish.name
    
    def authors_info(self):
        authors = ','.join([i.name for i in self.authors.all()])
        return {
    
    'authors': authors, 'sex': [author.get_sex_display() for author in self.authors.all()]}


class Publish(Base):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=64)


class Author(Base):
    name = models.CharField(max_length=32)
    sex = models.IntegerField(choices=((1, '男'), (2, '女')), default=0)
    
    def get_info(self):
        return self.detail.age


class AuthorDetail(Base):
    age = models.IntegerField()
    # 有作者可以没有详情,删除作者,详情一定会被级联删除
    # 外键字段为正向查询字段,related_name是反向查询字段
    author = models.OneToOneField(to='Author', related_name='detail', on_delete=models.CASCADE)

注意:以后所有的数据删除,尽量用软删除,使用一个字段标志是否删除,而不是真正的从数据库中删除
-好处:1 这样删除数据不会影响索引,不会导致索引失效
2 之前存的用户数据还在,以备以后使用

3 序列化器

class ListBookSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        print(instance)  # 这里面也是一个列表 包含着书对象
        print(validated_data)  # 这是一个列表里面存放着每本书的修改数据
        return [self.child.update(book, validated_data[i]) for i, book in enumerate(instance)]


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        list_serializer_class = ListBookSerializer  # 指定many=True的时候,生成的ListBookSerializer的对象了
        fields = ['name', 'price', 'publish', 'authors', 'publish_name', 'authors_info']
        extra_kwargs = {
    
    
            'publish': {
    
    'write_only': True, },
            'authors': {
    
    'write_only': True, },
            'publish_name': {
    
    'read_only': True, },
            'authors_info': {
    
    'read_only': True, },
        }

4 视图函数

class BookView(APIView):
    
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:
            book = models.Book.objects.get(id=pk, is_delete=False)
            ser = serializer.BookSerializer(instance=book)
        else:
            books = models.Book.objects.filter(is_delete=False)
            ser = serializer.BookSerializer(instance=books, many=True)
        return APIResponse(ser.data)
    
    def post(self, request, *args, **kwargs):
        if isinstance(request.data, dict):
            # 新增单条
            ser = serializer.BookSerializer(data=request.data)
            ser.is_valid()
            i = request.data.get('name')
        elif isinstance(request.data, list):
            ser = serializer.BookSerializer(data=request.data, many=True)
            ser.is_valid(raise_exception=True)
            i = ','.join([j.get('name') for j in request.data])
        msg = f'书籍 {i} 添加成功!'
        ser.save()
        return APIResponse(msg=msg, data=ser.data)
    
    def put(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        if pk:  # 存在pk说明单个修改 从路由中获取
            # 找书
            book = models.Book.objects.get(id=pk)
            # 序列化存储 默认的update
            ser = serializer.BookSerializer(instance=book, data=request.data)
            msg = '修改{}成功'.format(request.data.get('name'), )
        else:  # 说明多条数据在put的data中
            pks = []
            # data是一个 存放书籍修改信息的列表
            for item in request.data:
                pks.append(item.get('id'))
                # 反序列化的时候用不到id 也无法修改id 多以弹出来
                item.pop('id')
            # id__in sql的高级操作!
            books = models.Book.objects.filter(id__in=pks, is_delete=False)
            # 分发到序列器 然后进行many判断 使用 ListBookSerializer
            ser = serializer.BookSerializer(instance=books, data=request.data, many=True)
            ser.is_valid(raise_exception=True)
            ser.save()
            i = ','.join([j.get('name') for j in request.data])
        msg = f'书籍 {i} 修改成功!'
        print(msg)
        print(ser.data)
        return APIResponse(msg=msg, data=ser.data)
    
    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk', None)
        pks = []
        if pk:
            pks.append(pk)
        # 请求为一个索引的列表
        pks = request.data
        # 进行软删除,返回的是一个整形 操作的个数
        res = models.Book.objects.filter(id__in=pks).update(is_delete=True)
        if res > 0:  # 说明操作了数据
            msg = '删除{}条数据成功!'.format(res, )
        else:  # 说明数据不存在
            msg = '找不到该书籍哦!'
        return APIResponse(msg=msg)

猜你喜欢

转载自blog.csdn.net/A1L__/article/details/109732308
今日推荐