Django Rest Framework 框架

学习地址:Django从入门到放弃 - 刘清政 - 博客园

第一阶段:

一、什么是Rest Framework?

    REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
    REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
    所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
    对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)

二、Rest Framework API设计规范-10条

1、API与用户的通信协议,总是使用HTTPs协议。

2、域名 
  https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
  https://example.org/api/                        API很简单

3、版本
  URL,如:https://api.example.com/v1/
  请求头                                                  跨域时,引发发送多次请求

4、路径,视网络上任何东西都是资源,均使用名词表示(可复数)
  https://api.example.com/v1/zoos
  https://api.example.com/v1/animals
  https://api.example.com/v1/employees

5、method
  GET      :从服务器获取资源(一项或多项)
  POST    :在服务器新增一个资源 (提交一个数据)
  PUT      :在服务器更新资源(客户端提供改变后的完整资源)
  PATCH  :在服务器更新资源(客户端提供改变的属性)
  DELETE :从服务器删除资源

6、过滤,通过在url上传参的形式传递搜索条件,通过request.GET.get('key')的方式获取值
  https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
  https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
  https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
  https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
  https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件

7、状态码
  200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
  201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  204 NO CONTENT - [DELETE]:用户删除数据成功。
  400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
  401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
  404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
  500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
  更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

8、错误处理,应返回错误信息,error当做key。
{
    error: "Invalid API key"
}

9、返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
  GET /collection:返回所有资源对象,一个列表格式
  GET /collection/resource:返回单个资源对象
  POST /collection:返回新生成的资源对象
  PUT /collection/resource:返回完整的资源对象
  PATCH /collection/resource:返回完整的资源对象
  DELETE /collection/resource:返回一个空文档

10、Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{"link": {
  "rel":   "collection https://www.example.com/zoos",
  "href":  "https://api.example.com/zoos",
  "title": "List of zoos",
  "type":  "application/vnd.yourformat+json"
}}

第二阶段:

三、安装django rest framework

   方式一:pip3 install djangorestframework
   方式二:pycharm图形化界面安装
   方式三:pycharm命令行下安装(装在当前工程所用的解释器下)

   使用:基于django rest framework 的接口,均使用CBV来写方法。

四、django rest framework 的APIView分析,这里参考前面的CBV源码分析

      as_view 来自APIView类里的as_view方法,同时APIView类的dispatch方法("def dispatch(self, request, *args, **kwargs):")将request对象进行了一层封装"request = self.initialize_request(request, *args, **kwargs)"。而真正封装request对象方法的是"initialize_request"方法,如下:

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,   # 原生的request
            parsers=self.get_parsers(),  # 解析器
            authenticators=self.get_authenticators(),   # 认证器
            negotiator=self.get_content_negotiator(),   # 分页器
            parser_context=parser_context   # 响应器
        )

# self.initial(request, *args, **kwargs) 包含的
        self.perform_authentication(request)  # 认证组件
        self.check_permissions(request)       # 权限组件
        self.check_throttles(request)         # 频率组件

 经过封装后的request,会重新赋值给 request = self.initialize_request(request, *args, **kwargs),那么最终到达视图函数的就是封装后的request。

  4.1、如何拿到原生的request

  Request类里面将request赋值给了self._request,那么通过 self._request 就能拿到原生的request。对应到代码里面就是:request._request

  4.2、封装后的request如何通过request.method拿到对应的值

  Request类里面有一个"__getattr__"方法,原本需要通过request._request.method才能拿到值,现在通过request.method就可以拿到了。本质还是通过request._request.method(原生request)的方式。

    def __getattr__(self, attr):
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

  4.3、封装后的request可以直接通过request.data的方式直接拿到数据,而不向django只能接收from-data和urlencoded,Json的格式还需要转移才能接收。也就是说拿request的数据就直接通过request.data即可

五、解析器

  5.1、解析器的作用

  根据请求头 content-type 选择对应的解析器对请求体内容进行处理。有application/json,x-www-form-urlencoded,form-data等格式。

  5.2、解析器全局配置和局部配置

  如果解析器只解析json格式,那么其他格式请求过来就直接报错415。

  局部配置写到类里面,全局配置的话,写到setting.py文件里面。全局和局部可以同时使用,一般全局用json,局部可以自定义使用多种。

局部配置
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser  # 导入模块

class Index(APIView):
    parser_classes = [JSONParser,]  # 只解析json格式,局部配置

    def get(self,request):
        return HttpResponse("get")

    def post(self,request):
        return HttpResponse("post")

全局配置,写到setting.py文件里面
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'     # 只解析json格式的请求
        'rest_framework.parsers.FormParser'     # 只解析x-www-form-urlencoded格式的请求
        'rest_framework.parsers.MultiPartParser'
    ]
}

  非json格式的请求报错如下:

   5.3、解析器的默认配置

   在 "rest_framework/setting.py"文件里面,这个配置是rest_framework框架的默认配置(不是django的)。默认就这三个:

'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',   # 只解析json格式
        'rest_framework.parsers.FormParser',    # 只解析x-www-form-urlencoded格式
        'rest_framework.parsers.MultiPartParser'
    ]

   5.4、解析器的加载顺序

  1.视图函数内,2.django的setting.py文件内,3,."rest_framework/setting.py"文件内(默认配置)

六、认证组件

待研究

七、权限组件

待研究

第三阶段:

八、序列化组件

8.1、原生方式写接口以及返回数据(第一种方式)

from rest_framework.views import APIView
from app01.models import Book
from django.http import JsonResponse

class Books(APIView):
    def get(self,request):
        response={
            "status":200,
            "message":None,
        }
        books=Book.objects.all()
        books_list=[{"name":n.name,"price":n.price,"publish_data":n.publish_date} for n in books]
        response["message"]="success"
        response["data"]=books_list
        return JsonResponse(response,safe=False)
# 返回数据
# {
#     "status": 200,
#     "message": "success",
#     "data": [
#         {
#             "name": "xiyouji",
#             "price": "199.00"
#         },
#         {
#             "name": "sanguoyanyi",
#             "price": "299.00"
#         }
#     ]
# }

8.2、drf方式写接口以及返回数据(第二种方式)

1.单独定义某个类的序列化,这里以Book类为例,那么这个序列化只能解析Book类

# app01/my_serialzers.py
from rest_framework import serializers

class BookSerial(serializers.Serializer):
    nid=serializers.IntegerField()

    # name这个名称就是数据库内的名称name,这样写不安全。
    # 如果要指定别名:把name改为name3时,需要指定source='name'对应到name3这个别名。
    name3=serializers.CharField(source='name')

    price=serializers.CharField()
    publish_date=serializers.CharField()
    publish = serializers.CharField()

    # 调用类下面的某一个属性的值,通过source='publish.email'来传值。
    # source的功能就是调用类下面某一个属性或者方法(前提是这个属性或者方法必须存在)。
    # 如果类(A)下面还是一个类(B)时,调用类(B)下面的某个方法只需要source='类(B).方法名'
    publish_email = serializers.CharField(source='publish.email')

    # source既可以传属性,也可以传方法,source="属性/方法"时,拿到的结果就是"属性/方法"的返回值

