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实现增删改查群补功能。具体例子参考路由例子。

 

posted @ 2019-01-28 17:59  平常心u  阅读(209)  评论(0编辑  收藏  举报