django-rest-framework

web开发模式

前后端混合开发(前后端不分离);返回的是html的内容,需要写模板

前后端分离: 只专注于写后端接口,返回json,xml格式数据

什么是动态页面(查数据库的),什么是静态页面(静止的html)

页面静态化

api接口 

通过网络,规定了前后台信息交互规则的url连接,也就是前后台信息交互的媒介

postman的使用

点击安装后自动打开

解析json的网站

postman是目前最好用的,模拟发送http请求的工具

请求头中User-Agent: 客户端的类型

请求头中加其他参数

Restful规范

REST全称是Representational State Transfer,中文意思是表述表征性状态转移

Restful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中

这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源

事实上,我们可以使用任何一个框架都可以实现符合restful规范的API接口

抓包工具: fiddler, chales

10条规范

1. 数据的安全保障: url链接一般都采用https协议进行传输         注:采用https协议,可以提高数据交互过程中的安全性

2. 接口特征表现

        - 用api关键字标识urlL        

                - https://api.baidu.com

        注: 看到api字眼,就代表该请求url链接是完成前后端数据交互的

3. 多数据版本共存

4. 数据即是资源,均使用名词(可复数)

        接口一般都是完成前后台数据的交互,交互的数据我们称之为资源

        https://api.baidu.com/users

5. 资源操作由请求方式决定(method)

        操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作

6. 过滤,通过url传参的形式传递搜索条件

7. 响应状态码

        正常响应

  • 响应状态码2xx
    • 200:常规请求
    • 201:创建成功

         重定向响应

  • 响应状态码3xx
    • 301:永久重定向
    • 302:暂时重定向

        客户端异常

  • 响应状态码4xx
    • 403:请求无权限
    • 404:请求路径不存在
    • 405:请求方法不存在

        服务器异常

  • 响应状态码5xx
    • 500:服务器异常

8. 错误处理,应返回错误信息,error当做key

9. 返回结果,针对不同操作,服务端向用户返回的结果应该符合以下规范

10 需要url请求的资源需要访问资源的请求链接

drf的安装和简单使用

安装: pip install djangorestframework

使用:

        1. 在settings.py的app中注册

INSTALLED_APPS = [
    'rest_framework',
]

        2. 在models.py中写表模型

class Book(models.Model):
    nid = models.AutoField(primary_key = True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5,decimal_places=2)
    author = models.CharField(max_length=32)

        3. 新建一个序列化类(在app01下新建一个py文件,如ser.py)

from rest_framework.serializers import ModelSerializer
from app01.models import Book
class BookModelSerializer(ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

        4. 在视图中写视图类

from rest_framework.viewsets import ModelViewSet
from .models import Book
from .ser import BookModelSerializer

# Create your views here.

class BooksViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookModelSerializer

        5. 写路由

from django.contrib import admin
from django.urls import path
from app01 import views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('book',views.BooksViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
]

# 两个列表相加
urlpatterns += router.urls

CBV源码

视图类,必须继承View(读view的源码)

在类里写get,post方法就可以了,只要get请求来,就会走get方法(方法跟fbv写法完全一样)

路由:类名.as_view() 如: views.Books.as_view()   - 这个函数执行完一定是一个内存地址  -> view内存函数的地址

请求来了,路由匹配上 -> view(request) -> self.dispatch(request,*args,**kwargs)

dispatch -> 把请求方法转成小写 -> 通过反射,去对象中找,有没有get方法,有就加括号执行,并且把request传进去了

ModelviewSet 继承View(django原生view)

APIView继承了View

先读view的源码

from django.views import View

urls.py

path('books1',views.Books.as_view()), #  在这个地方应该写个函数内存地址, views.Books.as_view() 执行完了之后是个函数内存地址,as_view是一个类方法,类直接来调用,会把类自动传入

放了一个view的内存地址(View-> as_view->内层函数)

请求来 了,如果路径匹配,会执行,函数内存地址(request)

        def view(request, *args, **kwargs):
            #request是当次请求的request对象
            self = cls(**initkwargs) #实例化得到一个对象,Book对象
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        # request就是当次请求的request,self是book对象
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            #handler现在是 handler = getattr(self,'get') 你写的Book类的get方法的内存地址
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs) # 执行get(request)