class AuthorSerial(serializers.Serializer):
    nid=serializers.IntegerField()
    name=serializers.CharField()
    age=serializers.IntegerField()
    # author_detail = serializers.CharField() # 这里拿到的数据是一个对象:"author_detail": "AuthorDatail object (1)"
    author_telephone = serializers.CharField(source='author_detail.telephone')
    author_birthday = serializers.CharField(source='author_detail.birthday')

class AuthorDatailSerial(serializers.Serializer):
    nid = serializers.IntegerField()
    telephone = serializers.IntegerField()
    birthday = serializers.DateField()
    addr = serializers.CharField()

class PublishSerial(serializers.Serializer):
    nid = serializers.IntegerField()
    name = serializers.CharField()
    city = serializers.CharField()
    email = serializers.EmailField()

2、单独定义数据响应类,主要是定义数据返回的格式

# app01/my_response.py
class MyResponse():
    def __init__(self):
        self.status=200
        self.msg=None

    @property
    def get_dic(self):
        return self.__dict__
        # 将类里面的数据转换成为字典 {"status":200,"msg":None}

3、视图函数内使用

# 使用drf序列化组件的方式(第二种方式)
from rest_framework.views import APIView
from django.http import JsonResponse

from app01.models import Book
from app01 import my_serialzers
from app01 import my_response

class Books(APIView):
    def get(self,request):

        # 实例化response对象并赋值
        response=my_response.MyResponse()
        response.msg="success"

        # 数据库获取数据
        books=Book.objects.all()
        res=my_serialzers.BookSerial(books,many=True)  # many=True允许接收多条数据

        # 将数据库拿到的值,赋给response对象
        response.data=res.data

        # response.get_dic:把response对象的数据转换为字典格式
        return JsonResponse(response.get_dic,safe=False)
# 返回的数据
# {
#     "status": 200,
#     "msg": "success",
#     "data": [
#         {
#             "nid": 1,
#             "name3": "xiyouji",
#             "price": "199.00",
#             "publish_date": "1999-09-09",
#             "publish": "北京出版社",
#             "publish_email": "111@111"
#         },
#         {
#             "nid": 2,
#             "name3": "sanguoyanyi",
#             "price": "299.00",
#             "publish_date": "1993-04-05",
#             "publish": "上海出版社",
#             "publish_email": "222@222"
#         }
#     ]
# }

4、models.py文件内的配置

# 在models.py文件中
from django.db import models

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)
    publish_date = models.DateField(auto_now_add=True)

    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
    authors=models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDatail',to_field='nid',unique=True,on_delete=models.CASCADE)

class AuthorDatail(models.Model):
    nid = models.AutoField(primary_key=True)
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    addr = models.CharField(max_length=64)

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):     # 调用Publish类时,打印Publish.name
        return self.name

5、总结:序列化组件之Serializer
  1、导入:from rest_framework import serializers
  2、写一个类(myserial.py文件下,名字任意),继承类serializers.Serializer
    BookSer(serializers.Serializer)
  3、如果不指定"source",字段名,那么BookSer下的属性名,必须跟数据库列名一致。
  4、"source"既可以指定数据属性(source='name')name为数据属性,又可以指定方法属性(source='test')test为类下的一个函数。
    如果类(A)下面还是一个类(B)时,调用类(B)下面的某个方法只需要source='类(B).方法名'
  5、序列化组件的使用:
    在views.py文件下,查出所有要使用的数据:books=models.Book.objects.all()
    在views.py文件下,序列化数据:res=myserial.BookSer(books,many=True)多条(queryset对象)  # many=True表示序列化多条数据
    在views.py文件下,序列化数据:res=myserial.BookSer(books,many=False)一条(一个book对象)  # books=models.Book.objects.first()


8.3、序列化方法SerializerMethodField  数据多对多查询时使用,具体如下:

方式一:通过SerializerMethodField的自定义方法

from rest_framework import serializers

class BookSerial(serializers.Serializer):
    nid=serializers.IntegerField()

    # name这个名称就是数据库内的名称name,这样写不安全。
    # 如果要指定别名:把name改为name3时,需要指定source='name'对应到name3这个别名。
    name3=serializers.CharField(source='name')
    price=serializers.CharField()
    publish_date=serializers.CharField()
    publish = serializers.CharField()

    # 调用类下面的某一个属性的值,通过source='publish.email'来传值。
    # source的功能就是调用类下面某一个属性或者方法(前提是这个属性或者方法必须存在)。
    # 如果类(A)下面还是一个类(B)时,调用类(B)下面的某个方法只需要source='类(B).方法名'
    publish_email = serializers.CharField(source='publish.email')
    # authors=serializers.CharField(source='authors.all')

    # 方式一:拿到所有的authors的值
    # SerializerMethodField可以自定义方法:方法的返回值就是authors的值
    auth = serializers.SerializerMethodField()
    def get_auth(self,obj):
        # get_auth必须为(get_(auth))
        # obj表示当前BookSerial对象。

        authors=obj.authors.all()  # 这里拿到的是Book.authors的值,类似source='publish.email'拿到的值一样
        ll = [{'name':n.name,'age':n.age} for n in authors]
        return ll   # 这里的返回值,就是请求拿到的值

# 通过视图函数拿到的返回值
{
    "status": 200,
    "msg": "success",
    "data": [
        {
            "nid": 1,
            "name3": "xiyouji",
            "price": "199.00",
            "publish_date": "1999-09-09",
            "publish": "北京出版社",
            "publish_email": "111@111",
      看这里  "auth": [
                {
                    "name": "sudada",
                    "age": 28
                }
            ]
        },
        {
            "nid": 2,
            "name3": "sanguoyanyi",
            "price": "299.00",
            "publish_date": "1993-04-05",
            "publish": "上海出版社",
            "publish_email": "222@222",
      看这里  "auth": [
                {
                    "name": "szq",
                    "age": 18
                }
            ]
        }
    ]
}

方式二:通过实例化自定义的AuthorSerial类,拿到Author数据表内的所有值

from rest_framework import serializers

