• Django Rest framework


    首先我们需要先了解一下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)
    models.py
    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')
    views.py

    如果我们自己写了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用户可以访问')
    view.py

    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用户可以访问')
    views.py

    定义全局节流

    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)
    models.py
        # 表单的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)
    views.py

    还有超链接的序列化(返回前端某些字段是一个链接)

    # 超链接的序列化
    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)
    views.py

     

    渲染器

    渲染器同解析器相反,它定义了框架按照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))
    
    ]
    urls.py
    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
    views.py

    视图系统

    分情况而用越往下功能越多,一般用APIView(比较原生)

    用GenericViewSet加上个别的像ListModelMixin实现部分功能,

    用ModelView实现增删改查群补功能。具体例子参考路由例子。

    写出漂亮的博客就是为了以后看着更方便的。
  • 相关阅读:
    8.使用背景图,制作雪碧图效果
    7.使用定位制作轮播图样式
    6.使用定位,制作弹出框界面
    App 抓包代理设置
    Cypress 自动化环境搭建
    Android APP 性能测试之 GT 工具
    接口测试流程
    接口结构内容
    接口定义
    网络七层协议
  • 原文地址:https://www.cnblogs.com/zhaowei5/p/10331242.html
Copyright © 2020-2023  润新知