首先我们需要先了解一下CBV的执行流程:
通常在写CBV模式时,会在路由匹配时re_path('login/', views.LoginView.as_view()),进入as_view() 中发现最后返回的是view,然后又发现view函数中最后返回的是dispatch(),进入该函数发现,其实其中就是通过反射执行request.method对应的方法。总结,CBV本质是通过执行反射进行的。而且在执行对应方法之前会执行dispatch().
框架之认证系统
当我们的视图对应的类继承的是rest_framework.views 的APIView时,我们进入dispatch(),发现request = self.initialize_request(request, *args, **kwargs)中最后返回的是Request的对象,对原生request进行封装(进入Request发现原生的request为_request),并且添加了如authenticators=self.get_authenticators()等属性,进入get_authenticators()返回的是authentication_classes的实例对象,进而发现默认authentication_classes是对读配置文件的。
ok,我们继续看dispatch(),在封装之后又self.initial(request, *args, **kwargs),initial中又走self.perform_authentication(request),该方法返回了request.user,这时的request是最开始封装的request,查看Requet其中的user,发现走了self._authenticate(),其中对self.authenticators进行循环,执行对应的authenticate()。
from django.db import models # Create your models here. class UserInfo(models.Model): username = models.CharField(max_length=16) password = models.CharField(max_length=32) type = models.SmallIntegerField( choices=((1, '普通用户'), (2, 'VIP用户')), default=1 ) class Token(models.Model): user = models.OneToOneField(to='UserInfo',on_delete=models.CASCADE) token_code = models.CharField(max_length=128)
from django.shortcuts import render,HttpResponse from app01 import models from django.views import View from rest_framework.views import APIView from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from django.http import JsonResponse # Create your views here. def get_random_token(username): """ 根据用户名和时间戳生成随机token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) # 加盐 m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() # 自定义认证类 class MyAuth(BaseAuthentication): def authenticate(self,request): try: token = request._request.GET.get('token') token_obj = models.Token.objects.get(token_code=token) if token_obj: return (token_obj.user, token_obj) else: raise AuthenticationFailed('认证失败') except Exception as e: raise AuthenticationFailed('请携带token,认证失败') class LoginView(APIView): """ 校验用户名密码是否正确从而生成token的视图 """ authentication_classes = [MyAuth, ] def post(self, request): # self.dispatch() res = {"code": 0} username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 如果用户名密码正确 token = get_random_token(username) # 找到user对象,有就更新(更新token_code),没有就创建 models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用户名或密码错误" return JsonResponse(res) def get(self,request): return HttpResponse('get')
如果我们自己写了authentication_classes,那么就会走我们写在其中的类的实例对象,我自定义认证类中必须写有authenticate()实现认证逻辑。
re_path('login/', views.LoginView.as_view())
定义全局认证:
因为默认的authentication_classes是读配置文件的,如果我们想要设置全局的,我们要在settings.py中设置
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ('app01.utils.MyAuth.MyAuth', ), }
我把自定义认证类放在app01.utils.MyAuth中,如果某个视图不想认证就设置authentication_classes=[]。
权限系统
同样让我们先从dispatch()开始走,在initial中看到有check_permissions(),其中有循环get_permissions(),进入get_permissions()发现还是循环返回permission_classes中的类对象,进去发现默认还是在配置文件中,如果我们自己在视图中写,不就走我们自己设置的了?ok,check_permissions()中循环get_permissions()时,都对has_permission进行判断。
我们自定义的权限类中要有has_permission()实现权限逻辑流程。
from django.shortcuts import render,HttpResponse from app01 import models from django.views import View from rest_framework.views import APIView from rest_framework.permissions import BasePermission from django.http import JsonResponse # Create your views here. def get_random_token(username): """ 根据用户名和时间戳生成随机token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) # 加盐 m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() # 自定义权限 class MyPermission(BasePermission): message = 'vip用户才能访问' def has_permission(self,request,view): ''' 自定义权限只有vip用户才能访问 :param request: :return: ''' # 因为在进行权限判断之前已经做了认证,可以直接拿到request.user # print('request.user',request.user) # print('request',request) # print('view',view) # print('request.user.type',request.user.type) if request.user and request.user.type==2: return True else: return False class LoginView(APIView): """ 校验用户名密码是否正确从而生成token的视图 """ authentication_classes = [ ] permission_classes=[] def post(self, request): self.dispatch res = {"code": 0} username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 如果用户名密码正确 token = get_random_token(username) # 找到user对象,有就更新(更新token_code),没有就创建 models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用户名或密码错误" return JsonResponse(res) def get(self,request): return HttpResponse('login get') class OrderView(APIView): permission_classes = [MyPermission,] def get(self,request): return HttpResponse('这里是订单页面,只有vip用户可以访问')
re_path('order/$', views.OrderView.as_view()),
定义全局权限认证
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ('app01.utils.MyAuth.MyAuth', ), 'DEFAULT_PERMISSION_CLASSES':('app01.utils.MyPermission.MyPermission',), }
同理如果某个视图不需要权限认证就设置permission_classes=[]。
节流系统
同样让我们先从dispatch()开始走,在initial中看到有check_throttles(),其中有循环get_throttles(),进入其中发现是对throttle_classes中的类对象进行循环,而throttle_classes默认是读配置文件的。同样我们可以自己写,我们在check_throttles()会执行所有类对象中allow_request()所以需要重写,(自定义节流认证)
from django.shortcuts import render,HttpResponse from app01 import models from django.views import View from rest_framework.views import APIView from rest_framework.request import Request from rest_framework.throttling import BaseThrottle from django.http import JsonResponse import time # Create your views here. # 自定义节流类 visit_record={} class MyThrottle(BaseThrottle): def __init__(self): self.history=None def allow_request(self,request,view): ''' 自定义频率限制60s只能访问三次 :param request: :param view: :return: ''' # 访问ip ip=request.META.get('REMOTE_ADDR') ctime=time.time() # 如果这个ip本次访问时间不在记录就添加进去 if ip not in visit_record: visit_record[ip]=[ctime] return True history=visit_record[ip] self.history=history # 已经在就更新 history.insert(0,ctime) # {'ip':['22.10.10','22.10.8','22.10.6']} # 当最早的时间距离现在超过一分钟,就删除 while history and history[-1]<ctime-60: history.pop() if len(history)>3: return False else: return True def wait(self): ''' 限制时间还有多久 :return: ''' ctime=time.time() return 60-(ctime-self.history[-1]) def get_random_token(username): """ 根据用户名和时间戳生成随机token :param username: :return: """ import hashlib, time timestamp = str(time.time()) m = hashlib.md5(bytes(username, encoding="utf8")) # 加盐 m.update(bytes(timestamp, encoding="utf8")) return m.hexdigest() class LoginView(APIView): """ 校验用户名密码是否正确从而生成token的视图 """ authentication_classes = [ ] permission_classes=[] throttle_classes = [MyThrottle,] def post(self, request): self.dispatch res = {"code": 0} username = request.data.get("username") password = request.data.get("password") user = models.UserInfo.objects.filter(username=username, password=password).first() if user: # 如果用户名密码正确 token = get_random_token(username) # 找到user对象,有就更新(更新token_code),没有就创建 models.Token.objects.update_or_create(defaults={"token_code": token}, user=user) res["token"] = token else: res["code"] = 1 res["error"] = "用户名或密码错误" return JsonResponse(res) def get(self,request): return HttpResponse('login get') class OrderView(APIView): # permission_classes = [MyPermission,] def get(self,request): return HttpResponse('这里是订单页面,只有vip用户可以访问')
定义全局节流
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ('app01.utils.MyAuth.MyAuth', ), 'DEFAULT_PERMISSION_CLASSES':('app01.utils.MyPermission.MyPermission',), 'DEFAULT_THROTTLE_CLASSES':('app01.utils.MyThrottle.MyThrottle',) }
同样我们可以用框架自带的节流类
class MyThrottle2(SimpleRateThrottle): rate='3/m' def get_cache_key(self, request, view): return request.META.get('REMOTE_ADDR')
可以省略写很多逻辑,只要看看自带的类的要求就好了。如上,rate是频率,重写的函数也是要求的。其实allow_request中含有重写的函数。重写的函数返回的是像IP,用户名之类的能标示身份的key,去缓存中进行操作。在自定义的类中实现了相似的功能。注意节流一般不设置多个类,会重复,某个视图需要特殊验证, 就重写throttle_classes。
版本控制
我们CBV执行流程中,会先走dispatch()方法,当我们的视图对应的类继承的是rest_framework.views 的APIView时,在对request进行封装之后,执行initial(request, *args, **kwargs)方法,进入发现在执行认证之前有对版本
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
进入determine_version(),
scheme = self.versioning_class(),versioning_class()默认是读配置文件,scheme 也就是版本控制类的对象。
执行scheme .determine_version()。
框架自带的控制方案
这里我们以URLPathVersioning为例。
from rest_framework.versioning import URLPathVersioning
发现其中有determine_version,其中的逻辑就不仔细赘述。有几个需要配置的参数。
default_version = api_settings.DEFAULT_VERSION 默认版本
allowed_versions = api_settings.ALLOWED_VERSIONS 允许的版本
version_param = api_settings.VERSION_PARAM 对应url中的版本参数
REST_FRAMEWORK = { 'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning', #获得版本类 'DEFAULT_VERSION':'v2', #默认版本 'ALLOWED_VERSIONS':['v1','v2'], #允许版本 'VERSION_PARAM':'version', #取版本的字段在url中 }
from django.contrib import admin from django.urls import path,include,re_path from api import views urlpatterns = [ # path('admin/', admin.site.urls), re_path(r'^(?P<version>[v1|v2]+)/$',views.test.as_view()), ]
我们在视图中可以通过访问 request.version 来获取当前请求的具体版本。
注意,通常我们是不会单独给某个视图设置版本控制的,如果你确实需要给单独的视图设置版本控制,你可以在视图中设置versioning_class属性,如下:
class PublisherViewSet(ModelViewSet): ... versioning_class = URLPathVersioning
解析器
解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。 在了解解析器之前,我们要先知道Accept以及ContentType请求头。 Accept是告诉对方我能解析什么样的数据,通常也可以表示我想要什么样的数据。 ContentType是告诉对方我给你的是什么样的数据类型(服务端)。
解析器工作原理的就是拿到请求的ContentType来判断前端给我的数据类型是什么,然后我们在后端使用相应的解析器去解析数据。
ContentType对应的值(补充)
application/x-www-form-urlencoded:
窗体数据被编码为名称/值对,这是标准且默认的编码格式。当action为get时候,客户端把form数据转换成一个字串append到url后面,用?分割。当action为post时候,浏览器把form数据封装到http body中,然后发送到server。
application/x-www-form-urlencoded 传递时的数据构造:
......
username=twm&email=good@qq.com
......
multipart/form-data:
multipart表示的意思是单个消息头包含多个消息体的解决方案。multipart媒体类型对发送非文本的各媒体类型是有用的。一般多用于文件上传。
multipart/form-data只是multipart的一种。
application/json
ok,还是从dispatch()开始,进去之后在对request进行封装的initialize_request,进去发现对request封装了parsers=self.get_parsers(),进去get_parsers(),返回的是解析类的对象列表[parser() for parser in self.parser_classes],parser_classes还是读的配置文件。
我们进行请求数据通过request.data进行的,我们通过查看Request(from rest_framework.request import Request),
其中的data,看看其中的_load_data_and_files(),进去看看self._parse(),发现其中的
parser = self.negotiator.select_parser(self, self.parsers)是根据ContentType选择解析器
parsed = parser.parse(stream, media_type, self.parser_context)是对数据进行解析
最后返回return (parsed.data, parsed.files)。
总结:
全局配置解析器
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', ) }
局部视图配置解析器(配置后该视图只能解析json数据。POST请求中)
parser_classes = [JSONParser, ]
序列化
restframework中的序列化主要功能有对django数据(如queryset)进行序列化,对数据进行校验。
分别从普通的一个表,外键,多对多进行对数据序列化和校验。
from django.db import models # Create your models here. class Article(models.Model): id=models.AutoField(primary_key=True) title=models.CharField(max_length=64) create_time=models.DateField(auto_now=True) type=models.SmallIntegerField( choices=((1,'原创'),(2,'转载')), default=1 ) source=models.ForeignKey(to='Source',on_delete=models.CASCADE) tag=models.ManyToManyField(to='Tag') class Source(models.Model): id = models.AutoField(primary_key=True) name=models.CharField(max_length=32,unique=True,error_messages={'unique':'不能重复'}) class Tag(models.Model): id = models.AutoField(primary_key=True) name=models.CharField(max_length=32) class Comment(models.Model): content=models.CharField(max_length=255) article=models.ForeignKey(to='Article',on_delete=models.CASCADE)
# 表单的get和post re_path(r'my_source/$',views.MySourceView.as_view()), # 外键的get和post re_path(r'my_comment/$',views.MyCommentView.as_view()), # 多对多 re_path(r'my_article/$',views.MyArticleView.as_view()),
视图:
# 表单的get和post # 序列化类 class MySourceViewSerializer(serializers.ModelSerializer): # 对数据进行序列化 class Meta: model=Source fields='__all__' # 对数据进行自定义校验规则格式validate_字段名,value就是字段的值 def validate_name(self, value): # print('self',self) # print('value',value) if not value.endswith('出版社'): raise ValidationError('必须以 出版社 结尾') return value class MySourceView(APIView): def get(self,request,*args,**kwargs): res={'code':0} all_source=Source.objects.all() ser_obj=MySourceViewSerializer(instance=all_source,many=True) res['data']=ser_obj.data print(ser_obj.data) return HttpResponse(json.dumps(res,ensure_ascii=False)) def post(self,request,*args,**kwargs): res={'code':0} ser_obj=MySourceViewSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() return HttpResponse(json.dumps(res, ensure_ascii=False)) else: res['code']=1 res['error']=ser_obj.errors return HttpResponse(json.dumps(res, ensure_ascii=False)) # 外键的get和post # 序列化类 class MyCommentSerializer(serializers.ModelSerializer): # article_name=serializers.CharField(source='article.title') # content=serializers.CharField() class Meta: model=Comment # fields=['content',] fields='__all__' extra_kwargs = { "content": {"error_messages": {"required": "评论内容不能为空"}}, "article": {"error_messages": {"required": "文章不能为空"}} } class MyCommentView(APIView): def get(self,request,*args,**kwargs): res={'code':0} all_data=Comment.objects.all() ser_obj=MyCommentSerializer(instance=all_data,many=True) res['data']=ser_obj.data return HttpResponse(json.dumps(res, ensure_ascii=False)) def post(self, request, *args, **kwargs): res = {'code': 0} ser_obj=MyCommentSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() else: res['code']=1 res['error']=ser_obj.errors return HttpResponse(json.dumps(res, ensure_ascii=False)) # 多对多的get和post # 序列化类 class MyTagSerializer(serializers.ModelSerializer): class Meta: model=Tag fields='__all__' class MyArticleSerializer(serializers.ModelSerializer): type=serializers.CharField(source='get_type_display') tag=TagViewSerializer(many=True) class Meta: model=Article fields=['id','title','type','source','tag'] # fields='__all__' class MyArticleWriteSerializer(serializers.ModelSerializer): class Meta: model=Article fields='__all__' extra_kwargs = { "tag": { "error_messages": { "does_not_exist": '"{pk_value}"对应的tag对象不存在。' } } } class MyArticleView(APIView): def get(self, request, *args, **kwargs): res = {'code': 0} article_list=Article.objects.all() ser_obj=MyArticleSerializer(instance=article_list,many=True) res['data']=ser_obj.data # print('ret',ret) return Response(res) def post(self, request, *args, **kwargs): res = {"code": 0} ser_obj = MyArticleWriteSerializer(data=request.data) if ser_obj.is_valid(): ser_obj.save() else: res["code"] = 1 res["error"] = ser_obj.errors return Response(res)
还有超链接的序列化(返回前端某些字段是一个链接)
# 超链接的序列化 re_path(r'my_articlelinked/$',views.MyArticleLinkView.as_view()), re_path(r'my_source/(P<pk>d+)/$',views.MySourceDetialView.as_view(),name='source_detail'),
# 超链接的序列化 # 序列化类 class ArticleHyperlinkedSerializer(serializers.HyperlinkedModelSerializer): # view_name=要反向生成的url, # lookup_field=数据库中对应的显示在生成的url上的字段值, # lookup_url_kwarg=要生成url对应的(?P<pk>d+), source=serializers.HyperlinkedIdentityField( view_name='source_detail', lookup_field='source_id', lookup_url_kwarg='pk', ) class Meta: model=Article fields=["id", "type", "title", "source"] depth=1 class MyArticleLinkView(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = Article.objects.all() ser_obj=ArticleHyperlinkedSerializer(instance=article_list, many=True, context={'request': request}) res["data"] = ser_obj.data return Response(res) class MySourceDetialView(APIView): def get(self, request, *args, **kwargs): source_id=kwargs.get('pk') Source.objects.filter(id=source_id) return HttpResponse(Source.objects.filter(id=source_id).first().name)
分页
分页模式
rest framework中提供了三种分页模式:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
全局配置
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 100 }
局部配置
我们可以在视图类中进行局部设置
class PublisherViewSet(ModelViewSet):
queryset = models.Publisher.objects.all()
serializer_class = PublisherModelSerializer
pagination_class = PageNumberPagination # 注意不是列表(只能有一个分页模式)
分页实例:
路由:
# PageNumberPagination # 按页码数分页,第n页,每页显示m条数据 # 例如:http://127.0.0.1:8000/api/article/?page=2&size=1 re_path(r'article_page1/$',views.ArticlePage1View.as_view()), # LimitOffsetPagination # 分页,在n位置,向后查看m条数据 # 例如:http://127.0.0.1:8000/api/article/?offset=2&limit=2 re_path(r'article_page2/$',views.ArticlePage2View.as_view()), # CursorPagination # 加密分页,把上一页和下一页的id值记住 re_path(r'article_page3/$',views.ArticlePage3View.as_view()),
视图:
# 分页 # PageNumberPagination # 按页码数分页,第n页,每页显示m条数据 from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination from rest_framework.response import Response #分页器 class MyPageNumber(PageNumberPagination): # The default page size. # Defaults to `None`, meaning pagination is disabled. page_size = 1 #每页显示多少条 # Client can control the page using this query parameter. page_query_param ='page' #url中页码的参数 # Client can control the page size using this query parameter. # Default is 'None'. Set to eg 'page_size' to enable usage. page_size_query_param = 'size' #url中每页显示条数的参数 # Set to an integer to limit the maximum page size the client may request. # Only relevant if 'page_size_query_param' has also been set. max_page_size = 20 # 序列化类 class ArticlePage1Serializer(serializers.ModelSerializer): class Meta: model=Article fields='__all__' depth=1 class ArticlePage1View(APIView): def get(self, request, *args, **kwargs): print(request.GET) res={"code":0} article_list = Article.objects.all().order_by("id") #分页 page_obj=MyPageNumber() page_article=page_obj.paginate_queryset( queryset=article_list,request=request,view=self ) ser_obj=ArticlePage1Serializer(instance=page_article,many=True) res['data']=ser_obj.data return Response(res) # 返回带有页码链接的响应 # return page_obj.get_paginated_response(res) # LimitOffsetPagination # 分页,在n位置,向后查看m条数据 # 分页器 class MyLimitOffSet(LimitOffsetPagination): default_limit = 1 limit_query_param = 'limit' #向后查看limit条数据 offset_query_param = 'offset' #在offset位置 max_limit = 999 class ArticlePage2View(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = Article.objects.all().order_by("id") page_obj=MyLimitOffSet() page_article=page_obj.paginate_queryset( queryset=article_list, view=self, request=request ) ser_obj=ArticlePage1Serializer(instance=page_article,many=True) res['data']=ser_obj.data return Response(res) # CursorPagination # 加密分页,把上一页和下一页的id值记住 # 分页器 class MyCursor(CursorPagination): cursor_query_param = 'cursor' page_size = 1 ordering = '-id' #排序字段 class ArticlePage3View(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = Article.objects.all().order_by("id") page_obj=MyCursor() page_article=page_obj.paginate_queryset( queryset=article_list, view=self, request=request ) ser_obj=ArticlePage1Serializer(instance=page_article,many=True) res['data']=ser_obj.data return page_obj.get_paginated_response(res)
渲染器
渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。
想有restframework自带的比较好看的页面在app中注册restframework。
DRF提供的渲染器有很多,默认是
'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ),
我们也可以在视图中局部设置也可以在全局的settings.py中进行设置:
局部设置
class PublisherViewSet(ModelViewSet): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer renderer_classes = [JSONRenderer, ]
这样设置后就只能返回JSON格式的数据了,并不会像之前一样提供一个阅读友好的web页面。
全局设置
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', ), }
注意,在视图类中定义的配置项的优先级要高于全局配置中的配置项。
路由系统
举一个例子分别从手写路由,到半自动路由,到利用restframework的全自动路由
"""视图和路由 URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.0/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path,re_path,include from app01 import views from rest_framework import routers router = routers.DefaultRouter() router.register('users',views.TestView3) urlpatterns = [ # path('admin/', admin.site.urls), # re_path(r'^school/$',views.SchoolView.as_view()), # re_path(r'^schooldetail/(?P<pk>d+)/$',views.SchoolDetail.as_view()), # 路由系统 # 自定义路由 re_path(r'^test/$',views.TestView.as_view()), re_path(r'^test.(?P<format>[a-z0-9]+)/$',views.TestView.as_view()), re_path(r'^test/(?P<pk>[^/.]+)/$',views.TestView.as_view()), re_path(r'^test/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/$',views.TestView.as_view()), # 半自动路由 re_path(r'^test2/$',views.TestView2.as_view({'get': 'list','post':'create'})), # 列出所有 re_path(r'^test2/(?P<pk>d+)/$',views.TestView2.as_view({'get': 'retrieve', 'delete':'destroy', 'put':'update', 'patch':'partial_update'})), # retrieve检索出某一个 ,update更新,partial_update局部更新 # 全自动路由 re_path(r'^',include(router.urls)) ]
from django.shortcuts import render,HttpResponse from rest_framework import serializers from rest_framework.views import * from rest_framework import mixins from rest_framework import generics from app01 import models # 路由系统 # 自定义路由 class TestView(APIView): def get(self, request, *args, **kwargs): print(kwargs) print(self.renderer_classes) return Response('...') # 半自动路由 from rest_framework.viewsets import ModelViewSet # 序列化类 class UserInfoSerializer(serializers.ModelSerializer): class Meta: model=models.UserInfo fields='__all__' class TestView2(ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = UserInfoSerializer # 全自动路由 class TestView3(ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = UserInfoSerializer
视图系统
分情况而用越往下功能越多,一般用APIView(比较原生)
用GenericViewSet加上个别的像ListModelMixin实现部分功能,
用ModelView实现增删改查群补功能。具体例子参考路由例子。