API View源码分析

drf提供的,扩展了view的功能 

视图类,继承APIView(读APIView的源码)

在类里写get,post方法就可以了,只要get请求来,就会走get方法(方法跟fbv写法完全一样)

路由:类名.as_view() 如: views.Books.as_view()   - 这个函数执行完一定是一个内存地址  -> view内存函数的地址,处理了csrf,所有请求,都没有csrf校验了

请求来了,路由匹配上 -> view(request) -> self.dispatch(request,*args,**kwargs),现在这个dispatch不是view中的dispatch,而是APIView中的dispatch

from rest_framework.views import APIView

urls.py中

path('booksapiview/',views.BooksAPIView.as_view()) # 在这个地方是内存地址

APIView的as_view方法(类的绑定方法)

  @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs) # 调用父类(view)的as_view()方法
        view.cls = cls
        view.initkwargs = initkwargs
        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        # 以后所有的请求,都没有csrf认证了。只要继承了APIView,就没有csrf的认证了

        return csrf_exempt(view)

请求来了 -> 路由匹配上 ->  view(request)  -> 调用了 self.dispatch(),会执行APIView的dispatch

APIView的dispatch方法

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

drf中的Request类

from rest_framework.request import Request

只要继承了APIView,视图类中的request对象,都是新的,也就是上面那个request的对象了

老的request在新的request._request里

以后使用request对象,就像使用之前的request是一模一样的(因为重写了__getattr__方法)

    def __getattr__(self, attr):
        """
        If an attribute does not exist on this instance, then we also attempt
        to proxy it to the underlying HttpRequest object.
        """
        try:
            return getattr(self._request, attr) # 通过反射,取原生的request对象,取出属性或方法
        except AttributeError:
            return self.__getattribute__(attr)

request.data 感觉是个数据属性,其实是个方法,@property,修饰了

它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data里

get请求中传过来数据,从哪取?

    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

序列化组件

1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串

2. 反序列化,把客户端发送过来的数据,经过request以后转换成字典,序列化器可以把字典转换成模型

3. 反序列化,完成数据校验功能

序列化组件简单使用

1. 写一个序列化的类,继承serializer

2. 在类中写要序列化的字段,想序列化哪个字段,就在类中写哪个字段

3. 在视图类中使用,导入 -> 实例化得到序列化类的对象,把要序列化的对象传入

4. 序列化类的对象.data 是一个字典

5. 把字典返回,如果不使用rest_framework提供的Response,就得使用JsonResponse

序列化类的字段类型

有很多,不需要都记住 

CharField, IntegerField,DateField...

序列化字段选项

序列化组件修改数据

1. 写一个序列化的类,继承Serializer

2. 在类中写要反序列化的字段,想反序列化哪个字段,就在类中写哪个字段,字段的属性(max_length,min_length...)

3. 在视图类中使用,导入-> 实例化得到序列化类的对象,把要修改的对象传入,要修改的数据传入

        book_ser = BookSerializer(book, request.data)
        book_ser = BookSerializer(instance=book, data=request.data)

4. 数据校验

5. 如果数据校验通过,就保存

        if book_ser.is_valid():  # 返回True表示验证通过
            book_ser.save() # 报错
            # 注意不是book.save() , 而是book序列化器的save
            response_msg['data'] = book_ser.data

6. 如果不通过,逻辑自己写

7. 如果字段的校验规则不够,可以写钩子函数(局部和全局)

8. 可以使用字段的validators来校验

        写一个函数

def check_author(data):
    if data.startswith('sb'):
        raise ValidationError('作者名字不能以sb开头')
    else:
        return data

        配置

    author = serializers.CharField(validators=[check_author])

read_only 和 write_only

read_only 表明该字段仅用于序列化输出,默认False,如果设置成True,postman中可以看到该字段,修改时,不需要传该字段

write_only 表名该字段仅用于反序列化输入,默认False,如果设置成True,postman中看不到该字段,修改时,该字段必须传

