基于rest_framework的ModelViewSet类编写登录视图和认证视图

这篇具有很好参考价值的文章主要介绍了基于rest_framework的ModelViewSet类编写登录视图和认证视图。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景:看了博主一抹浅笑的rest_framework认证模板,发现登录视图函数是基于APIView类封装。
优化:使用ModelViewSet类通过重写create方法编写登录函数。
环境:既然接触到rest_framework的使用,相信已经搭建好相关环境了。

1 建立模型

编写模型类

# models.py
from django.db import models
class User(models.Model):
    username = models.CharField(verbose_name='用户名称',unique=True,max_length=16)
    password = models.CharField(verbose_name='登陆密码',max_length=16)
class Token(models.Model):
    username = models.CharField(verbose_name='用户名称',unique=True,max_length=16)
    token = models.CharField(verbose_name='验证密钥',max_length=32)

生成迁移文件

python manage.py makemigrations

迁移数据模型

python manage.py migrate

2 确定需要重写的方法

查看ModelViewSet类源码

'''
class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass
'''

最终目的是往Token模型对应的表添加数据,所以得选择CreateModelMixin模型的源码查看。

'''
class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}
'''

查看得知,CreateModelMixin类下的create方法调用了serializer类的save方法创建数据。继续查看save方法。
通过serializers.ModelSerializer定位到serializers.py文件,搜索'def save('定位到以下内容。

'''
    def save(self, **kwargs):
        assert hasattr(self, '_errors'), (
            'You must call `.is_valid()` before calling `.save()`.'
        )

        assert not self.errors, (
            'You cannot call `.save()` on a serializer with invalid data.'
        )

        # Guard against incorrect use of `serializer.save(commit=False)`
        assert 'commit' not in kwargs, (
            "'commit' is not a valid keyword argument to the 'save()' method. "
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
            "You can also pass additional keyword arguments to 'save()' if you "
            "need to set extra attributes on the saved model instance. "
            "For example: 'serializer.save(owner=request.user)'.'"
        )

        assert not hasattr(self, '_data'), (
            "You cannot call `.save()` after accessing `serializer.data`."
            "If you need to access data before committing to the database then "
            "inspect 'serializer.validated_data' instead. "
        )

        validated_data = {**self.validated_data, **kwargs}

        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                '`update()` did not return an object instance.'
            )
        else:
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                '`create()` did not return an object instance.'
            )
'''

看最后这个if……else……语句中的self.instance = self.create(validated_data)。
说明这里调用了create方法,返回一个模型对象。于是查看ModelSerializer类的create方法。

'''
    def create(self, validated_data):
        """
        We have a bit of extra checking around this in order to provide
        descriptive messages when something goes wrong, but this method is
        essentially just:

            return ExampleModel.objects.create(**validated_data)

        If there are many to many fields present on the instance then they
        cannot be set until the model is instantiated, in which case the
        implementation is like so:

            example_relationship = validated_data.pop('example_relationship')
            instance = ExampleModel.objects.create(**validated_data)
            instance.example_relationship = example_relationship
            return instance

        The default implementation also does not handle nested relationships.
        If you want to support writable nested relationships you'll need
        to write an explicit `.create()` method.
        """
        raise_errors_on_nested_writes('create', self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass._default_manager.create(**validated_data)
        except TypeError:
            tb = traceback.format_exc()
            msg = (
                'Got a `TypeError` when calling `%s.%s.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.%s.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception was:\n %s' %
                (
                    ModelClass.__name__,
                    ModelClass._default_manager.name,
                    ModelClass.__name__,
                    ModelClass._default_manager.name,
                    self.__class__.__name__,
                    tb
                )
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                field = getattr(instance, field_name)
                field.set(value)

        return instance
'''

这逻辑我是没看懂,但是通过print、type、dir函数可以确定
接收对象validated_data是一个字典,
返回对象instance是一个模型对象。
于是可以把源码cv过来,简单测试是否能够通。

import time
import hashlib

from rest_framework import status
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from myapp import models as myapp_models

class TokenSerializer(serializers.ModelSerializer):
    class Meta:
        model = myapp_models.Token
        fields = '__all__'
    def create(self,validated_data):
        ######################################
        query_obj = myapp_models.Token.objects.update_or_create(
            username=validated_data['username'],
            defaults={"username":validated_data['username'],"token":validated_data['token']})[0]
        print(query_obj)
        return query_obj
        #------------------------------------#
class LoginView(ModelViewSet):
    queryset = myapp_models.Token.objects.all()
    serializer_class = TokenSerializer
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

3 重写create方法

3.1 编写登录逻辑

TokenSerializer
1.获取username和password。
2.验证username、password匹配性。
3.匹配错误:更新或创建模型中username对应的token为空字符串,返回模型对象。
4.匹配正确:通过md5加密生成token,更新或创建模型中username对应的token为密钥。
ModelViewSet
1.根据username查询token值。
2.将username、token值设置到session会话。

import time
import hashlib

from rest_framework import status
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from myapp import models as myapp_models

class TokenSerializer(serializers.ModelSerializer):
    class Meta:
        model = myapp_models.Token
        fields = '__all__'
    def create(self,validated_data):
        ######################################
        user_obj = myapp_models.User.objects.filter(
            username=validated_data['username'],
            password=validated_data['token'])
        user_dict = validated_data
        user_dict['token'] = ''
        if not user_obj.exists():
            query_obj = myapp_models.Token.objects.update_or_create(
                username=user_dict['username'],
                defaults={"username":user_dict['username'],"token":user_dict['token']})[0]
            return query_obj
        validated_data['token'] = hashlib.md5(
            ''.format(time.time(),''.join(validated_data.values())).encode()).hexdigest()
        query_obj = myapp_models.Token.objects.update_or_create(
            username=validated_data['username'],
            defaults={"username":validated_data['username'],"token":validated_data['token']})[0]
        print(query_obj)
        return query_obj
        #------------------------------------#