class BookSerial(serializers.Serializer):
    nid=serializers.IntegerField()

    # name这个名称就是数据库内的名称name,这样写不安全。
    # 如果要指定别名:把name改为name3时,需要指定source='name'对应到name3这个别名。
    name3=serializers.CharField(source='name')
    price=serializers.CharField()
    publish_date=serializers.CharField()
    publish = serializers.CharField()

    # 调用类下面的某一个属性的值,通过source='publish.email'来传值。
    # source的功能就是调用类下面某一个属性或者方法(前提是这个属性或者方法必须存在)。
    # 如果类(A)下面还是一个类(B)时,调用类(B)下面的某个方法只需要source='类(B).方法名'
    publish_email = serializers.CharField(source='publish.email')
    # authors=serializers.CharField(source='authors.all')

    # 方式二:通过实例化自定义的AuthorSerial类,拿到Author数据表内的所有值
    authors = serializers.SerializerMethodField()
    def get_authors(self,obj):
        authors=obj.authors.all()
        ser=AuthorSerial(authors,many=True)
        return ser.data

class AuthorSerial(serializers.Serializer):
    nid=serializers.IntegerField()
    name=serializers.CharField()
    age=serializers.IntegerField()
    author_telephone = serializers.CharField(source='author_detail.telephone')
    author_birthday = serializers.CharField(source='author_detail.birthday')

# 通过视图函数拿到的返回值
{
    "status": 200,
    "msg": "success",
    "data": [
        {
            "nid": 1,
            "name3": "xiyouji",
            "price": "199.00",
            "publish_date": "1999-09-09",
            "publish": "北京出版社",
            "publish_email": "111@111",
            "authors": [    看这里,这里拿到的值就是AuthorSerial的属性。
                {
                    "nid": 2,
                    "name": "sudada",
                    "age": 28,
                    "author_telephone": "222",
                    "author_birthday": "1999-09-09"
                }
            ]
        },
        {
            "nid": 2,
            "name3": "sanguoyanyi",
            "price": "299.00",
            "publish_date": "1993-04-05",
            "publish": "上海出版社",
            "publish_email": "222@222",
            "authors": [    看这里,这里拿到的值就是AuthorSerial的属性。
                {
                    "nid": 1,
                    "name": "szq",
                    "age": 18,
                    "author_telephone": "111",
                    "author_birthday": "2000-06-11"
                }
            ]
        }
    ]
}

8.3.1、SerializerMethodField的用法总结:

   1.使用SerializerMethodField时,可以自定义方法A,方法A的返回值就是authors的值,方法如下:
   2.必须配套一个方法:方法的名字"get_authors"必须要等于get_(authors)
   3.def get_authors(self,obj):     # obj表示当前BookSer的所有对象。
       authors=obj.authors.all()
       ll = [{'name':author.name,'age':author.age} for author in authors]
       return ll
   4.在方法内可以继续用序列化组件。

8.4、序列化组件ModelSerializer,用法一:查询数据

# 序列化组件:ModelSerializer
from app01.models import Book
class BookSer(serializers.ModelSerializer):
    class Meta:
        # 指定要序列化的表模型,这里以app01.models.Book为例
        model=Book
        # 把所有字段都序列化
        fields="__all__"

    publish = serializers.CharField()
    authors = serializers.SerializerMethodField()
    def get_authors(self,obj):
        authors=obj.authors.all()
        ser=AuthorSerial(authors,many=True)
        return ser.data

"""
{
    "status": 200,
    "msg": "success",
    "data": [
        {
            "nid": 1,
            "publish": "北京出版社",
            "authors": [
                {
                    "nid": 2,
                    "name": "sudada",
                    "age": 28,
                    "author_telephone": "222",
                    "author_birthday": "1999-09-09"
                }
            ],
            "name": "xiyouji",
            "price": "199.00",
            "publish_date": "1999-09-09"
        },
        {
            "nid": 2,
            "publish": "上海出版社",
            "authors": [
                {
                    "nid": 1,
                    "name": "szq",
                    "age": 18,
                    "author_telephone": "111",
                    "author_birthday": "2000-06-11"
                }
            ],
            "name": "sanguoyanyi",
            "price": "299.00",
            "publish_date": "1993-04-05"
        }
    ]
}
"""

序列化组件之ModelSerializer 的用法总结:

1.用法同Serializer一致,但也有不同点,如下:
class BookSer(serializers.ModelSerializer):
    class Meta:
    
    model=models.Book                             # 指定要序列化的表模型为Book
    fields = ['name','authors','publish']         # 表示序列化的表字段,'__all__'表示所有字段,['name','authors','publish']表示自定义字段
    exclude = ['nid']                             # 自定义[]列表内排除某些字段
    # 注意:fields和exclude不能同时使用!
    depth=2                                       # depth指定深度查询

8.5、序列化组件的ModelSerializer,用法二:反序列化

 8.5.1、数据保存功能(通过post请求传入数据,然后保存)

POST传入数据:

 代码如下:

# 在views.py文件中
from rest_framework.views import APIView
from rest_framework import serializers
from app01 import myserial,models

class Book(APIView):
    def post(self,request):
        print(request.data)
        res=myserial.BookSer(data=request.data)      # 通过request.data拿到数据,然后执行反序列化
        if res.is_valid():
            res.save()                               # 执行反序列方法,保存数据到DB里面
        else:
            print(res.errors)                        # 打印错误日志
            # {'authors': [ErrorDetail(string='This field is required.', code='required')]}
        return HttpResponse('post')

# 在myserial.py文件中
from app01 import models
from rest_framework import serializers

# ModelSerializer用法
class BookSer(serializers.ModelSerializer):
    class Meta:
        # model:指定要序列化的表模型是Book
        model=models.Book
        # exclude:自定义[]列表内排除某些字段
        exclude = ['authors']

  8.5.2、请求数据校验,局部校验与全局校验

# urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # as_view()拿到的是CBTtest类里面某个函数的名称[详细信息查看View类里面的as_view函数,最终返回一个get|post函数的内存地址]以及一个request对象。
    path('books/', views.Book.as_view()),

]

# 在views.py文件中
from rest_framework.views import APIView
from rest_framework import serializers
from app01 import models,myserial

class Book(APIView):
    def post(self,request):
        print(request.data)
        res=myserial.BookSer(data=request.data)      # 通过request.data拿到数据,然后执行反序列化
        if res.is_valid():
            res.save()                               # 执行反序列方法,保存数据到DB里面
            return HttpResponse("post_ok")
        else:
            print(res.errors)                        # 打印错误日志
            # {'authors': [ErrorDetail(string='This field is required.', code='required')]}
            return JsonResponse(res.errors,safe=False)

# 在myserial.py文件中
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

# ModelSerializer用法
class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        exclude = ['authors']

    # 局部校验
    name=serializers.CharField()
    def validate_name(self,value):      # value就是前端传过来的JSON值(只匹配数据表内"name"这个关键字的值)
        if value.startswith('sb'):      # 判断数据表内"name"这个关键字的值是否以"sb"开头
            raise ValidationError('name字段不能以sb开头')   # {'name': [ErrorDetail(string='不能以sb开头', code='invalid')]}
        else:
            return value

    # 全局校验
    def validate(self,value):
        # print(value)    # value就是前端传过来的JSON值:{'name': '西游记', 'price': '199', 'publish_date': '2016-12-19', 'publish': 1}
        name=value.get('name')
        if name.startswith('sb'):
            raise ValidationError('name字段不能以sb开头')
        else:
            return value

