第一阶段:
一、什么是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
}