查询所有

views.py

class BooksView(APIView):
    def get(self,request):
        response_msg = {'status':100,'msg':'成功'}
        books = Book.objects.all()
        book_ser = BookSerializer(books,many=True) # 序列化多条,如果序列化一条,不需要写
        response_msg['data'] = book_ser.data
        return Response(response_msg)

urls.py

    path('books/',views.BooksView.as_view()),

新增数据

views.py

class BooksView(APIView):


    def post(self,request):
        # 修改的时候才有instance, 新增没有instance, 只有data
        response_msg = {'status':100,'msg':'成功'}
        book_ser = BookSerializer(data=request.data)
        # book_ser = BookSerializer(request.data) # 这个按位置传request.data给instance,就报错了
        # 校验字段
        if book_ser.is_valid():
            book_ser.save()
            response_msg['data'] = book_ser.data
        else:
            response_msg['status'] = 102
            response_msg['msg'] = '数据校验失败'
            response_msg['data'] = book_ser.errors
        return Response(response_msg)

ser.py 序列化类重写create方法

    def create(self, validated_data):
        instance = Book.objects.create(**validated_data)
        return instance

urls.py

    path('books/',views.BooksView.as_view()),

删除一个数据

class BookView(APIView):

    def delete(self,request,pk):
        ret = Book.objects.filter(id=pk).delete()
        response_msg = {'status':100,'msg':'删除成功'}
        return Response(response_msg)

urls.py

    re_path('books/(?P<pk>\d+)',views.BookView.as_view()),

自己封装Response对象

class MyResponse():
    def __init__(self):
        self.status = 100
        self.msg= '成功'

    @property
    def get_dict(self):
        return self.__dict__
if __name__ == '__main__':

    res = MyResponse()
    res.status = 101
    res.msg = '查询失败'
    res.data = {'name':'jeffrey'}
    print(res.get_dict)

模型序列化器

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book # 对应上models.py中的模型
        # fields = '__all__'
        # fields = ('name','price') # 只序列化指定的,字段
        exclude = ('name',) # 跟fields不能都写,写谁,就表示排除谁
        # read_only_fields = ('price',) # 弃用了
        # write_only_fields = ('author',) #弃用了 使用extra_kwargs
        extra_kwargs = {
            'price':{'write_only':True},
            'author':{'write_only':True}
        }

其他使用一模一样

不需要重写create和update方法了

源码分析

序列化多条,需要传many= True

book = Book.objects.all().first()
        book_ser = BookModelSerializer(books,many=True)
        print(type(book_ser))
# <class 'rest_framework.serializers.ListSerializer'>

        book_one_ser = BookSerializer(book)
        print(type(book_one_ser))
#<class 'app01.ser.BookSerializer'>

对象的生成  - > 先调用类的 __new__方法,生成空对象

对象 = 类名(name='jeffrey') 触发类的__init__ 方法

类的__new__方法控制对象的生成

    def __new__(cls, *args, **kwargs):
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        # 没有传 many = True的时候,走下面正常的对象实例化
        return super().__new__(cls, *args, **kwargs)

Serializer高级用法

source的使用

1. 可以改字段名字

    xxx = serializers.CharField(source='title')

2. 可以.跨表

    publish = serializers.CharField(source='publish.email')

3. 可以执行方法(test是Book表模型中的方法)

    pub_date = serializers.CharField(source='test')

SerializerMethodField()的使用

1. 它需要有个配套方法,方法名叫get_字段名, 返回值就是要显示的东西

authors = serializers.SerializerMethodField() # 它需要有一个配套方法,方法名叫get_字段名,返回值就是要显示的东西

    def get_authors(self,instance):
        # print('instance:',instance,type(instance))
        # instance就是book对象
        authors = instance.authors.all()  # 取出所有作者
        print(authors)
        ll = []
        for author in authors:
            ll.append({'name':author.name,'age':author.age})
        print(ll)
        return ll

在实际开发中碰到的问题及如何解决的

       read_only_fields = ('price',)
        write_only_fields = ('author',)