# 输出如下:
{'non_field_errors': [ErrorDetail(string='name字段不能以sb开头', code='invalid')]}

 8.5.3、序列化组件ModelSerializer的数据校验和反序列化总结

1.只有ModelSerializer有"res.save()"反序列化保存数据的方法。
2.用法
    def post(self,request):
    res=myserial.BookSer(data=request.data)      # 生成一个序列化对象,通过request.data拿到数据,然后执行反序列化
    if res.is_valid():                           # 判断字段是否校验通过。
        res.save()                               # 字段校验通过,则执行反序列方法,保存数据到DB里面
    else:
        print(res.errors)                        # 否则打印错误日志
        # {'authors': [ErrorDetail(string='This field is required.', code='required')]}

    return HttpResponse('post')

九、DRF视图类的用法

9.1、视图类的基本写法(增、删、改、查)

  方式一:最原始版本,增删改查,需要手写POST,GET等

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

    # as_view()拿到的是CBTtest类里面某个函数的名称[详细信息查看View类里面的as_view函数,最终返回一个get|post函数的内存地址]以及一个request对象。
    re_path('^books/$', views.Book.as_view()),
    re_path('^books/(\d+)/', views.BookDetail.as_view()),
    re_path('^books/(?P<id>\d+)/', views.BookDetail.as_view()),

]

# views.py
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from django.views import View

# Create your views here.
from rest_framework.views import APIView
from app01 import models
from rest_framework import serializers
from app01 import myserial

class MyResponse():
    def __init__(self):
        self.status=100
        self.msg=None
    @property
    def get_dic(self):
        return self.__dict__   # 将类里面的数据转换成为字典

class BookDetail(APIView):
    # 查询单个数据
    def get(self,request,id):
        response=MyResponse()
        book=models.Book.objects.filter(pk=id).first()      # 查询单个数据
        res=myserial.BookSer(instance=book,many=False)      # 序列化查询单个数据
        response.msg='查询成功'
        response.data=res.data
        return JsonResponse(response.get_dic,safe=False)

    # 修改单个数据
    def put(self,request,id):
        response = MyResponse()
        book = models.Book.objects.filter(pk=id).first()

        # instance=book:表示被修改的对象
        # data=request.data:表示修改的值
        res = myserial.BookSer(instance=book, data=request.data)
        if res.is_valid():
            res.save()                      # 可以新增可以修改数据
            # print(res.instance)           # res.instance为一个对象<class 'app01.models.Book'>,拿到的值是字段"name"("三国演义")
            print(res.data)                 # res.data拿到的就是前端的请求值: {'name': '三国演义', 'price': '188.00', 'publish_date': '2018-11-06', 'publish': 1}
            response.msg = '修改成功'
            response.data=res.data
            return JsonResponse(response.get_dic,safe=False)
            """
            {
                "status": 100,
                "msg": "修改成功",
                "data": {
                    "name": "三国演义",
                    "price": "188.00",
                    "publish_date": "2018-11-06",
                    "publish": 1
                }
            }
            """
        else:
            response.msg = '修改失败'
            response.status=1001
            response.data=res.errors
            """
            {
                "status": 1001,
                "msg": "修改失败",
                "data": {
                    "name": [
                        "This field is required."
                    ],
                    "price": [
                        "This field is required."
                    ]
                }
            }
            """
        return JsonResponse(response.get_dic,safe=False)

    # 删除单个数据
    def delete(self,request,id):
        response = MyResponse()
        models.Book.objects.filter(pk=id).delete()
        response.msg = '删除成功'
        return JsonResponse(response.get_dic,safe=False)


class Book(APIView):
    # 查询所有数据
    def get(self,request):
        response=MyResponse()
        books=models.Book.objects.all()
        res=myserial.BookSer(books,many=True)  # many=True表示序列化多条数据
        response.msg='查询成功'
        response.data=res.data
        return JsonResponse(response.get_dic,safe=False)

    # 新增一个数据
    def post(self,request):
        response = MyResponse()
        res=myserial.BookSer(data=request.data)      # 通过request.data拿到数据,然后执行反序列化
        if res.is_valid():
            res.save()                               # 执行反序列方法,保存数据到DB里面
            response.msg="新增数据成功"
            response.data=res.data
        else:
            response.msg = '新增数据失败'
            response.status=1001
            response.data=res.errors
        return JsonResponse(response.get_dic,safe=False)

# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        fields=['name','price','publish_date','publish']
    # 局部校验,当修改数据表是,某个字段未填值,会报错"该字段必填",给一个提示。
    name=serializers.CharField(error_messages={"required":"该字段必填"})
    price=serializers.CharField(error_messages={"required":"该字段必填"})
    publish_date=serializers.CharField(error_messages={"required":"该字段必填"})
    publish=serializers.CharField(error_messages={"required":"该字段必填"})

  方式二:需要手写get,post等方法

# urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),

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

]

# views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin

class Book(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset=models.Book.objects.all()
    serializer_class=myserial.BookSer
    def get(self,request,*args,**kwargs):
        return self.list(self,request,*args,**kwargs)
    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)

class BookDetail(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Book
    serializer_class = myserial.BookSer
    def get(self,request,*args,**kwargs):
        return self.retrieve(request,*args,**kwargs)
    def put(self,request,*args,**kwargs):
        return self.update(request,*args,**kwargs)
    def delete(self,request,*args,**kwargs):
        return self.destroy(request,*args,**kwargs)

# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        fields=['name','price','publish_date','publish']

 方式三:不需要手写get,post等方法

# urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),

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

]

# views.py
from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView

class BookView(ListCreateAPIView):
    queryset=models.Book.objects.all()
    serializer_class=myserial.BookSer

class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset=models.Book.objects.all()
    serializer_class=myserial.BookSer

# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        fields=['name','price','publish_date','publish']

  方式四:方法三的升级版,在urls.py处做了POST,GET请求方式的处理

# urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),

    re_path('^books/$', views.BookView.as_view({"get":"list","post":"create"})),
    re_path('^books/(?P<pk>\d+)/', views.BookView.as_view({"get":"retrieve","put":"update","delete":"destroy"})),

]

# views.py
from rest_framework.viewsets import ModelViewSet

class BookView(ModelViewSet):
    queryset=models.Book.objects.all()
    serializer_class=myserial.BookSer

# myserial.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        fields=['name','price','publish_date','publish']

9.2、视图类的基本写法(增、删、改、查),最终版本 -- 方式五

urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),

    # as_view()拿到的是CBTtest类里面某个函数的名称[详细信息查看View类里面的as_view函数,最终返回一个get|post函数的内存地址]以及一个request对象。
    re_path('^books/$', views.BookView.as_view({'get':'get_all'})),             # get请求响应get_all函数
    re_path('^books/(?P<pk>\d+)/', views.BookView.as_view({'get':'get_one'})),  # get请求响应get_one函数

]

views.py文件
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin

class MyResponse():
    def __init__(self):
        self.status=100
        self.msg=None
    @property
    def get_dic(self):
        return self.__dict__   # 将类里面的数据转换成为字典

# ViewSetMixin 这个类可以重改路由,可以让一个视图函数可以响应多个(post|gey|put|delete)方法
class BookView(ViewSetMixin,APIView):
    def get_all(self,request):
        response = MyResponse()
        book = models.Book.objects.all()                    # 查询所有数据
        res = myserial.BookSer(instance=book, many=True)    # # 序列化查询所有数据
        response.msg = '查询成功'
        response.data = res.data
        return JsonResponse(response.get_dic,safe=False)

    def get_one(self,request,pk):
        response=MyResponse()
        book=models.Book.objects.filter(pk=pk).first()      # 查询单个数据
        res=myserial.BookSer(instance=book,many=False)      # 序列化查询单个数据
        response.msg='查询成功'
        response.data=res.data
        return JsonResponse(response.get_dic,safe=False)

# myserial.py文件
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        fields=['name','price','publish_date','publish']

class AuthorSer(serializers.ModelSerializer):
    class Meta:
        model=models.Author
        fields=['nid','age','name']

十、认证组件

      作用:校验是否登录

# urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),
    # re_path('^books/$', views.Book.as_view()),
    re_path('^login/$', views.Login.as_view()),
    re_path('^books/$', views.Book.as_view()),
]

views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin


# 登录组件:登录之后,返回前端一个字符串,那么下次访问时携带这个字符串,通过校验字符串来判断登录是否有效
import hashlib
import time
# 生成随机字符串
def get_token(user_name):
    md=hashlib.md5()
    md.update(user_name.encode('utf-8'))
    md.update(str(time.time()).encode('utf-8'))
    return md.hexdigest()

class MyResponse():
    def __init__(self):
        self.status=100
        self.msg=None
    @property
    def get_dic(self):
        return self.__dict__   # 将类里面的数据转换成为字典


# 用户登录认证方法
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class myAuthen(BaseAuthentication):
    def authenticate(self,request):
        token=request.query_params.get('token')
        res=models.UserToken.objects.filter(token=token).first()
        if res:
            # 要写多个类,这里返回None
            # return None
            return res.user,res        # 最后一个认证类,返回2个对象
        else:
            raise AuthenticationFailed('您未登录,登录失败')


# 用户登录的方法
class Login(APIView):
    authentication_classes = []      # 用户登录认证,设置为空时,表示不需要认证
    def post(self,request):
        response=MyResponse()
        name=request.data.get('name')
        pwd=request.data.get('pwd')
        user = models.User.objects.filter(name=name,pwd=pwd).first()
        if user:
            response.msg = '登录成功'
            # 登录成功后生成一个随机字符串,以后再发请求,都携带这个字符串
            token = get_token(name)
            response.token=token
            # 把随机字符串保存到数据库
            models.UserToken.objects.update_or_create(user=user,defaults={'token':token})       # update_or_create表示:有就更新,没有就创建
        else:
            response.msg = '用户名或密码错误'
            response.status = 101
        return JsonResponse(response.get_dic,safe=False)


# 用户登录成功后,才可以查看
class Book(APIView):
    authentication_classes = [myAuthen,]  # authentication_classes会调用MyAuthen类,实例化出authenticate方法
    def get(self,request):
        # print(request.user)   # User object (1)         用户对象,user为源码固定写法
        # print(request.auth)   # UserToken object (1)    Token对象,auth为源码固定写法
        response = MyResponse()
        # 登录后才能访问
        book = models.Book.objects.all()
        res = myserial.BookSer(instance=book,many=True)
        response.msg = '查询成功'
        response.data = res.data
        return JsonResponse(response.get_dic,safe=False)

# models.py文件
from django.db import models
class User(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32,null=True)

# 用户登录后,存储一个随机字符串,下次用户在发起请求时,会携带这个随机字符串,
class UserToken(models.Model):
    user=models.OneToOneField(to=User,to_field='nid',unique=True,on_delete=models.CASCADE)
    token=models.CharField(max_length=64)

# 请求验证的方式:
post登录请求:http://127.0.0.1:8000/login/   # 输入用户名和密码(JSON格式)
{
	"name":"szq",
	"pwd":"123"
}

get登录成功后查看数据请求:http://127.0.0.1:8000/books/?token=9da5d186b82756855a2a9dfe00f00f7d

 认证组件总结:

1、作用:校验是否登录
2、首先定义一个类,继承BaseAuthentication,写一个方法,authenticate,在方法内部写认证过程。
    认证通过,返货None,或者2个对象(auth,user),这2个对象在视图类的request中可以取出来。
    class myAuthen(BaseAuthentication):
        def authenticate(self,request):
            token=request.query_params.get('token')
            res=models.UserToken.objects.filter(token=token).first()
            if res:
                # 要写多个类,这里返回None
                # return None
                return res.user,res        # 最后一个认证类,返回2个对象
            else:
                raise AuthenticationFailed('您未登录,登录失败')
3、局部使用:在需要验证的类组件下,加上:authentication_classes = [myAuthen,] 也就是说,想要访问Book类必须通过登录验证 "authentication_classes = [myAuthen,]"
4、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下: 
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ]
}

 认证组件补充 - 不存数据库的token验证:

def get_token(id,salt='123'):
    import hashlib
    md=hashlib.md5()
    md.update(bytes(str(id),encoding='utf-8'))
    md.update(bytes(salt,encoding='utf-8'))

    return md.hexdigest()+'|'+str(id)

def check_token(token,salt='123'):
    ll=token.split('|')
    import hashlib
    md=hashlib.md5()
    md.update(bytes(ll[-1],encoding='utf-8'))
    md.update(bytes(salt,encoding='utf-8'))
    if ll[0]==md.hexdigest():
        return True
    else:
        return False

class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        success=check_token(token)
        if success:
            return
        else:
            raise AuthenticationFailed('认证失败')
    def authenticate_header(self,request):
        pass
class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':1001,'msg':None}
        try:
            name=reuquest.data.get('name')
            pwd=reuquest.data.get('pwd')
            user=models.User.objects.filter(username=name,password=pwd).first()
            if user:
                token=get_token(user.pk)
                # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                back_msg['status']='1000'
                back_msg['msg']='登录成功'
                back_msg['token']=token
            else:
                back_msg['msg'] = '用户名或密码错误'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)
