基于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和模型对象组成的元组。
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类编写登录视图和认证视图的更多相关文章
- Django的rest_framework的视图之Mixin类编写视图源码解析
Mixin类编写视图 我们这里用auther表来做演示,先为auther和autherdetail写2个url url(r'^autherdetail/(?P<id>\d+)', view ...
- djangorestframework-jwt自带的认证视图进行用户登录验证源代码学习
Django REST framework JWT djangorestframework-jwt自带的认证视图进行用户登录验证源代码学习 SECRET_KEY = '1)q(f8jrz^edwtr2 ...
- Asp.Net Core基于Cookie实现同域单点登录(SSO)
在同一个域名下有很多子系统 如:a.giant.com b.giant.com c.giant.com等 但是这些系统都是giant.com这个子域. 这样的情况就可以在不引用其它框架的情况下, ...
- 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析
写着前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章,比如: ...
- springcloud微服务基于redis集群的单点登录
springcloud微服务基于redis集群的单点登录 yls 2019-9-23 简介 本文介绍微服务架构中如何实现单点登录功能 创建三个服务: 操作redis集群的服务,用于多个服务之间共享数据 ...
- 如何基于LSM-tree架构实现一写多读
一 前言 PolarDB是阿里巴巴自研的新一代云原生关系型数据库,在存储计算分离架构下,利用了软硬件结合的优势,为用户提供具备极致弹性.海量存储.高性能.低成本的数据库服务.X-Engine是阿里巴 ...
- 基于SpringBoot搭建应用开发框架(二) —— 登录认证
零.前言 本文基于<基于SpringBoot搭建应用开发框架(一)——基础架构>,通过该文,熟悉了SpringBoot的用法,完成了应用框架底层的搭建. 在开始本文之前,底层这块已经有了很 ...
- 基于tensorflow的MNIST手写数字识别(二)--入门篇
http://www.jianshu.com/p/4195577585e6 基于tensorflow的MNIST手写字识别(一)--白话卷积神经网络模型 基于tensorflow的MNIST手写数字识 ...
- 基于七牛Python SDK写的一个批量下载脚本
前言 上一篇基于七牛Python SDK写的一个同步脚本所写的脚本只支持上传,不支持文件下载. 虽然这个需求不太强烈,但有可能有人(在备份.迁移时)需要,而官方有没提供对应的工具,所以我就把这个功能也 ...
- 基于Requests和BeautifulSoup实现“自动登录”
基于Requests和BeautifulSoup实现“自动登录”实例 自动登录抽屉新热榜 #!/usr/bin/env python # -*- coding:utf-8 -*- import req ...
随机推荐
- Vue项目引用百度地图并实现搜索定位等功能
Tip:本篇文章为案例分析,技术点较多,所以篇幅较长,认真阅览的你一定会学到很多知识. 前言:百度地图开放平台 给开发者们提供了丰富的地图功能与服务,使我们的项目中可以轻松地实现地图定位.地址搜索.路 ...
- UIPath动态操作控制
如果放弃太早,你永远都不知道自己会错过什么. 一.浏览器 打开浏览器:OpenBrowser: 关闭浏览器:Close Tab.Close Application.Kill Process: 二. 鼠 ...
- python之继承及其实现方法
目录 继承 语法格式 继承的代码实现 多继承 继承 语法格式 class 子类类名(父类1, 父类2...): pass r如果一个类没有继承任何类,则默认继承object python支持多继承 定 ...
- 提升效率,打通万里牛ERP与下游用友U8财务软件的无缝对接
一.对接流程 1.1 销售/售后流程 在万里牛订单出库后,通过轻易云数据集成平台将数据推送至用友U8销售订单和销售出库单,这些单据可以进行关联操作. 当万里牛售后单完成退货入库后,通过数据集成平台将数 ...
- 本地部署modelscope-agent
本地部署modelscope-agent 部署流程 在modelscope社区创建一个自己的空间(假设name是LocalAgent),clone空间到本地(或云服务器如魔搭Notebook) git ...
- vivo 容器平台资源运营实践
作者:vivo 互联网服务器团队 - Chen Han 容器平台针对业务资源申请值偏大的运营问题,通过静态超卖和动态超卖两种技术方案,使业务资源申请值趋于合理化,提高平台资源装箱率和资源利用率. 一. ...
- 【笔记整理】忽略https证书校验
import requests url = "https://sam.huat.edu.cn:8443/selfservice/" # 默认不忽略ssl证书,如果有证书问题的网站会 ...
- 【scikit-learn基础】--『监督学习』之 岭回归
岭回归(Ridge Regression)是一种用于处理共线性数据的线性回归改进方法.和上一篇用基于最小二乘法的线性回归相比,它通过放弃最小二乘的无偏性,以损失部分信息.降低精度为代价来获得更实际和可 ...
- 2024-01-03:用go语言,给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time, 分别表示给 n 堵不同的墙刷油漆需要的开销和时间。你有两名油漆匠, 一位需要 付费 的油漆匠
2024-01-03:用go语言,给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time, 分别表示给 n 堵不同的墙刷油漆需要的开销和时间.你有两名油漆匠, 一位需要 付费 的油漆匠 ...
- 编译安装python 3.11
先处理下opensll的版本,以免编python译环境异常:安装 openssl-1.1.1 yum remove openssl cd /opt wget https://www.openssl.o ...