以上不能用了,使用 extra_kwargs解决了

        extra_kwargs = {
            'price':{'write_only':True}
        }

请求和响应

请求对象

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )
        # 二次封装request,把原生request作为drf request对象的 _request属性
        self._request = request
        
    def __getattr__(self,attr):
        return getattr(self_request,attr)

 

 请求对象.data: 前端以三种编码方式传入的数据,都可以取出来

请求对象.query_params与django标准的request.GET相同,只是更换了更正确的名称而已

响应对象

from rest_framework.response import Response
  def __init__(self, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None):

data: 你要返回的数据,字典

status:返回的状态码,默认是200

from rest_framework import status
# status在这个路径下,把所有状态码都定义成了常量

template_name: 渲染的模板的名字(自定制模板),不需要了解

headers: 响应头,可以往响应头里放东西,就是一个字典

content_type: 响应的编码格式

浏览器响应成浏览器的格式,postman响应成json格式,通过配置实现的(默认配置)

# 不管是postman还是浏览器都返回json格式数据

drf有默认的配置文件 -> 先从项目的settigns中找,找不到,采用默认的

drf的配置信息,先从自己类中找,再去项目的settings.py中找,再去默认的找

        - 局部使用:对某个视图类有效

from rest_framework.renderers import JSONRenderer

# Create your views here.
class TestView(APIView):
    renderer_classes = [JSONRenderer, ]
    def get(self,request):
        print('request:',request)
        return Response({'name':'jeffrey'},status=status.HTTP_200_OK,headers={'token':'xxx'})

        - 全局使用: 全局的视图类,所有请求,都有效

                - 在settings.py中加入如下

视图

两个视图基类

APIView

GenericAPIView

基于APIView写接口

views.py

class BookView(APIView):
    def get(self,request):
        book_list = Book.objects.all()
        book_ser = BookSerializer(book_list,many=True)

        return Response(book_ser.data)

    def post(self,request):
        book_ser = BookSerializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status':101,'msg':'校验失败'})

class BookDetailView(APIView):
    def get(self,request,pk):
        book = Book.objects.filter(id=pk).first()
        book_ser = BookSerializer(book)
        return Response(book_ser.data)

    def put(self,request,pk):
        book = Book.objects.filter(id=pk).first()
        book_ser = BookSerializer(instance=book,data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status':101,'msg':'校验失败'})

    def delete(self,request,pk):
        Book.objects.filter(id=pk).first().delete()
        return Response({'status':100,'msg':'删除成功'})

models.py

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5,decimal_places=2)
    publish = models.CharField(max_length=32)

class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)

ser.py

from rest_framework import serializers
from app01.models import Book,Publish

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

