这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
路由
# 继承了ViewSetMixin和GenericAPIView和类可以使用drf提供了路由系统,自动生成url,
# 也可以说继承了视图集GenericViewSet的子类都可以使用路由系统
# 需要注意,此时默认通过actions分配的请求方法是五个视图扩展类的方法
# 所以简便使用路由系统时最好搭配五个视图扩展类,也可以自己实现,如list方法
使用方法:
# views.py中的视图类满足继承要求,视图集合类
# urls.py
1:导入routers模块
from rest_framework import routers
2:通过两个类中的一个实例化对象
routers.DefaultRouter 生成的路由个数更多[华而不实]
routers.SimpleRouter 个人推荐
router = routers.SimpleRouter()
3:注册
# router.register('前缀','继承自ModelViewSet视图类','别名')
router.register('books', views.BookViewSet) # 不要加斜杠了
4:增加到urlpattern中
# router.urls # 自动生成的路由,两条
urlpatterns += router.urls
代码实现
# views.py
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = ser.BookModelSerializer
def list(self, request, *args, **kwargs):
book_list = self.get_queryset()[:3]
ser_obj = self.get_serializer(book_list, many=True)
return Response(ser_obj.data)
# urls.py
# 第一步
from rest_framework import routers
from app01 import views
# 第二步
router = routers.SimpleRouter()
# 第三步
router.register(prefix='books', viewset=views.BookModelViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
# 第四步
urlpatterns += router.urls
# 新增的两条url
print(router.urls)
[
<RegexURLPattern book-list ^books/$>,
<RegexURLPattern book-detail ^books/(?P<pk>[^/.]+)/$>
]
action装饰器
# 用途
为了给继承自ModelViewSet的视图类中定义的函数也添加路由
# 使用
from rest_framework.decorators import action
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = ser.BookModelSerializer
@action(methods=['get'], detail=False)
def get_top3(self, request):
print(request.user.username)
book_list = self.get_queryset()[:3]
ser_obj = self.get_serializer(book_list, many=True)
return Response(ser_obj.data)
# 参数说明:
methods参数赋值一个列表,放可以访问该方法的请求方式
detail参数是布尔值:
detail=False表示方法是不使用pk;^books/get_top3/$
反之使用pk参数;^books/(?P<pk>[^/.]+)/get_top3/$
# 总结:action装饰器,放在被装饰的函数上方,method:请求方式,detail:是否带pk
认证的使用
# 使用流程
1 写一个类,继承BaseAuthentication,重写authenticate,写认证的逻辑;
2 认证通过,返回两个值,一个值最终给了Requet对象的user;
3 认证失败,抛异常:APIException或者AuthenticationFailed
# 使用范围
全局使用,项目的配置文件
局部使用,写在视图类中
认证的配置
# 全局配置,项目的settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.app_auth.LoginAuthentication", ]
}
# 局部配置,当前视图类使用
authentication_classes = [LoginAuthentication, ]
# 局部禁用
authentication_classes = []
# 可以有多个认证,按照列表中的顺序以此执行
认证源码分析
# APIView的dispatch方法,替换了原生django的当次请求对象
# 替换的过程是实例化一个drf的Request对象,并给该request对象传入一个存放认证器类对象的列表
# dispatch方法内调用initial方法,在initial方法内调用登陆认证方法perform_authentication()
# 在该登陆认证方法内,调用request.user,[user是request对象的方法伪装成了数据属性]
# request的user内调用reuest的_authenticate方法是登陆认证的核心所在
# 在reuest的_authenticate方法内,循环request对象保存的认证器对象列表,以此校验
# 循环过程中,调用认证器对象的authenticate方法,并把request传进去。
# 这是就开始执行我们自己写的认证类对象的authenticate方法,
# 在authenticate方法内,认证成功,返回两个值;认证失败,抛出AuthenticationFailed异常
# 认证成功,authenticate方法需要返回两个数据分别赋值给request.user和request.auth
# 因此,认证通过进入视图后,可以在request中取出当前登陆用户对象request.user
核心源码
# request对象的_authenticate方法内
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
依次调用每一个认证类对象的authenticate方法,并传入request对象
self.user, self.auth = user_auth_tuple 这是为啥认证成功要返回两个值的原因
"""
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
代码案例
- models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
publish = models.CharField(max_length=32)
def __str__(self):
return self.name
class Username(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
user_type = models.IntegerField(choices=((1,'普通用户'),(2,'超级用户')))
class UserToken(models.Model):
user = models.OneToOneField(to='Username',on_delete=models.DO_NOTHING)
token = models.CharField(max_length=128)
- permission.py(自定义认证类)‘
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
# 这里也可以写APIException, AuthenticationFailed继承了APIException
from app01.models import UserToken
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑,如果认证通过返回true,失败返回false
token = request.META.get('HTTP_TOKEN')
if token:
user_token_obj = UserToken.objects.filter(token=token).first()
if user_token_obj:
return user_token_obj.user,token
raise AuthenticationFailed('认证失败')
raise AuthenticationFailed('请求地址中需要携带token')
- settings.py全局配置
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":["app01.permission.MyAuthentication",]
}
- views.py
from django.shortcuts import render
from rest_framework.viewsets import ModelViewSet
# Create your views here.
from app01 import models
from app01.ser import BookModelSerializer
class BookModelView(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
import uuid
class Login(APIView):
authentication_classes = []
def post(self,request):
name = request.data.get('name')
age = request.data.get('age')
user_obj = models.Username.objects.filter(name=name,age=age).first()
if user_obj:
token = uuid.uuid4()
models.UserToken.objects.update_or_create(defaults={'token':token},user=user_obj)
return Response(data={'status':100,'msg':'登陆成功','token':token})
return Response({'status':100,'msg':'用户名或者年龄错误',})
- urls.py
from django.urls import path
from app01 import views
from rest_framework import routers
router = routers.SimpleRouter()
router.register('books',viewset=views.BookModelView)
urlpatterns = [
path('admin/', admin.site.urls),
path('login/',views.Login.as_view()),
]
urlpatterns += router.urls
内置认证方案(需要配合权限使用)
可以在配置文件中配置全局默认的认证方案
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
)
}
也可以在每个视图中通过设置authentication_classess属性来设置
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
class ExampleView(APIView):
# 类属性
authentication_classes = [SessionAuthentication, BasicAuthentication]
...
结语
文章首发于微信公众号程序媛小庄,同步于掘金。
码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)