DRF框架入门笔记(一)有哪些要点?
- 内容介绍
- 文章标签
- 相关推荐
本文共计8848个文字,预计阅读时间需要36分钟。
目录+ + Restful规范 + + Django Rest Framework 3 + + serializers.Serializer + + serializers.ModelSerializer + + API FBV + + drf的request和response
目录
restful规范: 2
django rest framework 3
serializers.Serializer 5
serializers.ModelSerializer: 7
api FBV: 8
drf的request和response 10
rest_framework.decorators.api_view和rest_framework.views.APIView: 13
api CBV,rest_framework.views.APIView: 14
api CBV,使用rest_framework.mixins: 15
api CBV,ListAPIView|CreateAPIView|RetrieveAPIView|DestroyAPIView|UpdateAPIView|ListCreateAPIView 16
rest_framework.viewsets和rest_framework.routers.DefaultRouter: 18
分页: 20
认证: 21
授权: 26
面试题目: 27
django请求生命周期 27
csrf 29
权限 32
节流 33
版本 35
解析器: 37
序列化 38
序列化-序列化: 39
序列化-请求数据校验: 45
渲染 47
分页 49
视图 52
路由 55
content-type 57
drf的api文档自动生成: 60
drf对象级别的权限支持: 61
drf的过滤: 61
使用方式1: 62
使用方式2: 63
例,多IP查询: 63
drf的搜索和排序 63
例,排序(django_filters.OrderingFilter): 67
例,排序(rest_framework.filters.OrderingFilter): 68
例,高级搜索: 68
drf的token 69
jwt方式,用户认证 70
drf的缓存: 71
social_django实现第三方登录 72
Tokne的设计及加密方法 73
系统之间为了调用数据,用api;
格式:
json;
xml;
给用户提供一个接口前:
1.跟前端进行交互,确定前端要什么;
2.把需求写个文档;
restful规范:
restful api是用于前端和后台进行通信的一套规范,使用这个规范可以让前后端开发变得更加轻松;
数据传输格式,都用json,而不是xml;
url链接,只能有名词,不能有动词,名词的复数要加s;
restful是一种软件架构风格或者说是一种设计风格,并不是标准,它只是提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。
就像设计模式一样,并不是一定要遵循这些原则,而是基于这个风格设计的软件可以更简洁,更有层次,我们可以根据开发的实际情况,做相应的改变。
如一些规范:
1、restful提倡面向资源编程,在url接口中尽量要使用名词,不要使用动词;
2、在url接口中推荐使用www.bootcss.com/v1/mycss?page=3;
3、在url中可以体现版本号,v1.bootcss.com/mycss,不同的版本可以有不同的接口,使其更加简洁,清晰;
4、url中可以体现是否是API接口,www.bootcss.com/api/mycss;
5、url中可以添加条件去筛选匹配,www.bootcss.com/v1/mycss?page=3;
6、可以根据Http不同的method,进行不同的资源操作,5种方法:GET|POST|PUT|DELETE|PATCH;
7、响应式应该设置状态码;
8、有返回值,而且格式为统一的json格式;
9、返回错误信息,返回值携带错误信息;
10、返回结果中要提供帮助链接,即API最好做到Hypermedia,如果遇到需要跳转的情况携带调转接口的URL;
ret = {
code: 1000,
data:{
id:1,
小强',
depart_id:www.luffycity.com/api/v1/depart/8/
}
}
状态码 原生描述 描述
200 OK 服务器成功响应客户端的请求
400 INVALID REQUEST 用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized 用户没有权限访问这个请求
403 Forbidden 因为某些原因禁止访问这个请求
404 NOT FOUND 用户发送的请求的url不存在
405 Method not allowed 方法不被允许
406 NOT Accept 用户请求不被服务器接收(比如服务器期望客户端发送某个字段,但没有发送)
500 Internal server error 服务器内部错误,比如出现了bug
204 No Content
服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息,响应可能通过实体头部的形式,返回新的或更新后的元信息,如果存在这些头部信息,则应当与所请求的变量相呼应;
如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档;
由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾;
django rest framework
是django用来restful api的框架,风格完全和django一样,使用起来像是django本身提供的;
www.django-rest-framework.org/
(webproject) C:\webproject\mysite>pip install djangorestframework另coreapi,drf的文档支持;django-guardian,drf对象级别的权限支持
INSTALLED_APPS = [
'rest_framework',
用djangorestframework:
能自动生成符合 RESTful 规范的 API
1.在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本一样,这部分的代码可以简写;
2.在序列化与反序列化时,虽然操作的数据不同,但是执行的过程却相似,这部分的代码也可以简写;
REST framework可以帮助简化上述两部分的代码编写,大大提高REST API的开发速度
组件:
1.序列化组件:serializers,对queryset序列化|对请求数据反序列化并格式校验;
2.路由组件routers ,进行路由分发;
3.视图组件ModelViewSet,帮助开发者提供了一些类,并在类中提供了多个方法;
4.认证组件,写一个类并注册到认证类(authentication_classes),在类的的authticate方法中编写认证逻辑;
5.权限组件,写一个类并注册到权限类(permission_classes),在类的的has_permission方法中编写认证逻辑,
6.频率限制,写一个类并注册到频率类(throttle_classes),在类的的allow_request|wait方法中编写认证逻辑;
7.解析器,选择对数据解析的类,在解析器类中注册(parser_classes);
8.渲染器,定义数据如何渲染到到页面上,在渲染器类中注册(renderer_classes);
9.分页,对获取到的数据进行分页处理,pagination_class;
10.版本,版本控制用来在不同的客户端使用不同的行为,在url中设置version参数,用户请求时候传入参数,在request.version中获取版本,根据版本不同做不同处理;
django rest framework框架中的视图都可以继承哪些类?
class View(object):
class APIView(View): #封装了view,并且重新封装了request,初始化了各种组件
class GenericAPIView(views.APIView):增加了一些属性和方法,如get_queryset,get_serializer
class GenericViewSet(ViewSetMixin, generics.GenericAPIView)父类ViewSetMixin 重写了as_view,返回return csrf_exempt(view),并重新设置请求方式与执行函数的关系
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):pass继承了mixins下的一些类,封装了list,create,update等方法和GenericViewSet
简述 django rest framework框架的认证流程
1.用户请求走进来后,走APIView,初始化了默认的认证方法
2.走到APIView的dispatch方法,initial方法调用了request.user
3.如果我们配置了认证类,走我们自己认证类中的authentication方法
django rest framework如何实现的用户访问频率控制
使用IP|用户账号,作为键,每次的访问时间戳作为值,构造一个字典形式的数据,存起来,每次访问时对时间戳列表的元素进行判断,把超时的删掉,再计算列表剩余的元素数就能做到频率限制了
匿名用户,使用IP控制,但是无法完全控制,因为用户可以换代理IP登录用户;使用账号控制,但是如果有很多账号,也无法限制;
rest_framework序列化组件的作用,以及一些外键关系的钩子方法
作用:帮助我们序列化数据
1.choices get_字段名_display
2.ForeignKey source=orm 操作
3.ManyToManyFiled SerializerMethodField()
字段名():
自定义
serializers.Serializer
from rest_framework import serializers
class Serializer(BaseSerializer):
class ModelSerializer(Serializer):
class HyperlinkedModelSerializer(ModelSerializer):
rest_framework.serializers.Serializer和django.forms.Form:
功能一样,都是将用户提交的数据转为py数据结构,可用此来完成校验;
Form用在post提交中的form表单;
Serializer用在用户提交的json;
另,
from django.core import serializers的serializer虽可简单实现序列化,但有2个缺点,1字段序列化定死的,要想重组很麻烦,2images保存的是一个相对路径还需要补全,drf可自动补全
def serialize(format, queryset, **options): #serializers.serialize
例:
from rest_framework import serializers
from .models import Author
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
first_name = serializers.CharField(max_length=100)
last_name = serializers.CharField(required=False, allow_blank=True, max_length=100)
email = serializers.EmailField(required=False, allow_blank=True, max_length=100)
def create(self, validated_data):
return Author.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.email = validated_data.get('email', instance.email)
instance.save()
return instance
底层实现:
>>> from books.serializer import AuthorSerializer
>>> from rest_framework.renderers import JSONRenderer #py结构-->json
>>> from rest_framework.parsers import JSONParser #json-->py结构
>>> from books.models import Author
>>> Author.objects.all()
<QuerySet [<Author: chai jowin>, <Author: dsfdfd sfdsfdsf>]>
>>> a = Author.objects.get(id=1)
>>> s = AuthorSerializer(a)
>>> s.data #dict
{'last_name': 'jowin', 'email': 'jowin@chai.com', 'id': 1, 'first_name': 'chai'}
>>> content = JSONRenderer().render(s.data)
>>> content
b'{"id":1,"first_name":"chai","last_name":"jowin","email":"jowin@chai.com"}'
>>> from django.utils.six import BytesIO
>>> stream = BytesIO(content)
>>> stream
<_io.BytesIO object at 0x00000000046389E8>
>>> data = JSONParser().parse(stream)
>>> data
{'last_name': 'jowin', 'email': 'jowin@chai.com', 'id': 1, 'first_name': 'chai'}
>>> s = AuthorSerializer(data=data)
>>> s
AuthorSerializer(data={'last_name': 'jowin', 'email': 'jowin@chai.com', 'id': 1, 'first_name': 'chai'}):
id = IntegerField(read_only=True)
first_name = CharField(max_length=100)
last_name = CharField(allow_blank=True, max_length=100, required=False)
email = EmailField(allow_blank=True, max_length=100, required=False)
>>> s.is_valid()
True
>>> s.validated_data返回ordereddict
OrderedDict([('first_name', 'chai'), ('last_name', 'jowin'), ('email', 'jowin@chai.com')])
>>> s.save()
<Author: chai jowin>
>>> Author.objects.all()
<QuerySet [<Author: chai jowin>, <Author: dsfdfd sfdsfdsf>, <Author: chai jowin>]>
>>> s = AuthorSerializer(Author.objects.all(), many=True)序列化多个对象时要加many=True,调用many_init()
>>> s.data
[OrderedDict([('id', 1), ('first_name', 'chai'), ('last_name', 'jowin'), ('email', 'jowin@chai.com')]), OrderedDict([('id', 2), ('first_name', 'dsfdfd'), ('la
st_name', 'sfdsfdsf'), ('email', 'dsfdsf@dsfdsf.com')]), OrderedDict([('id', 3), ('first_name', 'chai'), ('last_name', 'jowin'), ('email', 'jowin@chai.com')])
]
serializers.ModelSerializer:
rest_framework.serializers.ModelSerializer同django.forms.ModelForm;
class ModelSerializer(Serializer):
serializer_field_mapping = {
models.AutoField: IntegerField,
models.BigIntegerField: IntegerField,
models.BooleanField: BooleanField,
models.CharField: CharField,
models.CommaSeparatedIntegerField: CharField,
models.DateField: DateField,
models.DateTimeField: DateTimeField,
models.DecimalField: DecimalField,
models.EmailField: EmailField,
models.Field: ModelField,
models.FileField: FileField,
models.FloatField: FloatField,
models.ImageField: ImageField,
models.IntegerField: IntegerField,
models.NullBooleanField: NullBooleanField,
models.PositiveIntegerField: IntegerField,
models.PositiveSmallIntegerField: IntegerField,
models.SlugField: SlugField,
models.SmallIntegerField: IntegerField,
models.TextField: CharField,
models.TimeField: TimeField,
models.URLField: URLField,
models.GenericIPAddressField: IPAddressField,
models.FilePathField: FilePathField,
}
例:
class Serializer(serializers.ModelSerializer):
project_cname = serializers.ReadOnlyField(source='project_name.project_cname')
pool_cname = serializers.ReadOnlyField(source='pool_name.pool_cname')
project_name = serializers.IntegerField(source='project_name_id')
owner_name = serializers.ReadOnlyField(source='owner.user')
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', required=False)
class Meta:
model = AppConfig
fields = '__all__'
例:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'first_name', 'last_name', 'email']
api FBV:
例:
from django.www.django-rest-framework.org/api-guide/permissions/#api-reference
rest_framework内置权限:
AllowAny
IsAuthenticated
IsAdminUser
IsAuthenticatedOrReadOnly
DjangoModelPermissions
DjangoModelPermissionsOrAnonReadOnly
DjangoObjectPermissions
REST_FRAMEWORK = {
"PAGE_SIZE": 1,
"DEFAULT_AUTHENTICATION_CLASSES": (
# 'rest_framework.authentication.BasicAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.permissions.IsAuthenticated',全局配置默认权限
)
}
例,view中使用权限控制:
from rest_framework import generics
from rest_framework import permissions
class AuthorList(generics.ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
permission_classes = (permissions.IsAuthenticated,) #多个是and的关系
例,自定义权限:
from rest_framework import generics
from rest_framework import permissions
class IsSuperUser(permissions.BasePermission): #继承permissions.BasePermission,重写has_permission()
def has_permission(self, request, view):
return request.user.is_superuser()
class AuthorList(generics.ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
# permission_classes = (permissions.IsAuthenticated,)
permission_classes = (IsSuperUser,)
面试题目:
1、中间件
2、csrf原理及解决,解决1cors(响应头放在中间件),解决2jsonp
3、restful规范,10条谈,谈restful api规范的认识?10条,认识要与实际使用经验结合
4、面向对象
5、django请求生命周期
6、django请求生命周期(包含rest_framework),dispatch()
7、rest_framework,认证流程|权限流程|节流流程
8、FBV|CBV
面向对象:
封装 将属性和方法封装到类中,2通过构造方法把一部分数据封装到对象里,之后打包使用
继承 #若在2个类或多个类中有相同的属性和方法,可提取到1个类中实现;多继承,先左边后右边,广度优先|深度优先,经典类|新式类
多态 #1个对象点出来什么,就具有什么方法或属性
django请求生命周期(包含rest framework):
wsgi-->
中间件-->
as_view中view中dispatch-->
initialize_request()中封装request,get_parsers()获取解析器列表解析式,另get_authenticators()获取认证对象列表解析式-->
initial()中determine_version()处理版本,另perform_authentication(request)|check_permissions(request)|check_throttles(request)-->
反射执行对应HTTP方法
各组件按生命周期:
路由
版本
认证
权限
节流
通过反射执行视图(继承...),用解析器拿到数据
序列化,对数据校验并序列化
分页
渲染器
另,wsgiref|werkzeug|tornado|uwsgi是实现wsgi协议的一个模块,本质是一个socket服务器;
CBV
基于反射实现,根据请求方法不同,执行不同的方法;
流程:url-->view()-->dispatch()反射执行HTTP方法GET|POST|DELETE|PUT
csrf
基于中间件的process_view方法,装饰器@csrf_exempt(无需认证)或@csrf_protect(需认证)给单独函数进行设置;
另,jsonp解决csrf;
django csrf用中间件实现,1个中间件里最多可写5个方法:
process_request
process_view #csrf在此步验证的,1先检查函数是否@csrf_exempt,2去请求体中取cookie中的token
process_response
process_exception
process_render_template
使用中间件做过什么?适用于所有请求批量做操作
基于角色的权限控制;
用户登录验证;
csrf; #process_view方法检查视图是否被@csrf_exempt,去请求体cookie里获取token
session;
黑名单;
日志记录;
情况1:
settings.py中配置了全局csrf;
from django.views.decorators.csrf import csrf_exempt #@csrf_exempt被装饰的函数免除csrf验证
情况2:
settings.py中注释掉了csrf;
from django.views.decorators.csrf import csrf_protect被装饰的函数启用csrf验证
CBV中解决使用csrf:
要用method_decorator(csrf_exempt),且要在dispatch上;
from django.utils.decorator import method_decorator
class StudentViews(View):
@method_decorator(csrf_exempt) 方式1
def dispatch(self, request, *args, **kwargs):
return super(StudentView,self).dispatch(request, *args, **kwargs)
def get(self, request, *args,**kwargs):
print('get()')
return HttpResponse('GET')
@method_decorator(csrf_exempt, name='dispatch') #方式2
class StudentViews(View):
def get(self, request, *args,**kwargs):
print('get()')
return HttpResponse('GET')
pip install djangorestframework
权限
源码流程:
self.initial(request, *args, **kwargs) #dispatch中
self.check_permissions(request) #initial中
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions(): 循环执行每个权限对象的has_permission
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
使用:
1,类,必须继承BasePersmission,必须实现has_permission方法,一般不在此处抛异常,返回False就可做到;
2,返回值:
False #无权访问
True #有权访问
3,全局使用,settings.py中,REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES':['utils.permission.SVIPPermission',]} ,可在不需要权限的视图中加permission_classes=[]
例,使用:
utils/permission.py
class MyPermission(BasePermission): 没有自定义的才找全局的
message = '必须是SVIP才能访问'
def has_permission(self, request, view):
if request.user.user_type != 3:
return False
return True
class MyPermission1(BasePermission):
def has_permission(self, request, view):
if request.user.user_type == 3:
return False
return True
api/views.py
from utils import permission
class OrderView(APIView):
permission_classes = [permission.MyPermission,]
class UserInfoView(APIView):
permission_classes = [permission.MyPermission1,]
节流
访问频率控制:
内置方法:
from rest_framework.throttlling import BaseThrottle #get_ident()获取remote_addr
class SimpleRateThrottle(BaseThrottle)
class AnonRateThrottle(SimpleRateThrottle)
class UserRateThrottle(SimpleRateThrottle)
class ScopedRateThrottle(SimpleRateThrottle)
使用:
1,类,继承BaseThrottle,实现allow_request和wait方法,
类,继承SimpleThrottle,实现get_cache_key方法,scope='luffy'用作配置文件的key;
2,全局使用|局部使用;
utils/throttle.py
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):
def __init__(self.):
self.history = None
def allow_request(self,request,view):
#remote_addr = request.META.get('REMOTE_ADDR') #也可以request._request.META.get('REMOTE_ADDR')
remote_addr = self.get_ident()
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[-1] < ctime - 60:
history.pop()
if len(history) < 3:
history.insert(0, ctime)
return True
def wait(self): #还剩多少时间可访问
ctime = time.time()
return 60-(ctime-self.history[-1])
class VisitThrottle(SimpleRateThrottle): #用于局部throttle_classes=[VisitThrottle,]
scope = 'luffy' #匿名用户
def get_cache_key(self, request, view):
return self.get_ident(request)
class UserThrottle(SimpleThrottle): 用于全局
scope = 'luffyUser' #登录用户
def get_cache_key(self, request, view):
return request.user.username
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['utils.throttle.UserThrottle'],
'DEFAULT_THROTTLE_RATES':{
'luffy': '3/m', #1分钟3次
'luffyUser': '10/m'
},
}
版本,*
解析器,*服务端判断是什么数据
序列化,****重要,请求数据进行校验,QuerySet进行序列化
分页,**
路由,**
视图,**
渲染器,*
版本
1url中通过GET传参,127.0.0.1:8000/api/users/?versinotallow=v1,from rest_framework.versioning import QueryParameterVersioning
2url路径中传参,127.0.0.1:8000/api/v1/users/,from rest_framework.versioning import URLPathVersioning,
url(r'^api/', include('api.urls')) 根路由
url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(),name='uuu') #子路由
REST_FRAMEWORK = { #settings.py
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION":"v1",
"ALLOWED_VERSIONS":"['v1','v2']",
"VERSION_PARAM":"version",
}
例,局部配置:
class ParamVersion(BaseVersioning): #此类不需要自己写了,有现成的QueryParameterVersioning,from rest_framework.versioning import BaseVersioning
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get('version')
return version
class UserView(APIView):
#versioning_class = ParamVersion
versioning_class = QueryParameterVersioning #versioning_class = URLPathVersioning
def get (self, request,*args,**kwargs):
print(request.version) #获取版本
print(request.versioning_scheme) #获取处理版本的对象
url1 = request.versioning_scheme.reverse(viewname='uuu',request=request) #反向生成url,rest_framework
print(url1)
url2 = reverse(viewname='uuu',kwargs={'version':2}) #反向生成url,django
print(url2)
return HttpResponse('用户列表')
解析器:
rest_framework解析器,对请求体数据进行解析,靠Content-Type对请求体中的数据解析;
本质:请求头|状态码|请求方法;
源码流程:
django的
request.body 请求体
request.POST #从请求体中转为QueryDict
from django.core.handlers.wsgi import WSGIRequest
要保证request.POST中有值,必须满足2个:
请求头要求,Content-Type: application/x-www-form-urlencoded,会去request.body中解析数据
数据格式要求,name=alex&age=18&gender=man
$.ajax({ #默认会将data转为name=alex&age=18,带上请求头Content-Type: application/x-www-form-urlencoded
url:...
type:POST,
data:{name:alex,age=18}
})
$.ajax({ #默认会将data转为name=alex&age=18,带上请求头Content-Type: application/json,request.body中有值,request.POST没有,请求头不对
url:...
type:POST,
headers: {'Content-Type':'application/json'},
data:{name:alex,age=18}
})
$.ajax({ #请求头和data都不对,request.body中有值,request.POST没有
url:...
type:POST,
headers: {'Content-Type':'application/json'},
data:JSON.stringify({name:alex,age=18})
})
局部使用:
from rest_framework.parsers import JSONParser
class ParserView(APIView):
parser_classes = [JSONParser,] 有JSONParser此view只能解析Content-Type是application/json的,另FormParser可解析Content-Type是application/x-www-form-urlencoded
def post(self,request, *args, **kwargs):
print(request.data)
全局使用:
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
}
在视图中,request.data
序列化
对请求数据验证;queryset序列化;
注1,django序列化,字段较多时一个个的提取很麻烦:
for good in goods:
json_dict = {}
获取商品的每个字段,键值对形式
json_dict['name'] = good.name
json_dict['category'] = good.category.name
json_dict['market_price'] = good.market_price
json_list.append(json_dict)
from django.127.0.0.1:8000/api/v1/pager1/?page=1&size=20 #size可自动调整,max_page_size控制最多显示多少条
max_page_size = 5
page_query_param = 'page'
例1:
例2:
pg.get_paginated_response(ser.data),会自动生成返回值,且有上一页|下一页|count,前端没有要求可以不传;
例3:
视图
View--> #class View(object):,django的,from django.views.generic import View
APIView--> #class APIView(View):,from rest_framework.views import View, APIView
GenericAPIView--> ,from rest_framework.generics import GenericAPIView,通常不用,还得重写get方法
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): #继承了2个,继承了ViewSetMixin,路由发生变化
class ModelViewSet(mixins.CreateModelMixin, #继承了6个
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
class ListAPIView(mixins.ListModelMixin,继承了2个;2写了get方法;使用时仅需定义queryset和serializer_class
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
总结:
增删改查 #ModelViewSet
增删 #CreateModelMixin|DestroyModelMixin|GenericViewSet
复杂逻辑 #GenericViewSet或APIView
另,class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
ViewSet类与View类几乎相同,但提供的是read|update这些操作,而不是get|put等HTTP方法,同时ViewSet提供了默认的url结构,使得我们更专注于API本身;
Router提供了一种简单|快速|集成的方式来定义一系列的urls;
权限相关:
例:
例:
例:
路由
手动要写4个url:
增删改查,用自动生成;
router = routers.DefaultRouter()
router.regisnter(r'rt', views.View1View) #rt为该路由的前缀,会以这个前缀生成4个路由
url(r'^(?P<version>[v1|v2]+)/', include(router.urls)) #r'^(?P<version>[v1|v2]+)/'为版本,没有版本为^
content-type
django内置的一个组件,帮助开发者做连表操作,FK|manytomany|onetoone,混搭(关联可以是任何类型);
适用于1张表和多张表关联;
例:
drf的api文档自动生成:
pip install coreapi
from rest_framework.documentation import include_docs_urls
urlpatterns = [
文档,title自定义
仙剑奇侠传')),
]
drf的api文档自动生成:
优点:
自动生成
文档里可以做交互和测试
可以生成js,shell和python代码段
#drf文档,title自定义
path('docs',include_docs_urls(title='仙剑奇侠传')),
代码中注释格式:
class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
'''
list:
商品列表,分页,搜索,过滤,排序
retrieve:
获取商品详情
'''
drf对象级别的权限支持:
pip install django-guardian
drf的过滤:
例,如果不用django-filters,用name过滤,代码为:
def get_queryset(self):
queryset = Product.objects.all()
name = self.request.query_params.get('name', None)
if name is not None:
queryset = queryset.filter(name=name)
return queryset
django-filters
INSTALLED_APPS = [ 'django_filters', ]
# goods/filters.py
import django_filters
from .models import Goods
class GoodsFilter(django_filters.rest_framework.FilterSet):
'''
商品过滤的类
'''
#两个参数,field_name是要过滤的字段,lookup_expr是执行的行为,‘小于等于本店价格’
price_min = django_filters.NumberFilter(field_name="shop_price",lookup_expr='gte')
price_max = django_filters.NumberFilter(field_name="shop_price",lookup_expr='lte')
top_category = django_filters.NumberFilter(field_name="category",method='top_category_filter')
def top_category_filter(self, queryset, name, value):
# 不管当前点击的是一级分类二级分类还是三级分类,都能找到。
return queryset.filter(Q(category_id=value) |Q(category__parent_category_id=value) | Q(category__parent_category__parent_category_id=value))
class Meta:
model = Goods
fields = ['price_min', 'price_max']
# views.py
from .filters import GoodsFilter
from django_filters.rest_framework import DjangoFilterBackend
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
#'商品列表页'
#这里必须要定义一个默认的排序,否则会报错
queryset = Goods.objects.all().order_by('id')
# 分页
pagination_class = GoodsPagination
serializer_class = GoodsSerializer
filter_backends = (DjangoFilterBackend,)
# 设置filter的类为我们自定义的类
filter_class = GoodsFilter
127.0.0.1:8000/goods/?price_min=10&price_max=50
使用方式1:
class ClusterFilter(filters.FilterSet):
class Meta:
model = ClusterConf
fields = ['cluster_name', 'cluster_type']
class DbList(RestBase, generics.ListAPIView):
serializer_class = Serializer
queryset = ClusterConf.objects.all()
filter_class = ClusterFilter
使用方式2:
class DbList(RestBase, generics.ListAPIView):
serializer_class = Serializer
queryset = ClusterConf.objects.all()
filterset_fields = ['cluster_name', 'cluster_type']
例,多IP查询:
class ServerFilter(django_filters.rest_framework.FilterSet):
ip_list = django_filters.rest_framework.BaseInFilter(field_name='ip', lookup_expr='in')
hostname_list = django_filters.rest_framework.BaseInFilter(field_name='hostname', lookup_expr='in')
class Meta:
model = ResourceManage
fields = ['ip_list', 'ip', 'hostname_list', 'app']
drf的搜索和排序
搜索的字段可用正则,更灵活;
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
'商品列表页'
#这里必须要定义一个默认的排序,否则会报错
queryset = Goods.objects.all().order_by('id')
# 分页
pagination_class = GoodsPagination
serializer_class = GoodsSerializer
filter_backends = (DjangoFilterBackend,filters.SearchFilter)
# 设置filter的类为我们自定义的类
filter_class = GoodsFilter
#搜索,=name表示精确搜索,也可以使用各种正则表达式
#search_fields = ('=name','goods_brief')
search_fields = ('name', 'goods_brief', 'goods_desc')
#ordering_fields = ('sold_num', 'add_time')
ordering_fields = ('sold_num', 'shop_price')
127.0.0.1:8000/goods/?ordering=-sold_num&search=水果
例:
from django_filters import rest_framework as filters
from rest_framework import filters as filters_search
from django_filters.rest_framework import DjangoFilterBackend
class AppList(generics.ListCreateAPIView):
db = AppConfig
serializer_class = Serializer
pagination_class = StandardResultsSetPagination
filter_class = CFilter
queryset = db.objects.all().order_by('id')
filter_backends = (filters_search.SearchFilter, DjangoFilterBackend) # 在既有filter_class和search_fields时,此行必写
search_fields = ('project_name__unit', 'project_name__plate', 'project_name__project_name',
'project_name__subsystem_name',
'pool_name__pool_cname', 'owner__user_name', 'app_name', 'app_type', 'app_descrip', 'app_lang',
'app_arch', 'app_level', 'app_cname', 'package_type',
'address')
例,根据姓名模糊搜索AppConfig维护人(表中存的是工号):
class CustomSearchFilter(SearchFilter):
def get_search_terms(self, request):
"""
Search terms are set by a ?search=... query parameter,
and may be comma and/or whitespace delimited.
"""
params = request.query_params.get(self.search_param, '')
params = params.replace(',', ' ').split()
lst = [i for i in params]
for item in params:
regex = re.compile(r'[\u4e00-\u9fa5]')
if regex.match(item):
uuids = [row.uuid for row in UserInfo.objects.filter(user_name=item)]
for uuid in uuids:
lst.append(uuid) if uuid else None
return lst
def filter_queryset(self, request, queryset, view):
search_fields = self.get_search_fields(view, request)
search_terms = self.get_search_terms(request)
if not search_fields or not search_terms:
return queryset
orm_lookups = [
self.construct_search(six.text_type(search_field))
for search_field in search_fields
]
base = queryset
conditions = []
for search_term in search_terms:
queries = [
models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
conditions.append(reduce(operator.or_, queries))
queryset = queryset.filter(reduce(operator.or_, conditions))
if self.must_call_distinct(queryset, search_fields):
# Filtering against a many-to-many field requires us to
# call queryset.distinct() in order to avoid duplicate items
# in the resulting queryset.
# We try to avoid this if possible, for performance reasons.
queryset = distinct(queryset, base)
return queryset
例,排序(django_filters.OrderingFilter):
class Product(models.Model):
name = models.CharField(max_length=100)
created_at = models.DatetimeField()
class ProductFilter(django_filters.FilterSet):
sort = django_filters.OrderingFilter(fields=('created_at',))
class Meta:
model = Product
fields = ['name',]
使用时,前端用/products/?sort=-created_at
例,排序(rest_framework.filters.OrderingFilter):
# 在类视图中配置如下
filter_backends = (OrderingFilter, )
ordering_fields = ('create_time', 'price', 'sales')
例,高级搜索:
fields为list形式,则是按=严格匹配;
fields为dict形式,可选择更多的匹配方式,如:
模糊查询fields = {'name': ['icontains']},那么模糊查询的url则为/products/?name__icnotallow=xxx;
日期范围查询,fields = {'name': ['icontains'], 'created_at': ['date__gte', 'date__lte']},url对应为/products/?created_at__date__gte=2019-11-20&created_at__date__lte=2019-11-22,
这种方式定义的url所需参数太长,可自定义filter字段,如:
class ProductFilter(django_filters.FilterSet):
sort = django_filters.OrderingFilter(fields=('created_at',))
min_date = django_filters.DateFilter(name='created_at__date', lookup_expr='gte')
max_date = django_filters.DateFilter(name='created_at__date', lookup_expr='lte')
class Meta:
model = Product
fields = {'name': ['icontains']}
项目根下urls.py
# MxShop/urls.py
from django.urls import path,include,re_path
import xadmin
from django.views.static import serve
from MxShop.settings import MEDIA_ROOT
# from goods.view_base import GoodsListView
from rest_framework.documentation import include_docs_urls
from goods.views import GoodsListViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
#配置goods的url
router.register(r'goods', GoodsListViewSet)
router.register(r'categorys', CategoryViewSet, base_name="categorys")
router.register(r'users', UserViewset, base_name="users")
router.register(r'userfavs', UserFavViewset, base_name="userfavs")
router.register(r'messages', LeavingMessageViewset, base_name="messages")
router.register(r'shopcarts', ShoppingCartViewset, base_name="shopcarts")
router.register(r'orders', OrderViewset, base_name="orders")
router.register(r'indexgoods', IndexCategoryViewset, base_name="indexgoods")
urlpatterns = [
path('xadmin/', xadmin.site.urls),
path('api-auth/',include('rest_framework.urls')),
path('ueditor/',include('DjangoUeditor.urls' )),
#文件
path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
#drf文档,title自定义
path('docs',include_docs_urls(title='仙剑奇侠传')),
#商品列表页
re_path('^', include(router.urls)),
]
drf的token
缺点:
保存在DB中,如果是一个分布式系统就非常麻烦;
token永久有效,没有过期时间;
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
from rest_framework.authtoken import views
urlpatterns = [
# token
path('api-token-auth/', views.obtain_auth_token)
]
客户端身份验证:
对于客户端进行身份验证,令牌密钥应包含在,关键字应以字符串文字,例如Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
注意:如果您想在header中使用不同的关键字(例如 Bearer),只需子类化TokenAuthentication并设置keyword类变量
如果成功通过身份验证,TokenAuthentication将提供以下凭据:
request.user #是一个Django User实例
request.auth #是一个rest_framework.authtoken.models.Token实例
未经身份验证的响应被拒绝将导致HTTP 401 Unauthorized的响应和相应的WWW-Authenticate header,例如:WWW-Authenticate: Token
要想获取request.user和request.auth要在settings.py中添加
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
)
}
jwt方式,用户认证
pip install djangorestframework-jwt
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
)
}
import datetime
#有效期限
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), #也可以设置secnotallow=20
'JWT_AUTH_HEADER_PREFIX': 'JWT', #JWT跟前端保持一致,比如“token”这里设置成JWT
}
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^api-token-refresh/', refresh_jwt_token),
url(r'^api-token-verify/', verify_jwt_token),
]
drf的缓存:
pip install drf-extensions
from rest_framework_extensions.cache.mixins import CacheResponseMixin
class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, Mixins.RetrieveModelMixin,viewsets.GenericViewSet): #必须在第1个位置
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 5 #5s过期,时间自己可以随便设定
}
drf配置redis
pip install django-redis
# redis缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
REST_FRAMEWORK = {
#限速设置
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', #未登陆用户
'rest_framework.throttling.UserRateThrottle' #登陆用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '3/minute', #每分钟可以请求两次
'user': '5/minute' #每分钟可以请求五次
}
}
from rest_framework.throttling import UserRateThrottle,AnonRateThrottle
class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
throttle_classes = (UserRateThrottle, AnonRateThrottle)
social_django实现第三方登录
pip install social-auth-app-django
'socail-django' #INSTALLED_APP中引入
python manage.py migrate #DB中多了5张表
# 设置邮箱和用户名和手机号均可登录
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
'social_core.backends.weibo.WeiboOAuth2',
'social_core.backends.qq.QQOAuth2',
'social_core.backends.weixin.WeixinOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
path('', include('social_django.urls', namespace='social'))
Tokne的设计及加密方法
Token的基本设计原则:Token不携带用户敏感信息;无需查询数据库,Token可进行自我验证;
hmac作用,验证消息是否被篡改;公式;无法抵御重放gongji;
hmac.new('secret123456', value).digest() # 加密密钥、value为加密的信息
验证Token的有效性:验证验证码是否一致;验证是否过期;验证这个Token是否属于被请求数据的用户;
本文共计8848个文字,预计阅读时间需要36分钟。
目录+ + Restful规范 + + Django Rest Framework 3 + + serializers.Serializer + + serializers.ModelSerializer + + API FBV + + drf的request和response
目录
restful规范: 2
django rest framework 3
serializers.Serializer 5
serializers.ModelSerializer: 7
api FBV: 8
drf的request和response 10
rest_framework.decorators.api_view和rest_framework.views.APIView: 13
api CBV,rest_framework.views.APIView: 14
api CBV,使用rest_framework.mixins: 15
api CBV,ListAPIView|CreateAPIView|RetrieveAPIView|DestroyAPIView|UpdateAPIView|ListCreateAPIView 16
rest_framework.viewsets和rest_framework.routers.DefaultRouter: 18
分页: 20
认证: 21
授权: 26
面试题目: 27
django请求生命周期 27
csrf 29
权限 32
节流 33
版本 35
解析器: 37
序列化 38
序列化-序列化: 39
序列化-请求数据校验: 45
渲染 47
分页 49
视图 52
路由 55
content-type 57
drf的api文档自动生成: 60
drf对象级别的权限支持: 61
drf的过滤: 61
使用方式1: 62
使用方式2: 63
例,多IP查询: 63
drf的搜索和排序 63
例,排序(django_filters.OrderingFilter): 67
例,排序(rest_framework.filters.OrderingFilter): 68
例,高级搜索: 68
drf的token 69
jwt方式,用户认证 70
drf的缓存: 71
social_django实现第三方登录 72
Tokne的设计及加密方法 73
系统之间为了调用数据,用api;
格式:
json;
xml;
给用户提供一个接口前:
1.跟前端进行交互,确定前端要什么;
2.把需求写个文档;
restful规范:
restful api是用于前端和后台进行通信的一套规范,使用这个规范可以让前后端开发变得更加轻松;
数据传输格式,都用json,而不是xml;
url链接,只能有名词,不能有动词,名词的复数要加s;
restful是一种软件架构风格或者说是一种设计风格,并不是标准,它只是提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。
就像设计模式一样,并不是一定要遵循这些原则,而是基于这个风格设计的软件可以更简洁,更有层次,我们可以根据开发的实际情况,做相应的改变。
如一些规范:
1、restful提倡面向资源编程,在url接口中尽量要使用名词,不要使用动词;
2、在url接口中推荐使用www.bootcss.com/v1/mycss?page=3;
3、在url中可以体现版本号,v1.bootcss.com/mycss,不同的版本可以有不同的接口,使其更加简洁,清晰;
4、url中可以体现是否是API接口,www.bootcss.com/api/mycss;
5、url中可以添加条件去筛选匹配,www.bootcss.com/v1/mycss?page=3;
6、可以根据Http不同的method,进行不同的资源操作,5种方法:GET|POST|PUT|DELETE|PATCH;
7、响应式应该设置状态码;
8、有返回值,而且格式为统一的json格式;
9、返回错误信息,返回值携带错误信息;
10、返回结果中要提供帮助链接,即API最好做到Hypermedia,如果遇到需要跳转的情况携带调转接口的URL;
ret = {
code: 1000,
data:{
id:1,
小强',
depart_id:www.luffycity.com/api/v1/depart/8/
}
}
状态码 原生描述 描述
200 OK 服务器成功响应客户端的请求
400 INVALID REQUEST 用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized 用户没有权限访问这个请求
403 Forbidden 因为某些原因禁止访问这个请求
404 NOT FOUND 用户发送的请求的url不存在
405 Method not allowed 方法不被允许
406 NOT Accept 用户请求不被服务器接收(比如服务器期望客户端发送某个字段,但没有发送)
500 Internal server error 服务器内部错误,比如出现了bug
204 No Content
服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息,响应可能通过实体头部的形式,返回新的或更新后的元信息,如果存在这些头部信息,则应当与所请求的变量相呼应;
如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档;
由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾;
django rest framework
是django用来restful api的框架,风格完全和django一样,使用起来像是django本身提供的;
www.django-rest-framework.org/
(webproject) C:\webproject\mysite>pip install djangorestframework另coreapi,drf的文档支持;django-guardian,drf对象级别的权限支持
INSTALLED_APPS = [
'rest_framework',
用djangorestframework:
能自动生成符合 RESTful 规范的 API
1.在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本一样,这部分的代码可以简写;
2.在序列化与反序列化时,虽然操作的数据不同,但是执行的过程却相似,这部分的代码也可以简写;
REST framework可以帮助简化上述两部分的代码编写,大大提高REST API的开发速度
组件:
1.序列化组件:serializers,对queryset序列化|对请求数据反序列化并格式校验;
2.路由组件routers ,进行路由分发;
3.视图组件ModelViewSet,帮助开发者提供了一些类,并在类中提供了多个方法;
4.认证组件,写一个类并注册到认证类(authentication_classes),在类的的authticate方法中编写认证逻辑;
5.权限组件,写一个类并注册到权限类(permission_classes),在类的的has_permission方法中编写认证逻辑,
6.频率限制,写一个类并注册到频率类(throttle_classes),在类的的allow_request|wait方法中编写认证逻辑;
7.解析器,选择对数据解析的类,在解析器类中注册(parser_classes);
8.渲染器,定义数据如何渲染到到页面上,在渲染器类中注册(renderer_classes);
9.分页,对获取到的数据进行分页处理,pagination_class;
10.版本,版本控制用来在不同的客户端使用不同的行为,在url中设置version参数,用户请求时候传入参数,在request.version中获取版本,根据版本不同做不同处理;
django rest framework框架中的视图都可以继承哪些类?
class View(object):
class APIView(View): #封装了view,并且重新封装了request,初始化了各种组件
class GenericAPIView(views.APIView):增加了一些属性和方法,如get_queryset,get_serializer
class GenericViewSet(ViewSetMixin, generics.GenericAPIView)父类ViewSetMixin 重写了as_view,返回return csrf_exempt(view),并重新设置请求方式与执行函数的关系
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):pass继承了mixins下的一些类,封装了list,create,update等方法和GenericViewSet
简述 django rest framework框架的认证流程
1.用户请求走进来后,走APIView,初始化了默认的认证方法
2.走到APIView的dispatch方法,initial方法调用了request.user
3.如果我们配置了认证类,走我们自己认证类中的authentication方法
django rest framework如何实现的用户访问频率控制
使用IP|用户账号,作为键,每次的访问时间戳作为值,构造一个字典形式的数据,存起来,每次访问时对时间戳列表的元素进行判断,把超时的删掉,再计算列表剩余的元素数就能做到频率限制了
匿名用户,使用IP控制,但是无法完全控制,因为用户可以换代理IP登录用户;使用账号控制,但是如果有很多账号,也无法限制;
rest_framework序列化组件的作用,以及一些外键关系的钩子方法
作用:帮助我们序列化数据
1.choices get_字段名_display
2.ForeignKey source=orm 操作
3.ManyToManyFiled SerializerMethodField()
字段名():
自定义
serializers.Serializer
from rest_framework import serializers
class Serializer(BaseSerializer):
class ModelSerializer(Serializer):
class HyperlinkedModelSerializer(ModelSerializer):
rest_framework.serializers.Serializer和django.forms.Form:
功能一样,都是将用户提交的数据转为py数据结构,可用此来完成校验;
Form用在post提交中的form表单;
Serializer用在用户提交的json;
另,
from django.core import serializers的serializer虽可简单实现序列化,但有2个缺点,1字段序列化定死的,要想重组很麻烦,2images保存的是一个相对路径还需要补全,drf可自动补全
def serialize(format, queryset, **options): #serializers.serialize
例:
from rest_framework import serializers
from .models import Author
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
first_name = serializers.CharField(max_length=100)
last_name = serializers.CharField(required=False, allow_blank=True, max_length=100)
email = serializers.EmailField(required=False, allow_blank=True, max_length=100)
def create(self, validated_data):
return Author.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.email = validated_data.get('email', instance.email)
instance.save()
return instance
底层实现:
>>> from books.serializer import AuthorSerializer
>>> from rest_framework.renderers import JSONRenderer #py结构-->json
>>> from rest_framework.parsers import JSONParser #json-->py结构
>>> from books.models import Author
>>> Author.objects.all()
<QuerySet [<Author: chai jowin>, <Author: dsfdfd sfdsfdsf>]>
>>> a = Author.objects.get(id=1)
>>> s = AuthorSerializer(a)
>>> s.data #dict
{'last_name': 'jowin', 'email': 'jowin@chai.com', 'id': 1, 'first_name': 'chai'}
>>> content = JSONRenderer().render(s.data)
>>> content
b'{"id":1,"first_name":"chai","last_name":"jowin","email":"jowin@chai.com"}'
>>> from django.utils.six import BytesIO
>>> stream = BytesIO(content)
>>> stream
<_io.BytesIO object at 0x00000000046389E8>
>>> data = JSONParser().parse(stream)
>>> data
{'last_name': 'jowin', 'email': 'jowin@chai.com', 'id': 1, 'first_name': 'chai'}
>>> s = AuthorSerializer(data=data)
>>> s
AuthorSerializer(data={'last_name': 'jowin', 'email': 'jowin@chai.com', 'id': 1, 'first_name': 'chai'}):
id = IntegerField(read_only=True)
first_name = CharField(max_length=100)
last_name = CharField(allow_blank=True, max_length=100, required=False)
email = EmailField(allow_blank=True, max_length=100, required=False)
>>> s.is_valid()
True
>>> s.validated_data返回ordereddict
OrderedDict([('first_name', 'chai'), ('last_name', 'jowin'), ('email', 'jowin@chai.com')])
>>> s.save()
<Author: chai jowin>
>>> Author.objects.all()
<QuerySet [<Author: chai jowin>, <Author: dsfdfd sfdsfdsf>, <Author: chai jowin>]>
>>> s = AuthorSerializer(Author.objects.all(), many=True)序列化多个对象时要加many=True,调用many_init()
>>> s.data
[OrderedDict([('id', 1), ('first_name', 'chai'), ('last_name', 'jowin'), ('email', 'jowin@chai.com')]), OrderedDict([('id', 2), ('first_name', 'dsfdfd'), ('la
st_name', 'sfdsfdsf'), ('email', 'dsfdsf@dsfdsf.com')]), OrderedDict([('id', 3), ('first_name', 'chai'), ('last_name', 'jowin'), ('email', 'jowin@chai.com')])
]
serializers.ModelSerializer:
rest_framework.serializers.ModelSerializer同django.forms.ModelForm;
class ModelSerializer(Serializer):
serializer_field_mapping = {
models.AutoField: IntegerField,
models.BigIntegerField: IntegerField,
models.BooleanField: BooleanField,
models.CharField: CharField,
models.CommaSeparatedIntegerField: CharField,
models.DateField: DateField,
models.DateTimeField: DateTimeField,
models.DecimalField: DecimalField,
models.EmailField: EmailField,
models.Field: ModelField,
models.FileField: FileField,
models.FloatField: FloatField,
models.ImageField: ImageField,
models.IntegerField: IntegerField,
models.NullBooleanField: NullBooleanField,
models.PositiveIntegerField: IntegerField,
models.PositiveSmallIntegerField: IntegerField,
models.SlugField: SlugField,
models.SmallIntegerField: IntegerField,
models.TextField: CharField,
models.TimeField: TimeField,
models.URLField: URLField,
models.GenericIPAddressField: IPAddressField,
models.FilePathField: FilePathField,
}
例:
class Serializer(serializers.ModelSerializer):
project_cname = serializers.ReadOnlyField(source='project_name.project_cname')
pool_cname = serializers.ReadOnlyField(source='pool_name.pool_cname')
project_name = serializers.IntegerField(source='project_name_id')
owner_name = serializers.ReadOnlyField(source='owner.user')
update_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M', required=False)
class Meta:
model = AppConfig
fields = '__all__'
例:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'first_name', 'last_name', 'email']
api FBV:
例:
from django.www.django-rest-framework.org/api-guide/permissions/#api-reference
rest_framework内置权限:
AllowAny
IsAuthenticated
IsAdminUser
IsAuthenticatedOrReadOnly
DjangoModelPermissions
DjangoModelPermissionsOrAnonReadOnly
DjangoObjectPermissions
REST_FRAMEWORK = {
"PAGE_SIZE": 1,
"DEFAULT_AUTHENTICATION_CLASSES": (
# 'rest_framework.authentication.BasicAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.permissions.IsAuthenticated',全局配置默认权限
)
}
例,view中使用权限控制:
from rest_framework import generics
from rest_framework import permissions
class AuthorList(generics.ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
permission_classes = (permissions.IsAuthenticated,) #多个是and的关系
例,自定义权限:
from rest_framework import generics
from rest_framework import permissions
class IsSuperUser(permissions.BasePermission): #继承permissions.BasePermission,重写has_permission()
def has_permission(self, request, view):
return request.user.is_superuser()
class AuthorList(generics.ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
# permission_classes = (permissions.IsAuthenticated,)
permission_classes = (IsSuperUser,)
面试题目:
1、中间件
2、csrf原理及解决,解决1cors(响应头放在中间件),解决2jsonp
3、restful规范,10条谈,谈restful api规范的认识?10条,认识要与实际使用经验结合
4、面向对象
5、django请求生命周期
6、django请求生命周期(包含rest_framework),dispatch()
7、rest_framework,认证流程|权限流程|节流流程
8、FBV|CBV
面向对象:
封装 将属性和方法封装到类中,2通过构造方法把一部分数据封装到对象里,之后打包使用
继承 #若在2个类或多个类中有相同的属性和方法,可提取到1个类中实现;多继承,先左边后右边,广度优先|深度优先,经典类|新式类
多态 #1个对象点出来什么,就具有什么方法或属性
django请求生命周期(包含rest framework):
wsgi-->
中间件-->
as_view中view中dispatch-->
initialize_request()中封装request,get_parsers()获取解析器列表解析式,另get_authenticators()获取认证对象列表解析式-->
initial()中determine_version()处理版本,另perform_authentication(request)|check_permissions(request)|check_throttles(request)-->
反射执行对应HTTP方法
各组件按生命周期:
路由
版本
认证
权限
节流
通过反射执行视图(继承...),用解析器拿到数据
序列化,对数据校验并序列化
分页
渲染器
另,wsgiref|werkzeug|tornado|uwsgi是实现wsgi协议的一个模块,本质是一个socket服务器;
CBV
基于反射实现,根据请求方法不同,执行不同的方法;
流程:url-->view()-->dispatch()反射执行HTTP方法GET|POST|DELETE|PUT
csrf
基于中间件的process_view方法,装饰器@csrf_exempt(无需认证)或@csrf_protect(需认证)给单独函数进行设置;
另,jsonp解决csrf;
django csrf用中间件实现,1个中间件里最多可写5个方法:
process_request
process_view #csrf在此步验证的,1先检查函数是否@csrf_exempt,2去请求体中取cookie中的token
process_response
process_exception
process_render_template
使用中间件做过什么?适用于所有请求批量做操作
基于角色的权限控制;
用户登录验证;
csrf; #process_view方法检查视图是否被@csrf_exempt,去请求体cookie里获取token
session;
黑名单;
日志记录;
情况1:
settings.py中配置了全局csrf;
from django.views.decorators.csrf import csrf_exempt #@csrf_exempt被装饰的函数免除csrf验证
情况2:
settings.py中注释掉了csrf;
from django.views.decorators.csrf import csrf_protect被装饰的函数启用csrf验证
CBV中解决使用csrf:
要用method_decorator(csrf_exempt),且要在dispatch上;
from django.utils.decorator import method_decorator
class StudentViews(View):
@method_decorator(csrf_exempt) 方式1
def dispatch(self, request, *args, **kwargs):
return super(StudentView,self).dispatch(request, *args, **kwargs)
def get(self, request, *args,**kwargs):
print('get()')
return HttpResponse('GET')
@method_decorator(csrf_exempt, name='dispatch') #方式2
class StudentViews(View):
def get(self, request, *args,**kwargs):
print('get()')
return HttpResponse('GET')
pip install djangorestframework
权限
源码流程:
self.initial(request, *args, **kwargs) #dispatch中
self.check_permissions(request) #initial中
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions(): 循环执行每个权限对象的has_permission
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
使用:
1,类,必须继承BasePersmission,必须实现has_permission方法,一般不在此处抛异常,返回False就可做到;
2,返回值:
False #无权访问
True #有权访问
3,全局使用,settings.py中,REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES':['utils.permission.SVIPPermission',]} ,可在不需要权限的视图中加permission_classes=[]
例,使用:
utils/permission.py
class MyPermission(BasePermission): 没有自定义的才找全局的
message = '必须是SVIP才能访问'
def has_permission(self, request, view):
if request.user.user_type != 3:
return False
return True
class MyPermission1(BasePermission):
def has_permission(self, request, view):
if request.user.user_type == 3:
return False
return True
api/views.py
from utils import permission
class OrderView(APIView):
permission_classes = [permission.MyPermission,]
class UserInfoView(APIView):
permission_classes = [permission.MyPermission1,]
节流
访问频率控制:
内置方法:
from rest_framework.throttlling import BaseThrottle #get_ident()获取remote_addr
class SimpleRateThrottle(BaseThrottle)
class AnonRateThrottle(SimpleRateThrottle)
class UserRateThrottle(SimpleRateThrottle)
class ScopedRateThrottle(SimpleRateThrottle)
使用:
1,类,继承BaseThrottle,实现allow_request和wait方法,
类,继承SimpleThrottle,实现get_cache_key方法,scope='luffy'用作配置文件的key;
2,全局使用|局部使用;
utils/throttle.py
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):
def __init__(self.):
self.history = None
def allow_request(self,request,view):
#remote_addr = request.META.get('REMOTE_ADDR') #也可以request._request.META.get('REMOTE_ADDR')
remote_addr = self.get_ident()
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
history = VISIT_RECORD.get(remote_addr)
self.history = history
while history and history[-1] < ctime - 60:
history.pop()
if len(history) < 3:
history.insert(0, ctime)
return True
def wait(self): #还剩多少时间可访问
ctime = time.time()
return 60-(ctime-self.history[-1])
class VisitThrottle(SimpleRateThrottle): #用于局部throttle_classes=[VisitThrottle,]
scope = 'luffy' #匿名用户
def get_cache_key(self, request, view):
return self.get_ident(request)
class UserThrottle(SimpleThrottle): 用于全局
scope = 'luffyUser' #登录用户
def get_cache_key(self, request, view):
return request.user.username
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['utils.throttle.UserThrottle'],
'DEFAULT_THROTTLE_RATES':{
'luffy': '3/m', #1分钟3次
'luffyUser': '10/m'
},
}
版本,*
解析器,*服务端判断是什么数据
序列化,****重要,请求数据进行校验,QuerySet进行序列化
分页,**
路由,**
视图,**
渲染器,*
版本
1url中通过GET传参,127.0.0.1:8000/api/users/?versinotallow=v1,from rest_framework.versioning import QueryParameterVersioning
2url路径中传参,127.0.0.1:8000/api/v1/users/,from rest_framework.versioning import URLPathVersioning,
url(r'^api/', include('api.urls')) 根路由
url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(),name='uuu') #子路由
REST_FRAMEWORK = { #settings.py
"DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
"DEFAULT_VERSION":"v1",
"ALLOWED_VERSIONS":"['v1','v2']",
"VERSION_PARAM":"version",
}
例,局部配置:
class ParamVersion(BaseVersioning): #此类不需要自己写了,有现成的QueryParameterVersioning,from rest_framework.versioning import BaseVersioning
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get('version')
return version
class UserView(APIView):
#versioning_class = ParamVersion
versioning_class = QueryParameterVersioning #versioning_class = URLPathVersioning
def get (self, request,*args,**kwargs):
print(request.version) #获取版本
print(request.versioning_scheme) #获取处理版本的对象
url1 = request.versioning_scheme.reverse(viewname='uuu',request=request) #反向生成url,rest_framework
print(url1)
url2 = reverse(viewname='uuu',kwargs={'version':2}) #反向生成url,django
print(url2)
return HttpResponse('用户列表')
解析器:
rest_framework解析器,对请求体数据进行解析,靠Content-Type对请求体中的数据解析;
本质:请求头|状态码|请求方法;
源码流程:
django的
request.body 请求体
request.POST #从请求体中转为QueryDict
from django.core.handlers.wsgi import WSGIRequest
要保证request.POST中有值,必须满足2个:
请求头要求,Content-Type: application/x-www-form-urlencoded,会去request.body中解析数据
数据格式要求,name=alex&age=18&gender=man
$.ajax({ #默认会将data转为name=alex&age=18,带上请求头Content-Type: application/x-www-form-urlencoded
url:...
type:POST,
data:{name:alex,age=18}
})
$.ajax({ #默认会将data转为name=alex&age=18,带上请求头Content-Type: application/json,request.body中有值,request.POST没有,请求头不对
url:...
type:POST,
headers: {'Content-Type':'application/json'},
data:{name:alex,age=18}
})
$.ajax({ #请求头和data都不对,request.body中有值,request.POST没有
url:...
type:POST,
headers: {'Content-Type':'application/json'},
data:JSON.stringify({name:alex,age=18})
})
局部使用:
from rest_framework.parsers import JSONParser
class ParserView(APIView):
parser_classes = [JSONParser,] 有JSONParser此view只能解析Content-Type是application/json的,另FormParser可解析Content-Type是application/x-www-form-urlencoded
def post(self,request, *args, **kwargs):
print(request.data)
全局使用:
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
}
在视图中,request.data
序列化
对请求数据验证;queryset序列化;
注1,django序列化,字段较多时一个个的提取很麻烦:
for good in goods:
json_dict = {}
获取商品的每个字段,键值对形式
json_dict['name'] = good.name
json_dict['category'] = good.category.name
json_dict['market_price'] = good.market_price
json_list.append(json_dict)
from django.127.0.0.1:8000/api/v1/pager1/?page=1&size=20 #size可自动调整,max_page_size控制最多显示多少条
max_page_size = 5
page_query_param = 'page'
例1:
例2:
pg.get_paginated_response(ser.data),会自动生成返回值,且有上一页|下一页|count,前端没有要求可以不传;
例3:
视图
View--> #class View(object):,django的,from django.views.generic import View
APIView--> #class APIView(View):,from rest_framework.views import View, APIView
GenericAPIView--> ,from rest_framework.generics import GenericAPIView,通常不用,还得重写get方法
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): #继承了2个,继承了ViewSetMixin,路由发生变化
class ModelViewSet(mixins.CreateModelMixin, #继承了6个
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
class ListAPIView(mixins.ListModelMixin,继承了2个;2写了get方法;使用时仅需定义queryset和serializer_class
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
总结:
增删改查 #ModelViewSet
增删 #CreateModelMixin|DestroyModelMixin|GenericViewSet
复杂逻辑 #GenericViewSet或APIView
另,class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
ViewSet类与View类几乎相同,但提供的是read|update这些操作,而不是get|put等HTTP方法,同时ViewSet提供了默认的url结构,使得我们更专注于API本身;
Router提供了一种简单|快速|集成的方式来定义一系列的urls;
权限相关:
例:
例:
例:
路由
手动要写4个url:
增删改查,用自动生成;
router = routers.DefaultRouter()
router.regisnter(r'rt', views.View1View) #rt为该路由的前缀,会以这个前缀生成4个路由
url(r'^(?P<version>[v1|v2]+)/', include(router.urls)) #r'^(?P<version>[v1|v2]+)/'为版本,没有版本为^
content-type
django内置的一个组件,帮助开发者做连表操作,FK|manytomany|onetoone,混搭(关联可以是任何类型);
适用于1张表和多张表关联;
例:
drf的api文档自动生成:
pip install coreapi
from rest_framework.documentation import include_docs_urls
urlpatterns = [
文档,title自定义
仙剑奇侠传')),
]
drf的api文档自动生成:
优点:
自动生成
文档里可以做交互和测试
可以生成js,shell和python代码段
#drf文档,title自定义
path('docs',include_docs_urls(title='仙剑奇侠传')),
代码中注释格式:
class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
'''
list:
商品列表,分页,搜索,过滤,排序
retrieve:
获取商品详情
'''
drf对象级别的权限支持:
pip install django-guardian
drf的过滤:
例,如果不用django-filters,用name过滤,代码为:
def get_queryset(self):
queryset = Product.objects.all()
name = self.request.query_params.get('name', None)
if name is not None:
queryset = queryset.filter(name=name)
return queryset
django-filters
INSTALLED_APPS = [ 'django_filters', ]
# goods/filters.py
import django_filters
from .models import Goods
class GoodsFilter(django_filters.rest_framework.FilterSet):
'''
商品过滤的类
'''
#两个参数,field_name是要过滤的字段,lookup_expr是执行的行为,‘小于等于本店价格’
price_min = django_filters.NumberFilter(field_name="shop_price",lookup_expr='gte')
price_max = django_filters.NumberFilter(field_name="shop_price",lookup_expr='lte')
top_category = django_filters.NumberFilter(field_name="category",method='top_category_filter')
def top_category_filter(self, queryset, name, value):
# 不管当前点击的是一级分类二级分类还是三级分类,都能找到。
return queryset.filter(Q(category_id=value) |Q(category__parent_category_id=value) | Q(category__parent_category__parent_category_id=value))
class Meta:
model = Goods
fields = ['price_min', 'price_max']
# views.py
from .filters import GoodsFilter
from django_filters.rest_framework import DjangoFilterBackend
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
#'商品列表页'
#这里必须要定义一个默认的排序,否则会报错
queryset = Goods.objects.all().order_by('id')
# 分页
pagination_class = GoodsPagination
serializer_class = GoodsSerializer
filter_backends = (DjangoFilterBackend,)
# 设置filter的类为我们自定义的类
filter_class = GoodsFilter
127.0.0.1:8000/goods/?price_min=10&price_max=50
使用方式1:
class ClusterFilter(filters.FilterSet):
class Meta:
model = ClusterConf
fields = ['cluster_name', 'cluster_type']
class DbList(RestBase, generics.ListAPIView):
serializer_class = Serializer
queryset = ClusterConf.objects.all()
filter_class = ClusterFilter
使用方式2:
class DbList(RestBase, generics.ListAPIView):
serializer_class = Serializer
queryset = ClusterConf.objects.all()
filterset_fields = ['cluster_name', 'cluster_type']
例,多IP查询:
class ServerFilter(django_filters.rest_framework.FilterSet):
ip_list = django_filters.rest_framework.BaseInFilter(field_name='ip', lookup_expr='in')
hostname_list = django_filters.rest_framework.BaseInFilter(field_name='hostname', lookup_expr='in')
class Meta:
model = ResourceManage
fields = ['ip_list', 'ip', 'hostname_list', 'app']
drf的搜索和排序
搜索的字段可用正则,更灵活;
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
'商品列表页'
#这里必须要定义一个默认的排序,否则会报错
queryset = Goods.objects.all().order_by('id')
# 分页
pagination_class = GoodsPagination
serializer_class = GoodsSerializer
filter_backends = (DjangoFilterBackend,filters.SearchFilter)
# 设置filter的类为我们自定义的类
filter_class = GoodsFilter
#搜索,=name表示精确搜索,也可以使用各种正则表达式
#search_fields = ('=name','goods_brief')
search_fields = ('name', 'goods_brief', 'goods_desc')
#ordering_fields = ('sold_num', 'add_time')
ordering_fields = ('sold_num', 'shop_price')
127.0.0.1:8000/goods/?ordering=-sold_num&search=水果
例:
from django_filters import rest_framework as filters
from rest_framework import filters as filters_search
from django_filters.rest_framework import DjangoFilterBackend
class AppList(generics.ListCreateAPIView):
db = AppConfig
serializer_class = Serializer
pagination_class = StandardResultsSetPagination
filter_class = CFilter
queryset = db.objects.all().order_by('id')
filter_backends = (filters_search.SearchFilter, DjangoFilterBackend) # 在既有filter_class和search_fields时,此行必写
search_fields = ('project_name__unit', 'project_name__plate', 'project_name__project_name',
'project_name__subsystem_name',
'pool_name__pool_cname', 'owner__user_name', 'app_name', 'app_type', 'app_descrip', 'app_lang',
'app_arch', 'app_level', 'app_cname', 'package_type',
'address')
例,根据姓名模糊搜索AppConfig维护人(表中存的是工号):
class CustomSearchFilter(SearchFilter):
def get_search_terms(self, request):
"""
Search terms are set by a ?search=... query parameter,
and may be comma and/or whitespace delimited.
"""
params = request.query_params.get(self.search_param, '')
params = params.replace(',', ' ').split()
lst = [i for i in params]
for item in params:
regex = re.compile(r'[\u4e00-\u9fa5]')
if regex.match(item):
uuids = [row.uuid for row in UserInfo.objects.filter(user_name=item)]
for uuid in uuids:
lst.append(uuid) if uuid else None
return lst
def filter_queryset(self, request, queryset, view):
search_fields = self.get_search_fields(view, request)
search_terms = self.get_search_terms(request)
if not search_fields or not search_terms:
return queryset
orm_lookups = [
self.construct_search(six.text_type(search_field))
for search_field in search_fields
]
base = queryset
conditions = []
for search_term in search_terms:
queries = [
models.Q(**{orm_lookup: search_term})
for orm_lookup in orm_lookups
]
conditions.append(reduce(operator.or_, queries))
queryset = queryset.filter(reduce(operator.or_, conditions))
if self.must_call_distinct(queryset, search_fields):
# Filtering against a many-to-many field requires us to
# call queryset.distinct() in order to avoid duplicate items
# in the resulting queryset.
# We try to avoid this if possible, for performance reasons.
queryset = distinct(queryset, base)
return queryset
例,排序(django_filters.OrderingFilter):
class Product(models.Model):
name = models.CharField(max_length=100)
created_at = models.DatetimeField()
class ProductFilter(django_filters.FilterSet):
sort = django_filters.OrderingFilter(fields=('created_at',))
class Meta:
model = Product
fields = ['name',]
使用时,前端用/products/?sort=-created_at
例,排序(rest_framework.filters.OrderingFilter):
# 在类视图中配置如下
filter_backends = (OrderingFilter, )
ordering_fields = ('create_time', 'price', 'sales')
例,高级搜索:
fields为list形式,则是按=严格匹配;
fields为dict形式,可选择更多的匹配方式,如:
模糊查询fields = {'name': ['icontains']},那么模糊查询的url则为/products/?name__icnotallow=xxx;
日期范围查询,fields = {'name': ['icontains'], 'created_at': ['date__gte', 'date__lte']},url对应为/products/?created_at__date__gte=2019-11-20&created_at__date__lte=2019-11-22,
这种方式定义的url所需参数太长,可自定义filter字段,如:
class ProductFilter(django_filters.FilterSet):
sort = django_filters.OrderingFilter(fields=('created_at',))
min_date = django_filters.DateFilter(name='created_at__date', lookup_expr='gte')
max_date = django_filters.DateFilter(name='created_at__date', lookup_expr='lte')
class Meta:
model = Product
fields = {'name': ['icontains']}
项目根下urls.py
# MxShop/urls.py
from django.urls import path,include,re_path
import xadmin
from django.views.static import serve
from MxShop.settings import MEDIA_ROOT
# from goods.view_base import GoodsListView
from rest_framework.documentation import include_docs_urls
from goods.views import GoodsListViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
#配置goods的url
router.register(r'goods', GoodsListViewSet)
router.register(r'categorys', CategoryViewSet, base_name="categorys")
router.register(r'users', UserViewset, base_name="users")
router.register(r'userfavs', UserFavViewset, base_name="userfavs")
router.register(r'messages', LeavingMessageViewset, base_name="messages")
router.register(r'shopcarts', ShoppingCartViewset, base_name="shopcarts")
router.register(r'orders', OrderViewset, base_name="orders")
router.register(r'indexgoods', IndexCategoryViewset, base_name="indexgoods")
urlpatterns = [
path('xadmin/', xadmin.site.urls),
path('api-auth/',include('rest_framework.urls')),
path('ueditor/',include('DjangoUeditor.urls' )),
#文件
path('media/<path:path>',serve,{'document_root':MEDIA_ROOT}),
#drf文档,title自定义
path('docs',include_docs_urls(title='仙剑奇侠传')),
#商品列表页
re_path('^', include(router.urls)),
]
drf的token
缺点:
保存在DB中,如果是一个分布式系统就非常麻烦;
token永久有效,没有过期时间;
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
from rest_framework.authtoken import views
urlpatterns = [
# token
path('api-token-auth/', views.obtain_auth_token)
]
客户端身份验证:
对于客户端进行身份验证,令牌密钥应包含在,关键字应以字符串文字,例如Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
注意:如果您想在header中使用不同的关键字(例如 Bearer),只需子类化TokenAuthentication并设置keyword类变量
如果成功通过身份验证,TokenAuthentication将提供以下凭据:
request.user #是一个Django User实例
request.auth #是一个rest_framework.authtoken.models.Token实例
未经身份验证的响应被拒绝将导致HTTP 401 Unauthorized的响应和相应的WWW-Authenticate header,例如:WWW-Authenticate: Token
要想获取request.user和request.auth要在settings.py中添加
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
)
}
jwt方式,用户认证
pip install djangorestframework-jwt
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication'
)
}
import datetime
#有效期限
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), #也可以设置secnotallow=20
'JWT_AUTH_HEADER_PREFIX': 'JWT', #JWT跟前端保持一致,比如“token”这里设置成JWT
}
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^api-token-refresh/', refresh_jwt_token),
url(r'^api-token-verify/', verify_jwt_token),
]
drf的缓存:
pip install drf-extensions
from rest_framework_extensions.cache.mixins import CacheResponseMixin
class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, Mixins.RetrieveModelMixin,viewsets.GenericViewSet): #必须在第1个位置
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 5 #5s过期,时间自己可以随便设定
}
drf配置redis
pip install django-redis
# redis缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
REST_FRAMEWORK = {
#限速设置
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle', #未登陆用户
'rest_framework.throttling.UserRateThrottle' #登陆用户
),
'DEFAULT_THROTTLE_RATES': {
'anon': '3/minute', #每分钟可以请求两次
'user': '5/minute' #每分钟可以请求五次
}
}
from rest_framework.throttling import UserRateThrottle,AnonRateThrottle
class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin, mixins.RetrieveModelMixin,viewsets.GenericViewSet):
throttle_classes = (UserRateThrottle, AnonRateThrottle)
social_django实现第三方登录
pip install social-auth-app-django
'socail-django' #INSTALLED_APP中引入
python manage.py migrate #DB中多了5张表
# 设置邮箱和用户名和手机号均可登录
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
'social_core.backends.weibo.WeiboOAuth2',
'social_core.backends.qq.QQOAuth2',
'social_core.backends.weixin.WeixinOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
path('', include('social_django.urls', namespace='social'))
Tokne的设计及加密方法
Token的基本设计原则:Token不携带用户敏感信息;无需查询数据库,Token可进行自我验证;
hmac作用,验证消息是否被篡改;公式;无法抵御重放gongji;
hmac.new('secret123456', value).digest() # 加密密钥、value为加密的信息
验证Token的有效性:验证验证码是否一致;验证是否过期;验证这个Token是否属于被请求数据的用户;

