1.9个视图扩展类

1.两个视图基类:APIView、GenricAPIView
2.5个视图扩展类:CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,ListModelMixin,DestroyModelMixin
3.9个视图子类:CreateAPIView,DestroyAPIView,ListAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView
(没有RetrieveUpdateAPIView) 4.利用9个视图自类编写5个接口(继承视图子类则不需要继承GenericAPIView):
views.py:
from rest_framework.generics import CreateAPIView,DestroyAPIView,ListAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView
class BookView(ListCreateAPIView):
#相当于继承了两个类:class BoookView(ListAPIView,CreateAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 继承9个子类不需要单独写某个方法,因为在父类ListCreateAPIView中已经写好了post和get方法。

class BookDetailView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 在其父类RetrieveUpdateDestroyAPIView中已经写好了put,get以及delete方法所以不需要自己写。并且在该BookDetailView方法中不需要上传pk,依然只需要拿到queryset就可以。

models.py:(和之前未变)
from django.db import models class Book(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author') @property
def publish_detail(self):
return {'name':self.publish.name,'addr':self.publish.addr} # {} @property
def author_list(self):
l = []
for author_obj in self.authors.all():
l.append({'name':author_obj.name,'phone':author_obj.phone})
return l # [{},{},{}] class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32) class Author(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=11) serializer.py:
from rest_framework import serializers
from .models import Book
from rest_framework.exceptions import ValidationError class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name','price','publish','authors','publish_detail','author_list']
extra_kwargs = {
'name':{'max_length':8},
'price':{'max_length':8},
'publish':{'write_only':True},
'authors':{'write_only':True},
'publish_detail':{'read_only':True},
'author_list':{'read_only':True}
} def validate_name(self,name):
book_obj = Book.objects.filter(name=name)
if book_obj:
raise ValidationError(f'书籍{name}已存在')
return name """
新需求:如果不需要全部接口功能,只需要查询所有以及删除一个,如何实现?
"""
我们只需要修改继承的类即可,查询所有是ListAPIView,删除一个是DestroyAPIView。serializer.py和models.py中代码未修改。
class BookView(ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer class BookDetailView(DestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer

2.视图集

2.1 通过ModelViewSet编写5个接口

使用ModelViewSet模块写接口,可以只写一个视图类,不用再写类BookDetailView,但是在路由层需要将两种类型分开,但是关联的视图类是同一个视图类,因此视图类中可以简写。
urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
# 不带参数的情况:get请求就会返回list(查所有),post请求就会返回create(新增一个)
path('books/',views.BookView.as_view({'get':'list','post':'create'})),
path('books/<int:pk>/',views.BookView.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
] views.py:
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
通过查看源码得知,ModelViewSet继承了类:CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,.ListModelMixin。所以也可以找得到这些类中的方法list、pos、get、update、delete。

2.2 通过ReadOnlyModelViewSet编写2个只读接口

只读接口包括:读所有和只读一个。如果我们只想编写只读接口,我们可以用模块ReadOnlyModelViewSet。继承该模块之后只能编写2个查询接口,编写其他接口会直接报错。
urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('books/',views.BookView.as_view({'get':'list'})),
path('books/<int:pk>/',views.BookView.as_view({'get':'retrieve'}))
] views.py:
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookView(ReadOnlyModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer

2.3 ViewSetMixin源码分析

1.请求来时,路由匹配成功,会执行views.BookView.as_view({'get':'list','post':'create'})(),我们需要找到as_view()方法。视图类BookView中没有,继续往上找:BookView>>>ModelViewSet>>>GenericViewSet>>>ViewSetMixin。
发现在ViewSetMixin中有as_view()方法。 2.as_view()源码如下:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
...
# actions就是我们上传的字典{'get':'list'},如果不上传字典就会报错
# actions must not be empty
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`") ...
# 去除了view函数的csrf校验,本质就是执行views.view。
return csrf_exempt(view) 3.查找view:
view查找顺序:BookView>>>ModelViewSet>>>GenericViewSet>>>ViewSetMixin>>>as_view()>>>view。 源码:
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# cls是调用函数as_view()的类BookView(视图类)
...
for method, action in actions.items():
# actions:{'get':'list','post':'create'},一共经历两次循环,action是list、create
handler = getattr(self, action)
# self是视图类,从视图类开始找list:BookView>>>ModelViewSet>>>mixins.ListModelMixin,最终在类mixins.ListModelMixin中找到了list方法,handler此时就是list方法
setattr(self, method, handler)
# 将视图类中的get方法修改成了list方法,此时发送get请求就能执行list()
self.request = request
self.args = args
self.kwargs = kwargs # And continue as usual
return self.dispatch(request, *args, **kwargs)
# dispatch方法时APIView中的dispatch方法。
结论:
1.只要继承了ViewSetMixin的视图类,路由写法就变了,需要上传actions参数(上传一个字典)。
2.以{'get': 'list'}为例,以后访问get就是访问list。以后视图类中的方法名,可以任意命名,只需要在路由中做好映射即可。

2.4 from rest_framework.viewsets包下的类

ModelViewSet:5个视图扩展类+GenericViewSet
ReadOnlyModelViewSet:2个视图扩展类(mixins.RetrieveModelMixin, mixins.ListModelMixin)+GenericViewSet
ViewSetMixin:魔法,重新定义了as_view,以后继承它,路由写法就变成了映射的写法
ViewSet:ViewSetMixin+APIView
GenericViewSet:ViewSetMixin+GenericAPIView 以后想继承APIView,但是想改变路由写法,就继承ViewSet。想继承GenericViewSet,但是想改变路由写法,就继承GenericViewSet。

3.视图类大总结

1.两个视图基类:APIView、GenricAPIView

2.5个视图扩展类:CreateModelMixin,UpdateModelMixin,RetrieveModelMixin,ListModelMixin,DestroyModelMixin

3.9个视图子类:CreateAPIView,DestroyAPIView,ListAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView
(没有RetrieveUpdateAPIView) 4.视图类:
ModelViewSet:路由写法变了,只需要写两行,5个接口都有了
ReadOnlyModelViewSet:路由写法变了,只需要写两行,2个只读接口都有了
ViewSetMixin:不是视图类,是魔法,路由写法变了,变成映射了
ViewSet:ViewSetMixin+APIView
GenericViewSet:ViewSetMixin+GenericAPIView

4.路由系统

4.1自动生成路由

1.用SimpleRouter来写(较为常见):
# 1.导入路由类
from rest_framework.routers import SimpleRouter
# 2.实例化得到对象
router = SimpleRouter()
# 3.注册:
router.register('books',views.BookView,'books')
# 4.在urlpatterns中注册(两种方式选一种即可):
方式一:
urlpatterns += router.urls
方式二:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/',include(router.urls))
]
"""
用方式二可以在路由中增加api表示和版本信息,此后访问的路有需要变成
http://127.0.0.1:8000/api/v1/books/
或:http://127.0.0.1:8000/api/v1/books/5/
""" 2.用DefaultRouter来写:
from rest_framework.route rs import DefaultRouter
router = DefaultRouter()
router.register('books',views.BookView,'books')
urlpatterns += router.urls
DefaultRouter和SimpleRouter的区别是:DefaultRouter会多附带一个默认的API根视图、返回一个包含所有列表视图超链接响应数据。
可以发现DefaultRouter的路由要比SimpleRouter多几个:

自动生成路由本质就是做映射,能够自动生成的前提是:视图类中要有5个方法:
get>>>list
get>>>retrieve
put>>>update
post>>>create
delele>>>destroy
ModelViewSet,ReadOnlyModelViewSet可以自动生成 9个视图子类+配合ViewSetMixin 才可以自动生成
5个视图扩展类+GenericAPIView+ViewSetMixin(ViewSetMixin+GenericAPIView=GenericViewSet)

5.action装饰器(配合自动生成路由使用)

"""
当我们自动生成路由时,我们想在路由中通过get方法定义到自定义名字的一个方法名字。这时需要用到action装饰器。action可以控制提交方式映射的对象。
"""
使用步骤:
views.py:
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
class SendView(ViewSet):
@action(methods=['GET'],detail=False,url_path='send_msg',url_name='send_msg')
"""
methods:指定请求方式,结果是个列表,列表内要传提交方式的大写,可以传一个,也可以传多个。
detail:上传的结果是True或者False,False:不带id的路径:
http://127.0.0.1:8000/api/v1/send/send_msg/?phone=111
True:带id的路径:
http://127.0.0.1:8000/api/v1/send/1/send_msg/?phone=111(并且在send_msg中还要加一个参数pk,此时pk是1)
url_path:拼接的路径,可以改,当前路径为:http://127.0.0.1:8000/api/v1/send/send_msg/?phone=111
url_name:别名,反向解析用,一般和函数名保持一致
"""
def send_msg(self,request):
phone = request.query_params.get('phone')
print(f'成功向{phone}发送信息')
# print(self.action) # send_msg self.action拿到的是方法名
return Response({'code':100,'msg':'发送成功'}) urls.py:
from rest_framework.routers import SimpleRouter router = SimpleRouter()
router.register('send',views.SendView,'send')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/',include(router.urls))
] 补充思路:
不同的action使用不同的序列化类: class SendView(ViewSet):
queryset = None
serializer_class = '序列化类' def get_serializer(self,*args,**kwargs):
if self.action == 'send_msg':
return '某个序列化类'
else:
return '某个序列化类' @action(methods=['GET'],detail=False,url_path='send_www',url_name='send_msg')
def send_msg(self,request):
pass @action(methods=['POST'],detail=False,url_path='phone_msg',url_name='phone_msg')
def phone_msg(self,request):
pass

6.登陆接口

编辑一个登陆接口,如果用户未登陆过第一次登陆就在UserToken中插一条随机字符串,登陆过再次登陆时就修改此条字符串:
urls.py:
router.register('user',views.UserView,'user') urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/',include(router.urls))
] models.py:
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32) class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to='User',on_delete=models.CASCADE) views.py:
from .models import User,UserToken
import uuid class UserView(ViewSet):
@action(methods=['POST'],detail=False)
def login(self,request):
username = request.data.get('username')
password = request.data.get('password')
user_obj = User.objects.filter(username=username,password=password).first()
if user_obj:
token = str(uuid.uuid4())
UserToken.objects.update_or_create(user=user_obj,defaults={'token':token})
return Response({'code':100,'msg':'登陆成功','token':token})
else:
return Response({'code':100,'msg':'用户名或密码错误'})

drf-视图集、路由系统、action装饰器的更多相关文章

  1. DRF的action装饰器

    1.action装饰器 Django默认的路由分发规则决定了视图函数只能以get.post等请求方式命名,如果想要使用自定义的方式命名,我们可以使用action去映射请求方法名与自定义方法 view. ...

  2. DRF的@action装饰器

    # 转自:http://www.cnblogs.com/zhzhlong/p/9325180.html 视图集中附加action的声明 from rest_framework.decorators i ...

  3. flask的路由配置,特殊装饰器

    1,flask中的路由 endpoint-url_for反向地址 endpoint默认是视图函数名endpoint="雪雪" methods 指定视图函数的请求方式,默认GET d ...

  4. tornado 第二种路由方法(装饰器)

    #!/usr/bin/env python # _*_coding:utf-8 _*_ import tornado.ioloop import tornado.web application = t ...

  5. Flask 中的路由系统

    基本用法 Django的路由系统url集中在一起,而Flask的路由系统以装饰器的形式装饰在视图上如: @app.route("/",methods=["GET" ...

  6. Django 学习之Rest Framework 视图集与Routers与扩展功能

    一.视图集使用 使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中: list() 提供一组数据 retrieve() 提供单个数据 create() 创建数据 update() 保存数 ...

  7. rest-framework-@action()装饰器

    路由Routers 使用方法:   在urls.py中定义路由 from rest_framework.routers import DefaultRouter # 定义视图集的路由 router = ...

  8. 使用DRF视图集时自定义action方法

    在我们用DRF视图集完成了查找全部部门,创建一个新的部门,查找一个部门,修改一个部门,删除一个部门的功能后,views.py的代码是这样子的: class DepartmentViewSet(Mode ...

  9. Flask路由系统

    Flask路由系统 我们之前了解了路由系统是由带参数的装饰器完成的. 路由本质:装饰器和闭包实现的. 设置路由的两种方式 第一种: @app.route('/index') def index(): ...

  10. python装饰器 语法糖

    简介: 装饰器(Decorators)是 Python 的一个重要部分.简单地说:他们是修改其他函数的功能的函数. 比如说我们写flask,路由就是用装饰器定义的.如果写权限控制,那么权限控制一般也是 ...

随机推荐

  1. go GMP

    动态栈 操作系统的线程一般都有固定的栈内存(通常为2MB),而 Go 语言中的 goroutine 非常轻量级,一个 goroutine 的初始栈空间很小(一般为2KB),所以在 Go 语言中一次创建 ...

  2. Perl exists 函数和defined函数的区别

    Perl exists 函数和defined函数的区别

  3. 两种方案实现Dubbo泛化调用

    Dubbo的泛化调用是一个服务A在没有服务B的依赖,包的情况下,只知道服务B的服务名:服务的接口的全限定类名和方法名,以及参数,实现服务A调用服务B. 原文链接:http://blog.qiyuan. ...

  4. ThinkPHP 6.0 RC2 版本发布——架构升级、精简核心

    自从5.2版本变更为6.0以来,官方一直致力于优化架构和精简核心,同时也在准备手册和测试工作,在经过近1个月的开发迭代后,官方宣布发布ThinkPHP6.0RC2版本. 主要更新 相比较RC1版本更新 ...

  5. i春秋Blog

    打开是个普普通通的hello world 然后有注册有登录 不多说我们直接注册登录试试 登录后有个POST选项,可以上传东西,先试试上传文字 没什么重要的回显 再上传一个文档试试 爆出提示:这里使用的 ...

  6. c++学习笔记(入门)

    1 struct和class的区别 struct成员变量(成员函数)的访问属性缺省的情况下默认为public. class成员变量(成员函数)的访问属性缺省的情况下默认为private. 2 初始化列 ...

  7. orcl between and 时间

    在网上查阅,大家都说between and两边都会包含,但是对于时期来讲,他会包含前者,不会包含后者. 也就是说求一个时间介于上周六到本周五的区间,用between and 需要计算出上周六的时间和本 ...

  8. Kettle:跨库(SQLServer->PostgreSQL)同步多张表数据的详细设计过程

    〇.参考地址 1.多个Excel实现同步 https://www.wangt.cc/2021/05/kettle%E5%A4%9A%E4%B8%AA%E8%A1%A8%E4%B8%80%E8%B5%B ...

  9. MongoDB数据库与Python的交互

    一.缘由 这是之前学习的时候写下的基础代码,包含着MongDB数据库和Python交互的基本操作. 二.代码实现 import pymongo #连接数据库 client=pymongo.MongoC ...

  10. Java/JDK各版本主要特性汇总

    目录 Java18(2022.3) Java17(2021.9)(LTS版本) Java16(2021.3) Java15(2020.9) Java14(2020.3) Java13(2019.9) ...