class BookSerializer2(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name']

class PublishSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = '__all__'

class PublishSerializer2(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = ['name',]

urls.py

    path('books/',views.BookView.as_view()),
    re_path('books/(?P<pk>\d+)',views.BookDetailView.as_view()),

基于GenericAPIView写的接口

views.py

class BooksGenericView(GenericAPIView):
    print('ccc')
    # queryset要传queryset对象,查询了所有的图书
    # serializer_class : 使用哪个序列化类来序列化这堆数据
    queryset = Book.objects.all()
    serializer_class =  BookSerializer2
    # 当视图中使用多个序列化器类时,可以使用该方法来区分
    # def get_serializer_class(self):
    #     if self.request.method  == 'GET':
    #         return BookSerializer2
    #     else:
    #         return  BookSerializer

    def get(self,request):
        print('get:',self.get_serializer())
        book_list = self.get_queryset()
        book_ser = self.get_serializer(book_list,many=True)
        return Response(book_ser.data)

    def post(self,request):
        print('post:',self.get_serializer())
        book_ser = self.get_serializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status':101,'msg':'校验失败'})

class BookDetailGenericView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self,request,pk):
        book = self.get_object()
        book_ser = self.get_serializer(book)
        return Response(book_ser.data)

    def put(self,request,pk):
        book = self.get_object()
        book_ser = self.get_serializer(instance=book,data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status':101,'msg':'校验失败'})

基于GenericAPIView和5个视图扩展类写的接口

views.py

from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin

# 基于视图扩展类来写视图接口

class Books3View(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get(self,request):
        return self.list(request)

    def post(self,request):
        return self.create(request)

class Book3DetailView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset =  Book.objects.all()
    serializer_class = BookSerializer
    def get(self,request,pk):
        return self.retrieve(request,pk)

    def put(self,request,pk):
        return self.update(request,pk)

    def delete(self,request,pk):
        return self.destroy(request,pk)

 使用ModelViewSet写5个接口

views.py

from rest_framework.viewsets import ModelViewSet

class Book5View(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

urls.py

    path('books5',views.Book5View.as_view(actions={'get':'list','post':'create'})),#当路径匹配,又个get请求,会执行Book5View的get方法
    re_path('books5/(?P<pk>\d+)',views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),

源码分析ViewSetMixin

            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

继承ViewSetMixin的视图类

 views.py

class Book6View(ViewSetMixin,APIView): # 一定要放在APIView前面

    def get_all_book(self,request):
        print('xxxx')
        book_list = Book.objects.all()
        book_ser = BookSerializer(book_list,many=True)
        return Response(book_ser.data)

urls.py

    path('books6/',views.Book6View.as_view(actions={'get':'get_all_book'}))

一切皆对象

def foo(a,b):
    return a+b


foo.name = 'lqz' # 由于一切皆对象,函数也是个对象,对象放值

print(foo(2,3))
print(foo.name)
print(foo.__name__)

局部禁用csrf

 在视图函数上加装饰器  @csrf_exempt

csrf_exempt(view) 这么写和在视图函数上加装饰器是一模一样的

urs.py中看到这种写法

path('test/',csrf_exempt(views.test))

路由

在urls.py中配置

path('book4/',views.Book4View.as_view())
re_path('book4/(?P<pk>\d+)',views.Book4DetailView.as_view()

一旦视图类,继承了ViewSetMixin,路由

path('books5',views.Book5View.as_view(actions={'get':'list','post':'create'})),#当路径匹配,又个get请求,会执行Book5View的get方法
    re_path('books5/(?P<pk>\d+)',views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),

继承自ModelViewSet的路由写法(自动生成路由)

urls.py

# 第一步: 导入routers模块
from rest_framework import routers
# 第二步: 有两个类, 实例化得到对象
# routers.DefaultRouter 生成的路由更多
# routers.SimpleRouter

# router = routers.SimpleRouter()
router = routers.DefaultRouter()
# 第三步: 注册
# router.register('前缀','继承自ModelViewSet的视图类')
router.register('books',views.BookViewSet) # books不要加斜杠了
# 第四步
# router.urls # 自动生成路由,加入到原路由
# print(router.urls)
# '''
# [<URLPattern '^books/$' [name='book-list']>, <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='book-detail']>]
#
# '''
urlpatterns = [
    path('admin/', admin.site.urls),
    # path('books/',views.BookViewSet.as_view(actions = {'get':'list','post':'create'})),
    # re_path('books/(?P<pk>\d+)',views.BookViewSet.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'}))
]
urlpatterns += router.urls

views.py

from rest_framework.viewsets import ModelViewSet
from app01.models import Book
from app01.ser import BookSerializer


# Create your views here.
class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

action的使用

action干什么用?为了给继承自modelviewset的视图类中定义的函数也添加路由

使用

class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # method第一个参数,传一个列表,列表中放请求方式
    # '^books/get_2/$ [name='book-get-2']' 当向这个地址发送get请求,会执行下面的函数
    # detail:布尔类型,如果是True:'^books/(?P<pk>[^/.]+)/get_2/$ [name='book-get-2']'
    @action(methods=['GET','POST'],detail=True)
    def get_2(self,request,pk):
        print(pk)
        book = self.get_queryset()[:2] # 从0开始截取一条
        ser = self.get_serializer(book,many=True)
        return Response(ser.data)

装饰器,放在被装饰的函数上麦那,两个参数 method: 请求方式,detail:是否带pk

认证的写法

认证的实现

1. 写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,认证通过返回两个值,一个值给了Request类的对象的user。认证失败,抛异常APIException或者AuthenticationFailed

2. 全局使用,局部使用

认证的源码实现

1. APIView ---> dispatch方法 ---> self.initial(request, *args, **kwargs)->有认证,权限,频率

2. 只读认证源码 self.perform_authentication(request)

3. self.perform_authentication(request)就一句话: request.user,需要去drf的Request对象中找user属性(方法)

4. Request中的user方法,刚开始来,没有_user,走self._authenticate()

5. 核心,就是Request类的_authenticate()方法

    def _authenticate(self):
    # 遍历拿到一个个认证器,进行认证
    # self.authenticators配置的一堆认证类产生的认证类对象组成的list
    # self.authenticators是你在视图类中配置的一个个的认证类:authentication_classes = [认证类1,认证类2],对象的列表
        for authenticator in self.authenticators:
            try:
                # 认证器(对象)调用认证方法的authenticate(认证类对象self,request请求对象)
                # 返回值:登录的用户与认证的信息组成的tuple
                # 该方法被try包裹,代表方法会抛异常,抛异常就代表认证失败
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
            # 返回值的处理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # 如果有返回值,就将登录用户与登录认证分别存到request.user,request.auth
                self.user, self.auth = user_auth_tuple
                return
    # 如果返回user_auth_tuple为空,代表认证通过,但是没有登录用户与登录认证信息,代表游客
        self._not_authenticated()

认证组件的使用

 写一个认证类

app_auth.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01.models import UserToken
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 认证逻辑,如果认证通过,返回两个值
        # 如果认证失败,抛出AuthenticationFailed
        token = request.GET.get('token')
        print(request.query_params)
        # print(token)
        if token:
            user_token = UserToken.objects.filter(token=token).first()
            # 认证通过
            if user_token:
                return user_token.user,token
                # return '333',token
            else:
                raise AuthenticationFailed('认证失败')
        else:
            raise AuthenticationFailed('请求地址中需要携带token')

可以有多个认证,从左到右依次执行

全局使用,在settings.py中配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.app_auth.MyAuthentication'
    ]
}

局部使用,在视图类上写

authentication_classes = [MyAuthentication]

局部禁用

authentication_classes = [MyAuthentication]

权限 

权限源码分析

# APIView -> dispatch -> initial --> self.check_permissions(request) APIView的    
def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions(): # 权限类的对象,放到列表中
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request,
                    message=getattr(permission, 'message', None),
                    code=getattr(permission, 'code', None)
                )

权限组件的使用

写一个类,继承BasePermission,重写has_permmission,如果权限公国,就返回True,不通过就返回False

class UserPermission(BasePermission):
    def has_permission(self, request, view):
        # 不是超级用户,不能访问
        # 由于认证已经过了,request内就有user对象了,当前登录用户
        user = request.user #当前登录用户
        # 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文名
        print(user.get_user_type_display())
        if user.user_type == 1:
            return True
        else:
            return False

局部使用

class TestView(APIView):
    authentication_classes =  [app_auth.MyAuthentication]
    permission_classes = [app_auth.UserPermission]
    def get(self,request):
        return Response('这是测试数据')

全局使用 settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.app_auth.MyAuthentication'
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.app_auth.UserPermission',
    ],
}