from rest_framework.authentication import BaseAuthentication
class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
            return
        else:
            raise AuthenticationFailed('认证失败')
    def authenticate_header(self,request):
        pass

class Course(APIView):
    authentication_classes = [TokenAuth, ]

    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')

十一、权限组件 --  和认证组件写法基本相同

      作用:校验已经登录的用户是否有权限访问

urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),
    # re_path('^books/$', views.Book.as_view()),
    re_path('^login/$', views.Login.as_view()),
    re_path('^books/$', views.Book.as_view()),
]

views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin


# 登录组件:登录之后,返回前端一个字符串,那么下次访问时携带这个字符串,通过校验字符串来判断登录是否有效
import hashlib
import time
# 生成随机字符串
def get_token(user_name):
    md=hashlib.md5()
    md.update(user_name.encode('utf-8'))
    md.update(str(time.time()).encode('utf-8'))
    return md.hexdigest()

class MyResponse():
    def __init__(self):
        self.status=100
        self.msg=None
    @property
    def get_dic(self):
        return self.__dict__   # 将类里面的数据转换成为字典


# 用户登录认证方法
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission

class myAuthen(BaseAuthentication):   # 认证组件
    def authenticate(self,request):
        token=request.query_params.get('token')
        res=models.UserToken.objects.filter(token=token).first()
        if res:
            # 要写多个类,这里返回None
            # return None
            return res.user,res        # 最后一个认证类,返回2个对象
        else:
            raise AuthenticationFailed('您未登录,登录失败')

class myPermission(BasePermission):    # 权限认证
    message = '不是超级用户,无权访问!'
    def has_permission(self,request,view):
        if request.user.usertype != 3:     # 判断用户的权限类型(权限值)
            return False
        else:
            return True


# 用户登录的方法
class Login(APIView):
    authentication_classes = []      # 用户登录认证,设置为空时,表示不需要认证
    def post(self,request):
        response=MyResponse()
        name=request.data.get('name')
        pwd=request.data.get('pwd')
        user = models.User.objects.filter(name=name,pwd=pwd).first()
        if user:
            response.msg = '登录成功'
            # 登录成功后生成一个随机字符串,以后再发请求,都携带这个字符串
            token = get_token(name)
            response.token=token
            # 把随机字符串保存到数据库
            models.UserToken.objects.update_or_create(user=user,defaults={'token':token})       # update_or_create表示:有就更新,没有就创建
        else:
            response.msg = '用户名或密码错误'
            response.status = 101
        return JsonResponse(response.get_dic,safe=False)


# 用户登录成功后,才可以查看
class Book(APIView):
    authentication_classes = [myAuthen,]  # authentication_classes会调用MyAuthen类,实例化出authenticate方法
    permission_classes = [myPermission,]  # permission_classes会调用myPermission类,实例化出has_permission方法
    def get(self,request):
        # print(request.user)   # User object (1)         用户对象,user为源码固定写法
        # print(request.auth)   # UserToken object (1)    Token对象,auth为源码固定写法
        response = MyResponse()
        # 登录后才能访问
        book = models.Book.objects.all()
        res = myserial.BookSer(instance=book,many=True)
        response.msg = '查询成功'
        response.data = res.data
        return JsonResponse(response.get_dic,safe=False)

# models.py文件
from django.db import models
class User(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32,null=True)
    mychoice=((1,'普通用户'),(2,'超级用户'),(3,'宇宙用户'))
    usertype=models.IntegerField(choices=mychoice,default=1)
    # 指定usertype的值,只能从mychoice里面取。

# 用户登录后,存储一个随机字符串,下次用户在发起请求时,会携带这个随机字符串,
class UserToken(models.Model):
            user=models.OneToOneField(to=User,to_field='nid',unique=True,on_delete=models.CASCADE)
    token=models.CharField(max_length=64)

# 请求验证的方式:
post登录请求:http://127.0.0.1:8000/login/   # 输入用户名和密码(JSON格式)
{
	"name":"szq",
	"pwd":"123"
}

get登录成功后查看数据请求:http://127.0.0.1:8000/books/?token=9da5d186b82756855a2a9dfe00f00f7d

  权限组件使用总结:

1、作用:校验用户是否有权限访问:
2、因为是在"perform_authentication"用户认证之后才执行,所以可以取出user(认证处需要return user,否则就没有)
class myPermission():
message = '不是超级用户,无权访问!'
def has_permission(self,request,view):
    if request.user.usertype != 3:
        return False
    else:
        return True
3、局部使用:在需要验证的类组件下,加上:permission_classes = [myPermission,]
4、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下: 
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ],
    "DEFAULT_PERMISSION_CLASSES": ["app01.auth.myPermission", ]
}

十二、频率组件

      作用:限制已登录用户的页面访问次数(每分钟允许访问几次)

urls.py文件
urlpatterns = [
    path('admin/', admin.site.urls),
    # re_path('^books/$', views.Book.as_view()),
    re_path('^login/$', views.Login.as_view()),
    re_path('^books/$', views.Book.as_view()),
]

views.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
from app01 import models
from app01 import myserial
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin


# 登录组件:登录之后,返回前端一个字符串,那么下次访问时携带这个字符串,通过校验字符串来判断登录是否有效
import hashlib
import time
# 生成随机字符串
def get_token(user_name):
    md=hashlib.md5()
    md.update(user_name.encode('utf-8'))
    md.update(str(time.time()).encode('utf-8'))
    return md.hexdigest()

class MyResponse():
    def __init__(self):
        self.status=100
        self.msg=None
    @property
    def get_dic(self):
        return self.__dict__   # 将类里面的数据转换成为字典


# 用户登录认证方法
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission

# 认证组件
class myAuthen(BaseAuthentication):   
    def authenticate(self,request):
        token=request.query_params.get('token')
        res=models.UserToken.objects.filter(token=token).first()
        if res:
            # 要写多个类,这里返回None
            # return None
            return res.user,res        # 最后一个认证类,返回2个对象
        else:
            raise AuthenticationFailed('您未登录,登录失败')

# 权限认证
class myPermission(BasePermission):    
    message = '不是超级用户,无权访问!'
    def has_permission(self,request,view):
        if request.user.usertype != 3:     # 判断用户的权限类型(权限值)
            return False
        else:
            return True

# 频率组件认证 -- restful框架自带的功能:需要重写2个方法:
#   1、scope = "test_szq"    # "test_szq" 是setting.py文件中 "REST_FRAMEWORK" 全局认证里面定义的
#   2、重写 get_cache_key 方法,如下:

from rest_framework.throttling import SimpleRateThrottle  
class MyThrottles(SimpleRateThrottle):
    scope = "test_szq"
    def get_cache_key(self, request, view):
        return self.get_ident(request)              # 方法一
        # return request.META.get("REMOTE_ADDR")    # 方法二