class LoginView(ModelViewSet):
    queryset = myapp_models.Token.objects.all()
    serializer_class = TokenSerializer
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        ######################################
        token_obj = myapp_models.Token.objects.filter(
            username=request.POST.get('username')).first()
        if token_obj.token == '':
            request.session['username'] = token_obj.username
            request.session['token'] = token_obj.token
            return Response('检查输入的账户和密码')
        request.session['username'] = token_obj.username
        request.session['token'] = token_obj.token
        #------------------------------------#
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

3.2 编写认证逻辑

1.从session中获取username,token。
2.判断username,token是否不存在、或token是否为空字符串。
3.判断正确:抛出异常。
4.判断错误:范围username和模型对象组成的元组。文章来源地址https://www.toymoban.com/news/detail-797582.html

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication

from myapp import models as myapp_models

class Authentication(BaseAuthentication):
    def authenticate(self,request):
        ######################################
        username = request._request.session.get('username','')
        token = request._request.session.get('token','')
        token_obj = myapp_models.Token.objects.filter(
            username=username,token=token)
        if not token_obj.exists or token_obj.first().token == '':
            raise exceptions.AuthenticationFailed('认证失败')
        return (token_obj.first().username,token_obj.first())
        #------------------------------------#

3.3 添加路由

path('login/',myapp_views.LoginView.as_view({
        'post':'create'}),name='login')

到了这里,关于基于rest_framework的ModelViewSet类编写登录视图和认证视图的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Django Rest_Framework(二)

    什么时候声明的序列化器需要继承序列化器基类Serializer,什么时候继承模型序列化器类ModelSerializer? 看数据是否从mysql数据库中获取,如果是则使用ModelSerializer,不是则使用Serializer drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作。所以在django原有的dja

    2024年02月14日
    浏览(26)
  • django rest_framework 部署doc文档

    1.背景    在实际开发过程中,前后端分离的项目,是需要将一份完整的接口文档交付给前端开发人员,这样有利于开发速度和开发质量,以及有可能减少协同时间。 2.内容   本项目是以Python+django+rest_framework作为技术框架,在这套框架中,是有自己支持的api文档,现将实现方

    2024年01月17日
    浏览(36)
  • django rest_framework 框架动态设置序列化返回的字段

    动态修改字段可以使Django rest框架API像graphQL端点一样,只从模型中检索所需的字段。 一旦序列化器被初始化,就可以使用.fields属性访问序列化器上设置的字段字典。访问和修改此属性允许您动态修改序列化器。 显式地修改fields参数可以帮助您做一些奇怪的事情,例如在运行

    2024年02月16日
    浏览(42)
  • rest_framework(3)序列化和反序列化(一)

    本系列文章中的上一篇文章:rest_framework.views.APIView 源码解析 导入的包 序列化器定义 BookView 类 get 方法中是序列化的使用,post 方法中是反序列化的使用 serializers.save() 方法分析 \\\"\\\"\\\"             serializer.save() 的源码             先看序列化器 BookSerializer ,类中没有 save() 方

    2024年02月11日
    浏览(29)
  • python Django Rest_Framework框架 安装与配置(图文并茂版)

    Django REST framework 是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发 REST API接口 应用。在REST framework中,提供了 序列化器Serialzier 的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。R

    2024年02月02日
    浏览(31)
  • Django rest_framework Serializer中的create、Views中的create/perform_create的区别

    对于后端来说,前后端分离的方式能让前后端的开发都爽。和所有的爽一样,每爽一次都要付出一定的代价。而前后端分离的代价,就是后端要面对巨量的模块化的功能组件以及这些组件的常规用法与重写复用。有一点经验,关于[Django rest_framework ] Serializer 中的create()、 Vie

    2024年02月13日
    浏览(25)
  • Django的Rest framework搭建自定义授权登录

    提示:阅读本章之前,请先阅读目录 之前的文章有写过通过jwt认证的文章,今天这一篇是通过自定义用户认证的; 使用场景:有些API需要用户登录成功之后,才能访问;有些无需登录就能访问 解决方法:创建两张表,一张用户表,一张token表,保存用户登录成功后生产的t

    2024年02月15日
    浏览(25)
  • 基于文心一言AI大模型,编写一段python3程序以获取华为分布式块存储REST接口的实时数据

    本文尝试基于文心一言AI大模型,编写一段python3程序以获取华为分布式块存储REST接口的实时数据。 一、用文心一言AI大模型将需求转化为样例代码 1、第一次对话:“python3写一段从rest服务器获取数据的样例代码” 同时生成了以下注解  这段代码首先定义了一个函数  get_da

    2024年02月03日
    浏览(32)
  • 深入探索 Django Rest Framework

    这篇文章会详细介绍Django REST Framework的核心组成部分,包括Serializers、ViewSets、Routers、权限和认证系统以及测试和调试工具。文章从基础开始,逐步深入,旨在帮助读者掌握使用Django REST Framework构建复杂API的技能。 Django REST框架,通常简称为DRF,是一个强大而灵活的Web API工具

    2024年02月15日
    浏览(28)
  • django rest framework 学习笔记2

    注意:该文章部分摘抄之百度,仅当做学习笔记供小白使用,若侵权请联系删除! 显示关联表的数据,本示例会显示所有的关联的数据信息 读取到的结果器数据关联的为数字,此时需要进行一些操作可以读到正确数据 方法1: source=\\\'字段名.关联属性值\\\' 方法2 : 返回其属性值

    2024年02月20日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包