局部禁用

        放一个空列表

内置权限

 内置权限的使用: IsAdminUser

        1.创建超级管理员

        2. 写一个测试视图类

        

from rest_framework.permissions import IsAdminUser
from rest_framework.authentication import SessionAuthentication,BasicAuthentication
class TestView3(APIView):
    authentication_classes =  [SessionAuthentication]
    permission_classes = [IsAdminUser]
    def get(self,request):
        return Response('这是测试数据33333,超级管理员可以看')

        3.超级用户登录到admin,再访问test3就有权限       

频率组件

内置的频率限制限制未登录用户

 全局使用 限制未登录用户1分钟访问5次

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.app_auth.MyAuthentication'
    ],
    # 'DEFAULT_PERMISSION_CLASSES': [
    #     'app01.app_auth.UserPermission',
    # ],
    'DEFAULT_THROTTLE_CLASSES':['rest_framework.throttling.AnonRateThrottle'],
    'DEFAULT_THROTTLE_RATES':{
        'anon':'3/m',
    }
}

局部使用 views.py

from rest_framework.throttling import AnonRateThrottle
class TestView5(APIView):
    authentication_classes =  []
    permission_classes = []
    throttle_classes = [AnonRateThrottle]
    def get(self,request):
        return Response('我是未登录用户')