# 用户登录的方法
class Login(APIView):
    authentication_classes = []      # 用户登录认证,设置为空时,表示不需要认证
    def post(self,request):
        response=MyResponse()
        name=request.data.get('name')
        pwd=request.data.get('pwd')
        user = models.User.objects.filter(name=name,pwd=pwd).first()
        if user:
            response.msg = '登录成功'
            # 登录成功后生成一个随机字符串,以后再发请求,都携带这个字符串
            token = get_token(name)
            response.token=token
            # 把随机字符串保存到数据库
            models.UserToken.objects.update_or_create(user=user,defaults={'token':token})       # update_or_create表示:有就更新,没有就创建
        else:
            response.msg = '用户名或密码错误'
            response.status = 101
        return JsonResponse(response.get_dic,safe=False)


# 用户登录成功后,才可以查看
class Book(APIView):
    authentication_classes = [myAuthen,]  # authentication_classes会调用MyAuthen类,实例化出authenticate方法
    permission_classes = [myPermission,]  # permission_classes会调用myPermission类,实例化出has_permission方法
    def get(self,request):
        # print(request.user)   # User object (1)         用户对象,user为源码固定写法
        # print(request.auth)   # UserToken object (1)    Token对象,auth为源码固定写法
        response = MyResponse()
        # 登录后才能访问
        book = models.Book.objects.all()
        res = myserial.BookSer(instance=book,many=True)
        response.msg = '查询成功'
        response.data = res.data
        return JsonResponse(response.get_dic,safe=False)

# models.py文件
from django.db import models
class User(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32,null=True)
    mychoice=((1,'普通用户'),(2,'超级用户'),(3,'宇宙用户'))
    usertype=models.IntegerField(choices=mychoice,default=1)
    # 指定usertype的值,只能从mychoice里面取。

# 用户登录后,存储一个随机字符串,下次用户在发起请求时,会携带这个随机字符串,
class UserToken(models.Model):
            user=models.OneToOneField(to=User,to_field='nid',unique=True,on_delete=models.CASCADE)
    token=models.CharField(max_length=64)

# 请求验证的方式:
post登录请求:http://127.0.0.1:8000/login/   # 输入用户名和密码(JSON格式)
{
	"name":"szq",
	"pwd":"123"
}

get登录成功后查看数据请求:http://127.0.0.1:8000/books/?token=9da5d186b82756855a2a9dfe00f00f7d

访问次数超过限制后报错: "detail": "Request was throttled. Expected available in 58 seconds."

  频率组件使用总结:

1、作用:校验已登录用户对某个页面的访问频率(每分钟可以访问多少次)

2、频率组件认证 -- restful框架自带的功能:需要重写2个方法:
#   1、scope = "test_szq"    # "test_szq" 是setting.py文件中 "REST_FRAMEWORK" 全局认证里面定义的
#   2、重写 get_cache_key 方法,如下:
from rest_framework.throttling import SimpleRateThrottle
class MyThrottles(SimpleRateThrottle):
    scope = "test_szq"
    def get_cache_key(self, request, view):
        return self.get_ident(request)              # 方法一
        # return request.META.get("REMOTE_ADDR")    # 方法二

3、局部使用:在需要验证的类组件下,加上:permission_classes = [MyThrottles,]

4、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下: 
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':[
        'rest_framework.parsers.JSONParser'
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auth.myAuthen", ],
    "DEFAULT_PERMISSION_CLASSES": ["app01.auth.myPermission", ],
    'DEFAULT_THROTTLE_RATES': {
        'test_szq': '5/m'     # 一分钟访问三次
    }
}
访问次数超过限制后报错: "detail": "Request was throttled. Expected available in 58 seconds."

十三、分页器

作用:把数据库查询到的数据按照指定的行数显示,比如一页只显示5行数据,或者10行数据

13.1、简单分页例子:

class Books(APIView):
    # 查询数据的接口
    def get(self,request):
        response=my_response.MyResponse()
        books=Book.objects.all()

        # 分页器
        '''
        page_size = api_settings.PAGE_SIZE  每页大小(显示多少数据)
        page_query_param = 'page2'    查询条件:"?page=2","2"表示第二页,和page_size关联使用
        
        page_size_query_param = None  指定每页的大小
        max_page_size = None          每页的大小,最多显示多少条,和page_size_query_param关联使用
        '''
        page=PageNumberPagination()
        page.page_size=5
        page.page_query_param='page'

        page.page_size_query_param='max'
        page.max_page_size=3
        page_book=page.paginate_queryset(books, request, view=self)

        ser=my_serialzers.BookSerial(page_book,many=True)
        response.data=ser.data
        return JsonResponse(response.get_dic,safe=False)

请求接口:http://127.0.0.1:8000/app01/index/?page=1&max=100
返回数据:
{
    "status": 200,
    "msg": null,
    "data": [
        {
            "nid": 1,
            "name": "xiyouji",
            "price": "999.00",
            "publish_date": "1999-09-09",
            "publish": "北京出版社",
            "publish_email": "111@111",
            "authors": [
                {
                    "nid": 2,
                    "name": "sudada",
                    "age": 28,
                    "author_telephone": "222",
                    "author_birthday": "1999-09-09"
                }
            ]
        },
        {
            "nid": 2,
            "name": "sanguoyanyi",
            "price": "299.00",
            "publish_date": "1993-04-05",
            "publish": "上海出版社",
            "publish_email": "222@222",
            "authors": [
                {
                    "nid": 1,
                    "name": "szq",
                    "age": 18,
                    "author_telephone": "111",
                    "author_birthday": "2000-06-11"
                }
            ]
        },
        {
            "nid": 3,
            "name": "人间世",
            "price": "399.00",
            "publish_date": "2021-12-01",
            "publish": "北京出版社",
            "publish_email": "111@111",
            "authors": []
        }
    ]
}

13.2、偏移分页例子:

class Books(APIView):
    # 查询数据的接口
    def get(self,request):
        response=my_response.MyResponse()
        books=Book.objects.all()

        # 分页器
        '''
        default_limit = api_settings.PAGE_SIZE   默认每页显示的数据条数
        limit_query_param = 'limit'              查询的条数,limit=10就查询10条,但优先级在default_limit之后
        offset_query_param = 'offset'            偏移条数
        max_limit = None                         最大显示数
        '''
        page=LimitOffsetPagination()
        page.default_limit=2
        page.limit_query_param="limit"
        page.limit_query_param="offset"
        page.limit_query_param=5
        page_book=page.paginate_queryset(books, request, view=self)

        ser=my_serialzers.BookSerial(page_book,many=True)
        response.data=ser.data
        return JsonResponse(response.get_dic,safe=False)

