为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。
目前市面上大部分公司开发人员使用的接口服务架构主要有:restful、rpc。
rpc: 翻译成中文:远程过程调用[远程服务调用].
http://www.lufei.com/api
post请求
action=get_all_student¶ms=301&sex=1
接口多了,对应函数名和参数就多了,前端在请求api接口时,就会比较难找.容易出现重复的接口
restful: 翻译成中文: 资源状态转换.
把后端所有的数据/文件都看成资源.
那么接口请求数据,本质上来说就是对资源的操作了.
web项目中操作资源,无非就是增删查改.所以要求在地址栏中声明要操作的资源是什么,然后通过http请求动词来说明对资源进行哪一种操作.
POST http://www.lufei.com/api/students/ 添加学生数据
GET http://www.lufei.com/api/students/ 获取所有学生
DELETE http://www.lufei.com/api/students/ 删除1个学生
应该尽量将API部署在专用域名之下。
https://www.jd.com
https://api.example.com
如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。
https://example.org/api/
应该将API的版本号放入URL。
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github就采用了这种做法。
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource)
(1) 资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应。
举例来说,以下是不好的例子:
/getProducts
/listOrders
/retreiveClientByOrder?orderId=1
对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。
GET /products :将返回所有产品清单
POST /products :将产品新建到集合
GET /products/4 :将获取产品 4
PATCH(或)PUT /products/4 :将更新产品 4
(2) API中的名词应该使用复数。无论子资源或者所有资源。
举例来说,获取产品的API可以这样定义
获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面四个(括号里是对应的SQL命令)。
还有三个不常用的HTTP动词。
下面是一些例子。
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数。query_string 查询字符串,地址栏后面问号后面的数据,格式: name=xx&sss=xxx
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoos/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
/zoos/2/animals
/animals?zoo_id=2
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
- 200 OK - [GET]:服务器成功返回用户请求的数据
- 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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
状态码的完全列表参见这里或这里。
如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
{
error: "Invalid API key"
}
针对不同操作,服务器向用户返回的结果应该符合以下规范。
RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。
比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。
{
"current_user_url": "https://api.github.com/user",
"authorizations_url": "https://api.github.com/authorizations",
// ...
}
从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。
{
"message": "Requires authentication",
"documentation_url": "https://developer.github.com/v3"
}
上面代码表示,服务器给出了提示信息,以及文档的网址。
服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
核心思想: 缩减编写api接口的代 – DRF
Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。
中文文档:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework
github: https://github.com/encode/django-rest-framework/tree/master
英文文档:https://www.django-rest-framework.org/
特点:
提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
提供了丰富的类视图、Mixin扩展类,简化视图的编写;
丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
多种身份认证和权限认证方式的支持;[jwt]
内置了限流系统;
直观的 API web 界面;
可扩展性,插件丰富
DRF需要以下依赖:
DRF是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建。(若没有Django环境,需要先创建环境安装Django)
安装DRF
pip3 install django==2.2
pip3 install djangorestframework
在settings.py的INSTALLED_APPS中添加’rest_framework’。
INSTALLED_APPS = [
...
'rest_framework',
]
接下来就可以使用DRF提供的功能进行api接口开发了。在项目中如果使用rest_framework框架实现API接口,主要有以下三个步骤:
class Student(models.Model):
# 模型字段
name = models.CharField(max_length=100,verbose_name="姓名",help_text='提示文本:不能为空')
sex = models.BooleanField(default=1,verbose_name="性别")
age = models.IntegerField(verbose_name="年龄")
class_null = models.CharField(max_length=5,verbose_name="班级编号")
description = models.TextField(max_length=1000,verbose_name="个性签名")
class Meta:
db_table="tb_student"
verbose_name = "学生"
verbose_name_plural = verbose_name
创建数据库
create database students charset utf8;
主引用中__init__.py
设置使用pymysql作为数据库驱动
import pymysql
pymysql.install_as_MySQLdb()
settings.py配置文件中设置mysql的账号密码
DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# },
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "students",
"HOST": "127.0.0.1",
"PORT": 3306,
"USER": "root",
"PASSWORD":"123456",
}
}
终端下,执行数据迁移
python manage.py makemigrations
python manage.py migrate
有如下报错:
解决方案:
注释掉 python/site-packages/django/backends/mysql/base.py中的35和36行代码。
解决方案:
backends/mysql/operations.py146行里面新增一个行代码:
query = query.encode()
在students应用目录中新建serializers.py用于保存该应用的序列化器。
创建一个StudentModelSerializer用于序列化与反序列化。
from rest_framework.serializers import ModelSerializer
from students.models import Student
# 创建序列化器类,回头会在试图中被调用
class StudentModelSerializer(ModelSerializer):
class Meta:
model = Student
fields = "__all__"
在students应用的views.py中创建视图StudentViewSet,这是一个视图集合
from rest_framework.viewsets import ModelViewSet
from .models import Student
from .serializers import StudentModelSerializer
# Create your views here.
class StudentViewSet(ModelViewSet):
queryset = Student.objects.all() #指明该视图集在查询数据时使用的查询集
serializer_class = StudentModelSerializer #指明该视图在进行序列化或反序列化时使用的序列化器
在students应用的urls.py中定义路由信息。
from . import views
from rest_framework.routers import DefaultRouter
# 路由列表
urlpatterns = []
router = DefaultRouter() # 可以处理视图的路由器,自动通过视图来生成增删改查的url路径
router.register('students', views.StudentViewSet) #students是生成的url前缀,名称随便写, 向路由器中注册视图集
urlpatterns += router.urls # 将路由器中的所以路由信息追到到django的路由列表中
最后把students子应用中的路由文件加载到总路由文件中.
from django.contrib import admin
from django.urls import path,include #path:直接写路径,re_path:可以写正则匹配
urlpatterns = [
path('admin/', admin.site.urls),
path("stu/",include("students.urls")),
]
运行该django项目
在浏览器输入路径,可以看到DRF提供的API Web浏览页面
在页面底部可以提交post请求,添加到数据库
在网址中输入路径/1,可以访问id为1的信息
在网址中输入路径/S/,可以访问删除的接口
作用:
1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能
定义好Serializer类后,就可以创建Serializer对象了。
Serializer(instance=None, data=empty, **kwarg)
说明:
1)用于序列化时,将模型类对象传入instance参数
2)用于反序列化时,将要被反序列化的数据传入data参数
3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如
serializer = AccountSerializer(account, context={
'request': request})
通过context参数附加的数据,可以通过Serializer对象的context属性获取。
1 定义一个序列化器,在应用中创建一个py文件,serializers.py
from rest_framework import serializers
class StudentSerizlizer(serializers.Serializer):
name = serializers.CharField()
age = serializers.IntegerField()
class_null = serializers.CharField()
description = serializers.CharField()
2 写views.py文件
from django.shortcuts import render
from django.http import JsonResponse
from django.views import View
from app01 import models
from .serializers import StudentSerizlizer
# Create your views here.
class StudentView(View):
def get(self,request):
one = models.Student.objects.get(id=1)
serializer = StudentSerizlizer(instance=one)
print(serializer.data) #结果为:{'name': 'ceshi1', 'age': 6, 'class_null': '1', 'description': '90886'}
# all = models.Student.objects.all()
# serializer = StudentSerizlizer(instance=all,many=True) #结果为:[{},{}]形式
#many=True 在序列化的数据是多条时,需要加上many=True
#print(serializer.data)
#结果为:[OrderedDict([('name', 'ceshi1'), ('age', 6), ('class_null', '1'), ('description', '90886.....
return JsonResponse(serializer.data,safe=False,json_dumps_params={
'ensure_ascii':False})
# JsonResponse回复的就是Json类型数据,JsonResponse响应非字典类型数据时,需要加上safe=False这个参数才行,json_dumps_params在浏览器中显示中文
3 配置路由
from django.urls import path
from use_serializers import views
urlpatterns = [
path('students/', views.StudentView.as_view())
]
from django.contrib import admin
from django.urls import path, include #path:直接写路径,re_path:可以写正则匹配
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('use01/', include('use_serializers.urls')),
]
测试结果:
1 定义一个序列化器,在当前应用中新建serializers.py
from rest_framework import serializers
class StudentSerializers(serializers.Serializer):
name = serializers.CharField(max_length=4) #校验长度是否是4
age = serializers.IntegerField(max_value=18) #校验长度是否是18
class_null = serializers.CharField()
description = serializers.CharField(allow_blank=True) #允许该字段不填,allow_null用null来存空值
2 写views.py文件
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.views import View
from .serializers import StudentSerializers
# Create your views here.
class StuView(View):
def post(self,request):
data = {
'name':request.POST.get('name'),
'age' : request.POST.get('age'),
'class_null':request.POST.get('class_null'),
'description':request.POST.get('description'),
}
ser = StudentSerializers(data=data)
if ser.is_valid(): #校验通过为True
print(ser.validated_data) #校验通过的正确信息
print(ser.errors) #校验未通过的错误信息
return HttpResponse('ok')
3 url:
from django.urls import path
from use_unserializers import views
urlpatterns = [
path('students/',views.StuView.as_view())
]
1 定义一个序列化器,在当前应用中新建serializers.py
from rest_framework import serializers
def check01(val):
if '666' in val :
raise serializers.ValidationError('错误!')
else:
return val
class StudentSerializers(serializers.Serializer):
name = serializers.CharField(max_length=4,validators=[check01,])
age = serializers.IntegerField(max_value=18)
class_null = serializers.IntegerField()
description = serializers.CharField(allow_blank=True)
#局部钩子 validate_字段名:针对单个字段进行校验
def validate_name(self,val):
if '777' in val :
raise serializers.ValidationError('错误!!')
return val
#全局钩子 validate : 针对多个属性数据进行校验
def validate(self, data):
print(data)
age = data.get('age')
print(age)
class_null = data.get('class_null')
print(class_null)
if age == class_null:
raise serializers.ValidationError('错误!!!!')
return data
执行顺序:
# is_valid()方法时,先校验序列化器类中的所有属性Field(CharField(参数))参数对应的校验规则
# 再执行局部钩子
# 举例:比如有name和age两个属性,先执行name 的参数校验,在执行name的局部钩子(validate_name(self,val)),然后再执行age属性的参数校验,再执行age的局部钩子,最后所有属性的参数校验和局部钩子校验完成之后,执行全局钩子
# 最后执行全局钩子
2 views.py文件
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.views import View
from .serializers import StudentSerializers
from app01 import models
# Create your views here.
class StuView(View):
def post(self,request):
data = {
'name':request.POST.get('name'),
'age' : request.POST.get('age'),
'class_null':request.POST.get('class_null'),
'description':request.POST.get('description'),
}
ser = StudentSerializers(data=data)
if ser.is_valid(): #校验通过为True
print(ser.validated_data) #打印正确信息
ret = models.Student.objects.create(
**ser.validated_data #将正确的信息写入数据库
)
serializer = StudentSerializers(instance=ret) #序列化
return JsonResponse(serializer.data,safe=False,json_dumps_params={
'ensure_ascii':False}) #返回新增数据
print(ser.errors) #打印错误信息
return JsonResponse(ser.errors) #返回错误信息
字段 | 字段构造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 微软时间戳,通过微秒生成一个随机字符串 |
IPAddressField | IPAddressField(protocol=‘both’, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices与Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_length | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最大值 |
min_value | 最小值 |
参数名称 | 说明 |
---|---|
read_only | 表明该字段仅用于序列化输出,默认False |
write_only | 表明该字段仅用于反序列化输入,默认False |
required | 表明该字段在反序列化时必须输入,默认True |
default | 反序列化时使用的默认值 |
allow_null | 表明该字段是否允许传入None,默认False |
validators | 该字段使用的验证器 |
error_messages | 包含错误编号与错误信息的字典 |
label | 用于HTML展示API页面时,显示的字段名称 |
help_text | 用于HTML展示API页面时,显示的字段帮助提示信息 |
等于True会主动抛异常:
在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。
serializer.is_valid(raise_exception=True)
from rest_framework import serializers
class StudentSerizlizer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
# read_only=True,序列化时序列化出该字段数据,反序列化校验时不要校验这个数据
name = serializers.CharField(max_length=4,)
age = serializers.IntegerField(max_value=18,write_only=True)
# write_only=True,序列化时不序列化这个字段数据,反序列校验时,需要客户端传递这个数据过来进行校验
class_null = serializers.CharField()
description = serializers.CharField(allow_blank=True)
只更新传入的数据
def put(self,request):
data = {
'name':'chao',
'age':18
}
serializer = StudentSerizlizer(data=data,partial=True)
# partial=True:只需要校验传入给序列化器的数据,适用于部分数据更新
serializer.is_valid()
print(serializer.errors)
print('正确数据:', serializer.validated_data)
return JsonResponse({
'ss': 'kk'})
**增:**在序列器中定义create方法,在视图中通过save来触发
class StudentSerizlizer(serializers.Serializer):
#
name = serializers.CharField(max_length=4,validators=[check666,])
...
def create(self, validated_data):
# self.validated_data
# validated_data
# print('>>',self.validated_data)
print('>>',validated_data)
ret = models.Student.objects.create(
**validated_data
)
return ret
视图中通过save方法来触发,序列化器类中的create方法
data = {
'name':request.POST.get('name'),
'age':request.POST.get('age'),
'class_null':request.POST.get('class_null'),
'description':request.POST.get('description'),
}
ser = StudentSerizlizer(data=data)
if ser.is_valid():
instance = ser.save() #得到create方法的返回值
serializer = StudentSerizlizer(instance=instance)
return JsonResponse(serializer.data,safe=False,json_dumps_params={
'ensure_ascii':False})
# return JsonResponse({'xx':'xx'})
**改:**在序列化器中定义更新方法,在视图函数中通过save保存
class StudentSerizlizer(serializers.Serializer):
#
name = serializers.CharField(max_length=4,validators=[check666,])
...
# 做更新动作
def update(self, instance, validated_data):
# print(instance)
# print(validated_data)
instance.name = validated_data['name']
instance.age = validated_data['age']
instance.class_null = validated_data['class_null']
instance.description = validated_data['description']
instance.save()
return instance
视图
def put(self,request):
data = {
'id': 2,
'name':'xx',
'age':8,
'sex':0,
'class_null':'9',
'description':'xx',
}
obj = models.Student.objects.get(id=data.get('id'))
s_obj = StudentSerizlizer(instance=obj,data=data) # 实例化序列化器类时,传入instance参数,
# 参数partial=True:只需要校验传入给序列化器的数据,适用于部分数据更新
if s_obj.is_valid():
s_obj.save() #触发序列化器类的update方法
else:
return JsonResponse({
'error':'数据错误'})
序列化与反序列化
1 定义一个序列化器,继承serializers.ModelSerializer:
from rest_framework import serializers
from students import models
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Student #声明对哪个模型类对象进行操作
fields = '__all__' #全部取出
# fields = ['id', 'name', 'age'] #只取id name age 字段
# exclude = ['name','age'] #除了name,age字段,其他的取出
extra_kwargs = {
#为字段添加额外的参数
'id':{
'read_only':True,},
'age':{
'write_only':True,}
}
2 视图文件:
from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
from django.views import View
from .serializers import StudentModelSerializer
from students import models
class StudentView(View):
def get(self,request):
all = models.Student.objects.all()
model_ser = StudentModelSerializer(instance=all,many=True)
return JsonResponse(model_ser.data, safe=False, json_dumps_params={
'ensure_ascii':False})
def post(self,request):
print(request.POST)
obj = StudentModelSerializer(data=request.POST)
if obj.is_valid():
print(obj.validated_data)
obj.save() #自动能够帮我们进行数据保存
return JsonResponse({
'msg':'success'})
print(obj.errors)
print(obj.validated_data)
return JsonResponse({
'xx':'ssssss'})
在get方法中request.GET<=>request.query_params
在post方法中:
当发送的数据格式为…urlencoded类型时,request.POST<=>request.data
当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据,得到的是普通字典类型。(如果是多选数据,不能使用getlist方法获取)
from rest_framework.views import APIView
class UserView(APIView):
def get(self,request):
print(request.GET)
print(request.query_params)
# request.GET和request.query_params是等价的
return JsonResponse({
'xx':'xxxxx'})
def post(self,request):
print(request)
# 当发送的数据格式为..urlencoded类型时,request.POST和request.data等价
# print(request.POST.getlist('hobby')) #
# 当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据
print(request.data) #{'username': 'xxxx', 'password': '123'},普通字典类型
# request.data能够获取到客户端发送过来的json类型数据,但是得到的结果为普通字典类型,但是如果是多选数据,不能使用getlist方法获取
return JsonResponse({
'xx': 'xxxxx'})
查看drf的默认配置
from rest_framework import settings
引入response
from rest_framework.response import Response
Response默认响应的是json数据类型
当用浏览器访问时看到页面效果.
用户配置替换drf内部配置的写法
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器API渲染器
)
}
通过浏览器访问:
注释浏览器API渲染器(得到的就是一个Json对象):
response的方法的相关参数
Response(data, status=None, template_name=None, headers=None, content_type=None)
data: 响应的序列化数据。
status: 响应的状态代码。默认为200。
template_name: 选择 HTMLRenderer 时使用的模板名称。
headers: 设置 HTTP header,字典类型。
content_type: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。
Response 可以将内置类型转换成字符串。写到data的位置
from rest_framework import status
# return Response({'xx':'xxxxx'}, status=201)
return Response({
'xx':'xxxxx'}, status=status.HTTP_201_CREATED)
所有状态码:
HTTP_100_CONTINUE = 100
HTTP_101_SWITCHING_PROTOCOLS = 101
HTTP_200_OK = 200
HTTP_201_CREATED = 201
HTTP_202_ACCEPTED = 202
HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203
HTTP_204_NO_CONTENT = 204
HTTP_205_RESET_CONTENT = 205
HTTP_206_PARTIAL_CONTENT = 206
HTTP_207_MULTI_STATUS = 207
HTTP_208_ALREADY_REPORTED = 208
HTTP_226_IM_USED = 226
HTTP_300_MULTIPLE_CHOICES = 300
HTTP_301_MOVED_PERMANENTLY = 301
HTTP_302_FOUND = 302
HTTP_303_SEE_OTHER = 303
HTTP_304_NOT_MODIFIED = 304
HTTP_305_USE_PROXY = 305
HTTP_306_RESERVED = 306
HTTP_307_TEMPORARY_REDIRECT = 307
HTTP_308_PERMANENT_REDIRECT = 308
HTTP_400_BAD_REQUEST = 400
HTTP_401_UNAUTHORIZED = 401
HTTP_402_PAYMENT_REQUIRED = 402
HTTP_403_FORBIDDEN = 403
HTTP_404_NOT_FOUND = 404
HTTP_405_METHOD_NOT_ALLOWED = 405
HTTP_406_NOT_ACCEPTABLE = 406
HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407
HTTP_408_REQUEST_TIMEOUT = 408
HTTP_409_CONFLICT = 409
HTTP_410_GONE = 410
HTTP_411_LENGTH_REQUIRED = 411
HTTP_412_PRECONDITION_FAILED = 412
HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_414_REQUEST_URI_TOO_LONG = 414
HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416
HTTP_417_EXPECTATION_FAILED = 417
HTTP_418_IM_A_TEAPOT = 418
HTTP_422_UNPROCESSABLE_ENTITY = 422
HTTP_423_LOCKED = 423
HTTP_424_FAILED_DEPENDENCY = 424
HTTP_426_UPGRADE_REQUIRED = 426
HTTP_428_PRECONDITION_REQUIRED = 428
HTTP_429_TOO_MANY_REQUESTS = 429
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451
HTTP_500_INTERNAL_SERVER_ERROR = 500
HTTP_501_NOT_IMPLEMENTED = 501
HTTP_502_BAD_GATEWAY = 502
HTTP_503_SERVICE_UNAVAILABLE = 503
HTTP_504_GATEWAY_TIMEOUT = 504
HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_506_VARIANT_ALSO_NEGOTIATES = 506
HTTP_507_INSUFFICIENT_STORAGE = 507
HTTP_508_LOOP_DETECTED = 508
HTTP_509_BANDWIDTH_LIMIT_EXCEEDED = 509
HTTP_510_NOT_EXTENDED = 510
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511
基于APIView的五个接口
get--查询所有
post--提交
get(...pk)--查询单条数据
put(...pk)--更新单条数据
delete(...pk)--删除单条数据
新建一个应用,定义一个序列器,
from rest_framework import serializers
from app01 import models
class StudentModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Student
fields = '__all__'
路由:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student1/', views.StudentViews11.as_view()),
re_path(r'student1/(?P\d+)/' , views.StudentViews12.as_view()),
]
视图:
from rest_framework.views import APIView
from app01 import models
from .serializers import StudentModelSerializer
from rest_framework.response import Response
from rest_framework import status
# Create your views here.
class StudentViews11(APIView):
def get(self,request):
all = models.Student.objects.all()
ser = StudentModelSerializer(instance=all,many=True)
return Response(ser.data)
def post(self,request):
data = request.data
ser = StudentModelSerializer(data=data)
if ser.is_valid():
instance = ser.save()
ser = StudentModelSerializer(instance=instance)
return Response(ser.data,status=status.HTTP_201_CREATED)
return Response(ser.errors)
class StudentViews12(APIView):
def get(self,request,pk):
one = models.Student.objects.get(pk=pk)
ser = StudentModelSerializer(instance=one)
return Response(ser.data)
def put(self,request,pk):
data = request.data
one = models.Student.objects.get(pk=pk)
ser = StudentModelSerializer(instance=one,data=data,partial=True)
if ser.is_valid():
instance = ser.save()
ser = StudentModelSerializer(instance=instance)
return Response(ser.data)
return Response(ser.errors)
def delete(self,request,pk):
models.Student.objects.get(pk=pk).delete()
return Response(None,status=status.HTTP_204_NO_CONTENT)
简单来说,相比与APIView,GenericAPIView拓展了:
1 可以指定使用的数据查询集。属性:queryset
通过self.get_queryset()–>获取所有查询集
通过self.get_object()–>获取单个查询对象,(传入的参数是pk,可通过lookup_field修改)
2 可以指定使用的序列器。属性:serializer_class
通过self.get_serializer()–>调用序列器
通过get_serializer_class,控制不同条件下,使用不同的序列化器类
def get_serializer_class(self):
if self.request.method == 'GET':
return Student2Serializer
else:
return StudentSerializer
实例(用GenericAPIView实现五个接口):
路由:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student1/', views.StudentViews11.as_view()),
re_path(r'student1/(?P\d+)/' , views.StudentViews12.as_view()),
path('student2/', views.StudentViews21.as_view()),
re_path(r'student2/(?P\d+)/' , views.StudentViews22.as_view()),
]
视图:
from rest_framework.generics import GenericAPIView
class StudentViews21(GenericAPIView):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
def get(self,request):
ser = self.get_serializer(instance=self.get_queryset(),many=True)
return Response(ser.data)
def post(self,request):
data = request.data
ser = self.get_serializer(data=data)
if ser.is_valid():
instance = ser.save()
ser = self.get_serializer(instance=instance)
return Response(ser.data,status=status.HTTP_201_CREATED)
return Response(ser.errors)
class StudentViews22(GenericAPIView):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
def get(self,request,pk):
ser = self.get_serializer(instance=self.get_object())
return Response(ser.data)
def put(self,request,pk):
ser = self.get_serializer(instance=self.get_object(),data=request.data,partial=True)
if ser.is_valid():
instance = ser.save()
ser = self.get_serializer(instance=instance)
return Response(ser.data)
return Response(ser.errors)
def delete(self,requset,pk):
self.get_object().delete()
return Response(None,status=status.HTTP_204_NO_CONTENT)
需要配合GenericAPIView使用,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法
相比于GenericAPIView,Mixin拓展封装了:调用序列器序列化与反序列化的过程
ListModelMixin:self.list(request)
CreateModelMixin:self.create(request)
RetrieveModelMixin:self.retrieve(request,pk)
UpdateModelMixin:self.update(request,pk)
DestroyModelMixin:self.destroy(request,pk)
实例(实现五个接口):
urls.py:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student1/', views.StudentViews11.as_view()),
re_path(r'student1/(?P\d+)/' , views.StudentViews12.as_view()),
path('student2/', views.StudentViews21.as_view()),
re_path(r'student2/(?P\d+)/' , views.StudentViews22.as_view()),
path('student3/', views.StudentViews31.as_view()),
re_path(r'student3/(?P\d+)/' , views.StudentViews32.as_view()),
]
views.py:
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class StudentViews31(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class StudentViews32(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
def get(self,request,pk):
return self.retrieve(request,pk)
def put(self,request,pk):
return self.update(request,pk)
def delete(self,request,pk):
return self.destroy(request,pk)
ListAPIView:get请求,继承 GenericAPIView、ListModelMixin
CreateAPIView:post请求,继承GenericAPIView、CreateModelMixin
RetrieveAPIView:带参数的get请求,继承GenericAPIView、RetrieveModelMixin
UpdateAPIView:put请求,继承GenericAPIView、UpdateModelMixin
DestroyAPIView:delete请求,继承GenericAPIView、DestoryModelMixin
路由:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student1/', views.StudentViews11.as_view()),
re_path(r'student1/(?P\d+)/' , views.StudentViews12.as_view()),
path('student2/', views.StudentViews21.as_view()),
re_path(r'student2/(?P\d+)/' , views.StudentViews22.as_view()),
path('student3/', views.StudentViews31.as_view()),
re_path(r'student3/(?P\d+)/' , views.StudentViews32.as_view()),
path('student4/', views.StudentViews41.as_view()),
re_path(r'student4/(?P\d+)/' , views.StudentViews42.as_view()),
]
视图:
from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
class StudentViews41(ListAPIView,CreateAPIView):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
class StudentViews42(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。
路由:
from use_view import views
urlpatterns = [
path('student4/', views.StudentViews41.as_view()),
re_path(r'student4/(?P\d+)/' , views.StudentViews42.as_view()),
path('student5/', views.StudentView5.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student5/(?P\d+)/' , views.StudentView5.as_view({
'get': 'get_one'})),
]
视图:
from rest_framework.viewsets import ViewSet
class StudentView5(ViewSet):
def get_all_student(self,request):
all = models.Student.objects.all()
ser = StudentModelSerializer(instance=all,many=True)
return Response(ser.data)
def add_student(self,request):
data = request.data
ser = StudentModelSerializer(data=data)
if ser.is_valid():
instance = ser.save()
ser = StudentModelSerializer(instance=instance)
return Response(ser.data,status=status.HTTP_201_CREATED)
return Response(ser.errors)
def get_one(self,request,pk):
one = models.Student.objects.get(pk=pk)
ser = StudentModelSerializer(instance=one)
return Response(ser.data)
相比于ViewSet,GenericViewSet拓展了queryset,serializer_class属性
路由:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student4/', views.StudentViews41.as_view()),
re_path(r'student4/(?P\d+)/' , views.StudentViews42.as_view()),
path('student5/', views.StudentView5.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student5/(?P\d+)/' , views.StudentView5.as_view({
'get': 'get_one'})),
path('student6/', views.StudentView6.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student6/(?P\d+)/' , views.StudentView6.as_view({
'get': 'get_one'})),
]
视图:
from rest_framework.viewsets import GenericViewSet
class StudentView6(GenericViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
def get_all_student(self,request):
all = self.get_queryset()
ser = self.get_serializer(instance=all,many=True)
return Response(ser.data)
def add_student(self,request):
data = request.data
ser = self.get_serializer(data=data)
if ser.is_valid():
instance = ser.save()
ser = self.get_serializer(instance=instance)
return Response(ser.data,status=status.HTTP_201_CREATED)
return Response(ser.errors)
def get_one(self,request,pk):
one = self.get_object()
ser = self.get_serializer(instance=one)
return Response(ser.data)
路由:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student4/', views.StudentViews41.as_view()),
re_path(r'student4/(?P\d+)/' , views.StudentViews42.as_view()),
path('student5/', views.StudentView5.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student5/(?P\d+)/' , views.StudentView5.as_view({
'get': 'get_one'})),
path('student6/', views.StudentView6.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student6/(?P\d+)/' , views.StudentView6.as_view({
'get': 'get_one'})),
path('student7/', views.StudentView7.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student7/(?P\d+)/' , views.StudentView7.as_view({
'get': 'get_one','put':'update_one','delete':'delete_one'})),
]
视图:
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class StudentView7(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
def get_all_student(self, request):
return self.list(request)
def add_student(self, request):
return self.create(request)
def get_one(self, request, pk):
return self.retrieve(request, pk)
def update_one(self, request, pk):
return self.update(request, pk)
def delete_one(self, request, pk):
return self.destroy(request, pk)
在路由中直接定义:{‘get’: ‘list’, ‘post’: ‘create’…},可以简化代码省去类中的自定义方法
ModelViewSet:继承了GenericViewSet与mixins,故代码如下:
路由:
from django.urls import path, re_path
from use_view import views
urlpatterns = [
path('student7/', views.StudentView7.as_view({
'get': 'get_all_student', 'post': 'add_student'})),
re_path(r'student7/(?P\d+)/' , views.StudentView7.as_view({
'get': 'get_one','put':'update_one','delete':'delete_one'})),
path('student8/', views.StudentView8.as_view({
'get': 'list', 'post': 'create'})),
re_path(r'student8/(?P\d+)/' , views.StudentView8.as_view({
'get':'retrieve','put':'update','delete':'destroy'})),
]
视图:
from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
作用:将视图中类的路径自动注册
from rest_framework import routers
router = routers.DefaultRouter()
#router = routers.SimpleRouter()
router.register('dd',views.StudentView8)
urlpatterns += router.urls
用于自定义的方法
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
class StudentModelViewSet(ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# methods 设置当前方法允许哪些http请求访问当前视图方法
# detail 设置当前视图方法是否是操作一个数据
# detail为True,表示路径名格式应该为 router_stu/{pk}/login/
@action(methods=['get'], detail=True)
def login(self, request,pk):
"""登录"""
...
# detail为False 表示路径名格式应该为 router_stu/get_new_5/
@action(methods=['put'], detail=False)
def get_new_5(self, request):
"""获取最新添加的5个学生信息"""
...
python manage.py createsuperuser
可以在setting文件中配置后台管理系统为中文
LANGUAGE_CODE = 'zh-hans'
访问http://127.0.0.1:8000/admin就可以看到后台admin站点
admin后台管理系统简单使用:
在应用的admin.py文件中
from app01 import models
class StudentAdmin(admin.ModelAdmin):
list_display = ['id','name','age','class_null'] #展示哪些字段
list_editable = ['name','age'] #可编辑哪些字段
admin.site.register(models.Student,StudentAdmin)
url:
path('admin/', admin.site.urls),
1 在配置文件中配置全局默认的认证方案
该认证方案与后台管理系统是一套系统。
在setting文件中可以配置:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
#哪个写在前面,优先使用哪个认证
'rest_framework.authentication.SessionAuthentication', # session认证,admin后台其实就使用的session认证,其实接口开发很少用到session认证,所以我们通过配置可以改为其他认证,比如后面项目里面我们用到jwt,JSON WEB TOKEN认证,或者一些配合redis的认证
'rest_framework.authentication.BasicAuthentication', # 基本认证,工作当中可能一些测试人员会参与的话,他们会将一些认证数据保存在内存当中,然后验证的,我们基本上用不上
),
}
2 在当前应用下新建一个文件夹,utils/auth.py
from rest_framework.authentication import BaseAuthentication #引入基础的认证rest_framework基础的认证类
from rest_framework.exceptions import AuthenticationFailed #引入认证失败的错误
class Auth(BaseAuthentication):
def authenticate(self, request): #authenticate可以扩展的方法
username = 'root' #request为请求对象
if username == 'root':
return 'root','xx'
else:
raise AuthenticationFailed('登录失败!!')
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
#哪个写在前面,优先使用哪个认证
'use_view.utils.auth.Auth', #路径,配置的是全局
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
}
3.2 配置局部生效。在局部视图中定义authentication_classes属性
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
from use_view.utils.auth import Auth
class StudentView7(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
authentication_classes = [Auth, ] #在本视图类中生效
def get_all_student(self, request):
print(request.user)
print(request.auth)
return self.list(request)
def add_student(self, request):
return self.create(request)
def get_one(self, request, pk):
return self.retrieve(request, pk)
def update_one(self, request, pk):
return self.update(request, pk)
def delete_one(self, request, pk):
return self.destroy(request, pk)
权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
1 全局配置:在setting文件中配置REST_FRAMEWORK
REST_FRAMEWORK = {
....
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', #登录状态下才能访问我们的接口,可以通过退出admin后台之后,你看一下还能不能访问我们正常的接口就看到效果了
)
}
AllowAny 允许所有用户,未指明时默认为允许所有用户
from rest_framework import settings可以看出
IsAuthenticated 仅通过认证的用户
IsAdminUser 仅管理员用户(可以通过admin创建一个用户进行测试)
IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。
2 局部配置:在视图中添加属性permission_classes
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
class StudentAPIView(RetrieveAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部
.has_permission(self, request, view)
是否可以访问视图, view表示当前视图对象
.has_object_permission(self, request, view, obj)
是否可以访问数据对象, view表示当前视图, obj为数据对象
例如:
1 在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类:
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
def has_permission(self, request, view):
if request.user.username == 'xx':
return True
return False
2 在视图中添加属性
from rest_framework.viewsets import ModelViewSet
from .utils.auth import MyPermission
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
permission_classes = [MyPermission]
可以对接口访问的频次进行限制,以减轻服务器压力。
一般用于付费购买次数,投票等场景使用.
在setting文件中配置REST_FRAMEWORK
REST_FRAMEWORK = {
...
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', # 匿名用户,未登录的
'rest_framework.throttling.UserRateThrottle' # 经过登录之后的用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '5/m',
'user': '3/m'
}
}
#{'s': 1, 'm': 60, 'h': 3600, 'd': 86400} m表示分钟,可以写m,也可以写minute
1 在setting文件中配置REST_FRAMEWORK
'DEFAULT_THROTTLE_RATES': {
'anon': '5/m',
'user': '3/m',
}
在某个视图中添加throttle_classess属性来配置
from rest_framework.viewsets import ModelViewSet
from rest_framework.throttling import UserRateThrottle
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
throttle_classes = [UserRateThrottle,]
可选限流类
1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用DEFAULT_THROTTLE_RATES['anon']
来设置频次
2)UserRateThrottle
限制认证用户,使用User id 来区分。
使用DEFAULT_THROTTLE_RATES['user']
来设置频次
3)ScopedRateThrottle (待定…)
限制用户对于每个视图的访问频次,使用ip或user id,先找的用户id,没有设置用户id的话就会使用ip地址。
例如: 全局
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
-------------------------------------------------
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
1 setting文件中配置
'DEFAULT_THROTTLE_RATES': {
'anon': '5/m',
'user': '3/m',
'xx': '3/m',
}
2 视图
from rest_framework.viewsets import ModelViewSet
from .utils.auth import MyPermission
from rest_framework.throttling import ScopedRateThrottle
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
permission_classes = [MyPermission]
throttle_classes = [ScopedRateThrottle,]
throttle_scope = 'xx'
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-filter扩展来增强支持。
安装django-filter:
pip3 install django-filter
1 配置setting文件(注册应用,配置REST_FRAMEWORK)
INSTALLED_APPS = [
...
'rest_framework',
'app01',
'use_serializers',
'use_unserializers',
'use_view',
'django_filters', #注册应用
]
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
2 视图:增加filter_fields属性,将需要过滤的字段传参。
from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
filter_fields = ('age','sex','id') #增加filter_fields属性
访问:http://127.0.0.1:8000/use03/student8/?id=1即可得到id为1的值
对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。
在视图中
1 添加filter_backends属性(使用OrderingFilter过滤器)
2 指定字段,通过ordering_fields属性指定需要按某字段排序
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import OrderingFilter
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
filter_backends = [OrderingFilter]
ordering_fields = ('id','age')
在网址中输入
http://127.0.0.1:8000/use03/student8/?ordering=-id(按id字段倒叙)
在视图中
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
filter_fields = ('age','sex','id')
# 因为filter_backends是局部过滤配置,局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
# 否则过滤功能会失效
filter_backends = [OrderingFilter,DjangoFilterBackend]
ordering_fields = ('id','age')
如不加声明,过滤功能会失效:
REST framework提供了分页的支持。
在setting文件中配置REST_FRAMEWORK(一般不用,list方法都会触发)
REST_FRAMEWORK = {
# 全局分页,一旦设置了全局分页,那么我们drf中的视图扩展类里面的list方法提供的列表页都会产生分页的效果。所以一般不用全局分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页最大数据量
}
在视图中关闭分页功能,只需在视图中添加属性pagination_class
pagination_class = None
在视图中定义一个类,继承PageNumberPagination
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
class LargeResultsSetPagination(PageNumberPagination):
#page_size = 2 #默认每一页显示的数据量
# http://127.0.0.1:8000/use03/student8/?pp=1&size=3
page_query_param = 'pp' # 自定义页码的参数名
page_size_query_param = 'size' #允许客户端通过get参数来控制每一页的数据量
max_page_size = 5 #客户端通过size指定获取数据的条数时,最大不能超过多少
from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
pagination_class = LargeResultsSetPagination
就是通过偏移量(索引)来取数据
如:GET http://127.0.0.1/four/students/?limit=100&offset=400 #从下标为400的记录开始,取100条记录
from rest_framework.pagination import LimitOffsetPagination
class LargePagination(LimitOffsetPagination): #其实就是通过偏移量来取数据
default_limit = 3 #一页显示3条
from rest_framework.viewsets import ModelViewSet
class StudentView8(ModelViewSet):
queryset = models.Student.objects.all()
serializer_class = StudentModelSerializer
pagination_class = LargePagination
参数说明:
1. default_limit 默认限制,每页数据量大小,默认值与`PAGE_SIZE`设置一致
2. limit_query_param limit参数名,默认'limit' , 可以通过这个参数来改名字
3. offset_query_param offset参数名,默认'offset' ,可以通过这个参数来改名字
4. max_limit 最大limit限制,默认None, 无限制
先看一个简单的实例:
视图:
class ApiError(Exception):
pass
class Test(APIView):
def get(self,request,pk):
try:
one = models.Student.objects.get(pk=pk)
except models.Student.DoesNotExist:
raise ApiError('查询出错!!!')
ser = StudentModelSerializer(instance=one)
return Response(ser.data)
当索引超出范围后,会抛出错误:
在REST framework中提供了异常处理,我们可以自定义异常处理函数,来捕获异常反馈前端
1 在应用下新建一个文件exceptions.py,
from rest_framework.views import exception_handler
from .views import ApiError
from rest_framework.response import Response
def custom_exception_handler(exc, context): #自定义的错误处理函数
# 先调用REST framework默认的异常处理方法获得标准错误响应对象
response = exception_handler(exc, context) #这个函数是drf提供的,它处理了一些错误,但是如果它处理不了的,它会返回None,所以,如果是None的话,我们需要自己来处理错误
# 在此处补充自定义的异常处理
if response is None:
if isinstance(exc,ApiError):
#这里就可以记录错误信息了,一般记录到文件中,可以使用日志系统来进行记录
return Response({
'msg':'自定义API错误了'})
#response.data['status_code'] = response.status_code
return response
2 在setting文件中配置REST_FRAMEWORK
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
这样就完成了自定义异常的捕获:
同理,补充上处理关于数据库的异常:
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
if response is None:
view = context['view'] #出错的方法或者函数名称
if isinstance(exc, DatabaseError):
print('[%s]: %s' % (view, exc))
response = Response({
'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response
也就是说,上面列出来的异常不需要我们自行处理了,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。
需要3步:
1 安装依赖,REST framewrok生成接口文档需要coreapi
库的支持。
pip3 install coreapi
2 配置路由
from django.urls import path, include #path:直接写路径,re_path:可以写正则匹配
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
path('docs/', include_docs_urls(title='站点页面标题'))
]
3 配置setting文件REST_FRAMEWORK
'DEFAULT_SCHEMA_CLASS': "rest_framework.schemas.AutoSchema"
这样一个接口文档就自动生成了。
1) 单一方法的视图,可直接使用类视图的文档字符串,如
class BookListView(generics.ListAPIView):
"""
get: 返回所有图书信息.
post: 添加记录
"""
#注意,这是在类中声明的注释,如果在方法中你声明了其他注释,会覆盖这个注释的
2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如
class BookListCreateView(generics.ListCreateAPIView):
"""
get:
返回所有图书信息.
post:
新建图书.
"""
3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
"""
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
"""
4) 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read
5)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义
或 注意,如果你多个应用使用同一个序列化器,可能会导致help_text的内容显示有些问题,小事情
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = "__all__"
extra_kwargs = {
'age': {
'required': True,
'help_text': '年龄'
}
}
需要4步,如下:
1 通过如下命令安装xadmin的最新版。
pip3 install https://codeload.github.com/sshwsfc/xadmin/zip/django2
2 在setting配置文件中注册。
INSTALLED_APPS = [
...
'xadmin',
'crispy_forms',
'reversion',
...
]
# 修改使用中文界面
LANGUAGE_CODE = 'zh-Hans'
# 修改时区
TIME_ZONE = 'Asia/Shanghai'
3 数据库迁移。xadmin有建立自己的数据库模型类,需要进行数据库迁移
python manage.py makemigrations
python manage.py migrate
4 添加路由。在总路由中添加
import xadmin
xadmin.autodiscover()
# version模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()
urlpatterns = [
path(r'xadmin/', xadmin.site.urls),
]
另外,如果之前没有创建超级用户,需要创建,如果有了,则可以直接使用之前的
python manage.py createsuperuser
admin.ModelAdmin
,而是直接继承object
即可。例如:在子应用中创建adminx.py文件。
from xadmin import views
from app01 import models
class BaseSetting(object):
"""xadmin的基本配置"""
enable_themes = True # 开启主题切换功能
use_bootswatch = True # 引导控制盘(其实就是我们的左侧菜单栏)
xadmin.site.register(views.BaseAdminView, BaseSetting)
class GlobalSettings(object):
"""xadmin的全局配置"""
site_title = "路飞学城" # 设置站点标题
site_footer = "路飞学城有限公司" # 设置站点的页脚
menu_style = "accordion" # 设置菜单折叠
xadmin.site.register(views.CommAdminView, GlobalSettings)
class StudentXadmin(object):
list_display = ['id','name','age','class_null']
list_editable = ['name','age']
xadmin.site.register(models.Student,StudentXadmin)
xadmin可以使用的页面样式控制基本与Django原生的admin一直。
可以在models类中定义个__str__
方法来定义对象显示成什么内容
list_display 控制列表展示的字段
list_display = ['id', 'btitle', 'bread', 'bcomment']
search_fields 控制可以通过搜索框搜索的字段名称,xadmin使用的是模糊查询
search_fields = ['id','btitle']
list_filter 可以进行过滤操作的列,对于分类、性别、状态
list_filter = ['is_delete']
ordering 默认排序的字段
ordering = ['-age',] #-倒序
show_detail_fields 在列表页提供快速显示详情信息
show_detail_fields = ['id',]
list_editable 在列表页可以快速直接编辑的字段
list_editable = ['name','age',]
refresh_times 指定列表页的定时刷新
refresh_times = [5, 10,30,60] # 设置允许后端管理人员按多长时间(秒)刷新页面,选好之后就能自动刷新了
list_export 控制列表页导出数据的可选格式
list_export = ('xls', 'json','csv')#写元祖或者列表都行 list_export设置为None来禁用数据导出功能
list_export_fields = ('id', 'btitle', 'bpub_date') #设置允许导出的字段
show_bookmarks 控制是否显示书签功能
show_bookmarks = True #False就隐藏了这个功能
data_charts 控制显示图表的样式
data_charts = {
"order_amount": {
#随便写的名称order_amount
'title': '图书发布日期表',
"x-field": "bpub_date",
"y-field": ('btitle',),
"order": ('id',),
},
# 支持生成多个不同的图表
# "order_amount2": {
# 'title': '图书发布日期表',
# "x-field": "bpub_date",
# "y-field": ('btitle',),
# "order": ('id',)
# },
}
model_icon 控制菜单的图标【图标的设置可以参考font-awesome的图标css名称】
model_icon = 'fa fa-gift'
readonly_fields 在编辑页面的只读字段
readonly_fields = ['name',]
exclude 在编辑页面隐藏的字段,比如判断这个数据是否删除的delete_status字段,一般就是用来标识一下字段是不是被删除了,但是数据库中不删除
exclude = ['name',]
这并不是所有功能,可以参看它的文档,它提供的一些功能我们可能还需要自定制,调整或者添加一些它没有的功能,后面再说