内置频率限制之限制登录用户的访问频次

需求:未登录用户1分钟访问5次,登录用户1分钟方为10次

再settings.py中配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.app_auth.MyAuthentication',
    ],
    # 'DEFAULT_PERMISSION_CLASSES': [
    #     'app01.app_auth.UserPermission',
    # ],
    'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.AnonRateThrottle',
                                 'rest_framework.throttling.UserRateThrottle'
                                 ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '5/m',
        'user': '20/m',
    }
}

局部配置:

        在视图中配一个就行

过滤 Filtering

安装  pip install django-filter

注册: 在app中注册

全局配,或者局部配置 'DEFAULT_FILTER_BACKENDS':('django_filters.rest_framework.DjangoFilterBackend',),

视图类 (配置可以按照哪个字段来过滤)

class BookView(ListAPIView):
    authentication_classes = []
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filterset_fields = ('name',)

排序

局部使用和全局使用

from rest_framework.generics import ListAPIView
from rest_framework.filters import OrderingFilter
from app01.models import Book
from app01.ser import BookSerializer
class Book2View(ListAPIView):
    authentication_classes = []
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter]
    filterset_fields = ('name','price','publish')
    orderingset_fields= ('id','price')

使用:

http://127.0.0.1:8000/books2/?ordering=-id

http://127.0.0.1:8000/books2/?ordering=price

异常处理

统一接口返回

自定义异常方法,替换掉全局

写一个方法

自定义异常处理方法 

from rest_framework.response import Response
from rest_framework import status
def my_exception_handler(exc, context):
    from rest_framework.views import exception_handler

    # print(exc)
    response = exception_handler(exc, context)
    # print(response.data)
    # 两种情况,一个是None, drf没有处理
    # response对象,django处理了,但是处理的不符合咱们的要求
    # if isinstance(exc,)
    if not response:
        if isinstance(exc,ZeroDivisionError):
            return Response(data={'status':777,'msg':'除以0的错误'+str(exc)},status=status.HTTP_400_BAD_REQUEST)
        return Response(data={'status':999,'msg':str(exc)},status=status.HTTP_400_BAD_REQUEST)
    else:
        return Response(data={'status':888,'msg':response.data.get('detail')},status=status.HTTP_400_BAD_REQUEST)

全局配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'app01.app_auth.MyAuthentication',
    ],
    # 'DEFAULT_PERMISSION_CLASSES': [
    #     'app01.app_auth.UserPermission',
    # ],
    'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.AnonRateThrottle',
                                 'rest_framework.throttling.UserRateThrottle'
                                 ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '5/m',
        'user': '20/m',
    },
    'DEFAULT_FILTER_BACKENDS':('django_filters.rest_framework.DjangoFilterBackend',),
    'EXCEPTION_HANDLER': 'app01.app_auth.my_exception_handler',

}

封装Response对象

以后都用自己封装的 

class APIResponse(Response):
    def __init__(self, code=100, msg='成功', data=None, status=None, headers=None,*args,**kwargs):
        dic = {'code':code,'msg':msg}
        if data:
            dic = {'code':code,'msg':msg,'data':data}
        dic.update(kwargs)
        super().__init__(data=dic, status=status, headers=headers)

使用 views.py

from app01.app_auth import APIResponse
class TestView7(APIView):
    authentication_classes =  []
    permission_classes = []
    # throttle_classes = [AnonRateThrottle]
    def get(self,request):
        return APIResponse(data= {'name':'jeffrey'},token='ccc',aa = 'bbb')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值