13.3、加密分页例子:

 想查看下一页的数据,需要请求返回数据里面的"next"字段对应的地址(默认是加密的)

 只能向下查看,不能向上。

class Books(APIView):
    # 查询数据的接口
    def get(self,request):
        response=my_response.MyResponse()
        books=Book.objects.all()

        # 分页器
        '''
        cursor_query_param = 'cursor'       查询字段,这里返回的是加密的,不能手动指定上/下页
        page_size = api_settings.PAGE_SIZE  默认每页条数
        ordering = '-created'               按什么排序
        '''
        page=CursorPagination()
        page.page_size=2
        page.ordering="nid"
        page_book=page.paginate_queryset(books, request, view=self)

        ser=my_serialzers.BookSerial(page_book,many=True)
        response.data=ser.data
        return page.get_paginated_response(ser.data)

请求:http://127.0.0.1:8000/app01/index/?cursor=cD0y
响应:{
    "next": "http://127.0.0.1:8000/app01/index/?cursor=cD00",  # 想查看下一页的数据,需要请求这个地址(默认是加密的)
    "previous": "http://127.0.0.1:8000/app01/index/?cursor=cj0xJnA9Mw%3D%3D",
    "results": [
        {
            "nid": 3,
            "name": "人间世",
            "price": "399.00",
            "publish_date": "2021-12-01",
            "publish": "北京出版社",
            "publish_email": "111@111",
            "authors": []
        },
        {
            "nid": 4,
            "name": "人间世1",
            "price": "399.00",
            "publish_date": "2021-12-01",
            "publish": "北京出版社",
            "publish_email": "111@111",
            "authors": []
        }
    ]
}

十四、响应器

  作用:显示一个优雅的页面

  显示页面主要使用的是:from rest_framework.response import Response 这个"Response"模块,也就是说返回的值在浏览器页面想要好看,必须 "return Response(response.get_dic)"。

# urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),
    # re_path('^books/$', views.Book.as_view()),
    re_path('^books/$', views.Book.as_view()),
]

# views.py文件中
from rest_framework.response import Response
from rest_framework.renderers import  HTMLFormRenderer,BrowsableAPIRenderer,JSONRenderer

class Book(APIView):
    renderer_classes = [JSONRenderer,BrowsableAPIRenderer]    # "JSONRenderer": 只返回纯JSON格式的字符串(局部使用)

    def get(self,request):
        response = MyResponse()
        book = models.Book.objects.all()
        res = myserial.BookSer(instance=book,many=True)
        response.msg = '查询成功'
        response.data = res.data
        return Response(response.get_dic)

浏览器查看的格式:

   响应器使用总结:

1、作用:显示一个相对优雅的页面

2、局部使用:在需要验证的类组件下,加上:
renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

3、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下: 
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES' : [
        'rest_framework.renderers.JSONRenderer',         # 响应json格式的数据
        'rest_framework.renderers.BrowsableAPIRenderer', # 响应json格式的数据,浏览器优雅显示
    ]  # 响应器的全局使用
}

十四、URL控制器(路由控制routers),自动注册路由

# urls.py文件中
from django.contrib import admin
from django.urls import path,re_path
from app01 import views

from django.conf.urls import url,include
from rest_framework import routers
router = routers.DefaultRouter()
router.register('book', views.BooksDetail)   # book:URL的名称,views.BooksDetail:对应view视图的BooksDetail类

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

    # url控制器,手动版,需要手动定义传参,并且在Book类下,使用"*args,**kwargs"接收。
    # re_path('^books/$', views.Book.as_view({"get": "get_all"})),
    # re_path('^books.(?P<format>\w+)', views.Book.as_view({"get": "get_all"})),

    # url控制器,全自动版
    re_path('^', include(router.urls)),   # 这里不需要在写具体的URL名称了,因为在router.register里已经定义好了URL为book,对应的view视图为Book类。
}

# views.py文件中
class BooksDetail(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = my_serialzers.BookSerial

    # 响应数据的格式
    renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

    # 查询数据的接口
    def get(self,request):
        response=my_response.MyResponse()
        response.msg="success"
        books=Book.objects.all()
        ser=my_serialzers.BookSerial(books,many=True)
        response.data=ser.data
        return Response(response.get_dic)

访问请求:
普通请求:http://127.0.0.1:8000/book/?token=464ba5789d79fb655be3e58c9a0f40ea
JSON格式的请求:http://127.0.0.1:8000/book.json?token=464ba5789d79fb655be3e58c9a0f40ea

如果请求的URL格式不对,则抛出异常如下:
#########################################################################################
Using the URLconf defined in djangoresful_20190331_28.urls, Django tried these URL patterns, in this order:

admin/
^ ^book/$ [name='book-list']
^ ^book\.(?P<format>[a-z0-9]+)/?$ [name='book-list']
^ ^book/(?P<pk>[^/.]+)/$ [name='book-detail']
^ ^book/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail']
^ ^$ [name='api-root']
^ ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']
^login/$
The current path, book/.json, didn't match any of these.

十五、版本控制

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # 以GET请求的方式请求时,使用这种方式
    # re_path('^books/$', views.Book.as_view()),
    # re_path('^login/$', views.Login.as_view()),

    # 以URL请求的方式请求时,使用这种方式
    re_path('^(?P<version>\w+)/books/', views.Book.as_view()),
    re_path('^login/$', views.Login.as_view()),
]

# 在views.py文件中
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning,AcceptHeaderVersioning,NamespaceVersioning,URLPathVersioning

class BookSer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        fields=['name','price','publish_date','publish']

class Book(APIView):
    # versioning_class = QueryParameterVersioning   # 以GET请求的方式请求时,使用"QueryParameterVersioning"
    versioning_class = URLPathVersioning   # 以URL请求的方式,使用"URLPathVersioning"
    def get(self,request,*args,**kwargs):
        response = MyResponse()
        print(request.version)
        book = models.Book.objects.all()
        res = myserial.BookSer(instance=book,many=True)
        response.msg = '查询成功'
        response.data = res.data
        return Response(response.get_dic)

# modules.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)
    publish_date = models.DateField(auto_now_add=False)
    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
    authors=models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name

URL请求:http://127.0.0.1:8000/v2/books/   # 只允许v1和v2版本访问

  版本控制使用总结:

1、作用:允许某个版本可以访问,某个版本不能访问(体现在URL的路径上)

2、局部使用:在需要验证的类组件下,加上:versioning_class = URLPathVersioning

3、全局使用:在settings.py文件中加上:"DEFAULT_PERMISSION_CLASSES",如下: 
REST_FRAMEWORK = {
    # 全局使用版本控制
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',
    'DEFAULT_VERSION': 'v1',            # 默认版本(从request对象里取不到,显示的默认值)
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允许的版本
    'VERSION_PARAM': 'version'          # URL中获取值的key
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值