DRF源码分析
题外:
django中间件
-
process_request
-
process_view
-
process_response
-
process_exception
-
process_render_template
执行顺序:
-
process_request ——> 路由匹配——> 执行process_view ——> 执行视图函数 ——>执行process_response;
-
如果执行视图函数出错——>执行process_exception;
-
如果视图函数中有render模板渲染——>执行process_render_template
中间件的使用场景:
-
用户认证(登录校验)
-
权限验证
-
csrf实现
-
django中的csrf如何实现?
原理就是在用户请求进来时验证是否携带随机token;
看起来通过process_request或process_view都能完成,但它是通过process_view方法实现:
-
它需要先检查视图是否被@csrf_exempt装饰 (免除csrf验证),
如果在process_request中拦截,就无法做到这一点。
-
去请求体或cookie中获取token
-
-
django中的FBV,CBV理解
django的请求生命周期
请求 ——> wsgi(wsgiref模块) ——> django中间件 ——> 视图(FBV: 执行视图函数,CBV: 通过dispatch反射类方法)
-
wsgi:
-
wsgi,一个webserver通信协议
-
wsgiref,是实现了wsgi协议的一个模块(django使用),本质:一个socket服务端
-
werkzeug,是实现了wsgi协议的一个模块(flask使用),本质:一个socket服务端
-
tornado是自己实现了socket服务,当然也可以使用wsgiref和werkzeug
-
uwsgi,也是实现了wsgi协议的一个模块,部署时使用。
-
1.FBV
FBV是指基于函数的视图
2.CBV
CBV是基于类的视图
CBV:基于反射实现根据不同请求方式,执行类视图中不同的方法
原理:请求进来会先调用dispatch方法,方法中根据reuqest.method
反射执行类视图中的各类方法(GET/POST/PATCH/PUT/DELETE),然后返回响应对象;整个请求处理过程在dispatch中完成
原码流程:
url
——>类视图的as_view中的view方法
——>dispatch方法
;代码如下:
(原生django中的请求处理流程)django.views.generic.base.py:
@classonlymethod
def as_view(cls, **initkwargs):
......
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
# handler 就是我们定义的的视图方法
handler = getattr(self, request.method.lower(),self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 返回响应对象
我们可以在请求处理前或响应前做一些操作,类视图中,重写dispatch方法:
def dispatch(self, request, *args,**kwargs):
print(before)
res = super().dispatch(request, *args,**kwargs)
print('after')
return res
drf中的请求流程
drf的APIView重写了 dispatch
方法,在 initialize_request
方法中对django的请求request做了二次封装,封装的内容包括:
- django的request
- 所有认证类实例化后的对象列表authenticators
- 所有解析器类实例化后的对象列表parsers
将这些都保存在当前新的request对象上,通过request._request 可以获取原生的request对象;
request = self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 对request进行二次封装
return Request(
request, # django 的 request
parsers=self.get_parsers(), # 配置的所有解析器
authenticators=self.get_authenticators(), # 配置的所有认证类的实例
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
在我们的视图方法调用前,处理一些额外事情:认证,权限检测,限流
self.initial(request, *args, **kwargs)
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request) # 认证 ,调用request.py中的user
self.check_permissions(request) # 权限校验
self.check_throttles(request) # 限流
认证
1.源码执行流程
# views.py: APIView
def dispatch():
# 新的request中封装原生request和认证类的对象列表
self.initialize_request(self, request, *args, **kwargs)
# 执行视图方法前的操作
self.initial(request)
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
def initial():
self.perform_authentication(request)
def perform_authentication():
request.user
# request.py: Request
@property
def user():
self._authenticate()
def _authenticate()
# 使用认证实例对象列表依次验证
for authenticator in self.authenticators:
# 调用认证类中的authenticate方法进行身份验证
user_auth_tuple = authenticator.authenticate(self)
2.关键函数分析
self._authenticate(self)
依次尝试使用每个认证实例对请求进行身份验证。
def _authenticate(self):
# 使用认证实例对象列表依次验证
for authenticator in self.authenticators:
try:
# 调用authenticate方法验证
# 1.如果authenticate方法抛出异常,执行self._not_authenticated()
# 2.有返回值,必须是元组(request.user,request.auth)
# 3.返回None,去执行下一个认证类
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
# 如果所有认证返回的都是None,则执行以下函数,设置一个匿名用户
self._not_authenticated()
3.自定义认证类
必须继承 BaseAuthentication,并实现 authenticate 方法
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# token = request.query_params.get('token') # 按get请求获取,
# if not token: # 获取不到时
# token = request.data.get('token') # 按POST请求获取
token = request.META.get('HTTP_AUTHORIZATION', None)
# 登陆判断 验证jwt
try:
payload = jwt_encode_handler(token)
except:
raise exceptions.AuthenticationFailed() #返回一个403状态码
user = User.objects.get(phone=payload['phone'])
return user, token
权限
权限分为两种:视图访问权限 和 对象访问权限
1.源码执行流程
1.1 视图访问权限:
# views.py: APIView
def dispatch():
self.initialize_request(self, request, *args, **kwargs)
# 执行视图方法前的操作
self.initial(request)
def initial():
self.check_permissions(request)
def check_permissions(self, request):
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
def get_permissions(self):
return [permission() for permission in self.permission_classes]
1.2 对象访问权限
在访问某个对象具体详情时,调用 get_object() 方法使用
# generics.py: GenericAPIView
def get_object(self):
self.check_object_permissions(self.request, obj)
# views.py: APIView
def check_object_permissions(self, request, obj):
for permission in self.get_permissions():
if not permission.has_object_permission(request, self, obj):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
def get_permissions(self):
return [permission() for permission in self.permission_classes]
2.自定义权限类
必须继承 BasePermission,并实现 has_permission 方法
class MyPermission(BasePermission):
message = "您没有权限访问"
def has_permission(self, request, view):
"""视图访问权限控制"""
return True # True有权访问/False无权访问
def has_object_permission(self, request, view, obj):
"""对象访问权限控制,调用 get_object() 时使用"""
return True
节流
1.源码执行流程
# views.py: APIView
def dispatch():
self.initialize_request(self, request, *args, **kwargs)
# 执行视图方法前的操作
self.initial(request)
def initial():
self.check_throttles(request)
def check_throttles(self, request):
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
self.throttled(request, throttle.wait())
def get_throttles(self):
return [throttle() for throttle in self.throttle_classes]
2.自定义节流类
2.1 继承 基础的节流类 BaseThrottle:
需要实现 allow_request
和 wait
方法
# 访问记录 实际应该放在缓存(drf默认节流类就是存放在缓存);放在全局变量,程序重启会清空;
VISIT_RECORD = {}
class VisitThrottle(BaseThrottle):
"""60s内只能访问3次"""
def __init__(self):
self.history = None
def allow_request(self, request, view):
# 获取用户ip (也可以基于用户名来限制)
# remote_addr = request.META.get('REMOTE_ADDR')
remote_addr = self.get_ident(request) # 调用父类方法获取唯一标识
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() # 60s以前的记录直接删掉
if len(history) < 3:
history.insert(0, ctime)
return True
# 返回True允许访问, False/None表示超过频率,被限制
def wait(self):
"""提示还需要等待n秒才能访问"""
ctime = time.time()
n = 60 - (ctime - self.history[-1])
return n
2.2 继承 内置节流类 SimpleRateThrottle:
基本功能都已经帮我们实现好了,只需实现get_cache_key
,再配置settings就行:
class VisitThrottle(SimpleRateThrottle):
"""匿名用户节流类(根据ip)"""
scope = ‘yds’ # 设置一个key,用于去settings配置取值(需自己全局配置)
def get_cache_key(self, request, view):
"""获取缓存的key:必须自己实现这个方法"""
return self.get_ident(request) # 对匿名用户做限制
class UserVisitThrottle(SimpleRateThrottle):
"""登录用户节流类(根据用户名)"""
scope = ‘yds_user’ # 设置一个key,用于去settings配置取值(需自己全局配置)
def get_cache_key(self, request, view):
return request.user # 对登录用户做限制
还需全局配置:
REST_FRAMEWORK = {
# 如果有两个节流类不要都配置在全局,可以指定接口局部使用
# "DEFAULT_THROTTLE_CLASSES": ["utils.throttle.VisitThrottle",]
"DEFAULT_THROTTLE_RATES": {
"yds": '3/m' # 匿名用户每分钟3次
"yds_user": '10/m' # 登录用户每分钟10次
}
}
SimpleRateThrottle
源码的方法:
def get_cache_key(self, request, view):
def get_rate(self):
def parse_rate(self, rate):
def allow_request(self, request, view):
def throttle_success(self):
def throttle_failure(self):
def wait(self):
解析器
1.解析器是干什么的
解析器:就是根据请求头Content-Type
对请求体的数据进行解析,变成我们代码中的数据结构
请求头 Content-Type
: 指明发给后端的数据的类型(也就是请求体格式)
根据不同的 Content-Type
需使用合适的解析器进行解析
2.django的解析器
request.POST
和 request.body
一般情况下都可以用于获取前端传来的数据,request.POST是由request.body在特定条件下解析而来;所以有时request.POST不一定能取到传来数据,它需要满足两个要求:
-
请求头要求:
Content-Type: 'application/x-www-form-urlencode'
-
数据格式要求:
name=alex&age=18&gender=男
django只支持满足以上要求,才会去request.body中解析数据赋给request.POST
如: 表单提交 和 ajax提交
它们默认headers:{"Content-Type": "application/x-www-form-urlencode"},并且在内部都会将data中的数据转化为 name=alex&age=18&gender=男
这种形式
但ajax也可以自己定制请求头参数,其它情形则无法解析:
情况一:
$ajax({
url:...,
type:POST,
headers:{"Content-Type":"application/json"}
data:{name:alex, age=18} # 内部转化`name=alex&age=18`
})
# body有值,POST无
情况二:
$ajax({
url:...,
type:POST,
headers:{"Content-Type":"application/json"}
data:JSON.stringfy({name:alex,age=18})
})
# body有值,POST无
#取值: json.loads(request.body.decode())
3. drf的解析器
代码位于 rest_framework.parsers.py
3.1 解析器介绍
drf的内置解析器常用的有 JsonParser 和 FormParser两个类 ,还有两个文件上传的解析器类 MultiPartParser 和 FileUploadParser:
JsonParser:支持解析 请求头{"Content-Type":"application/json"} 的json数据
FormParser:支持解析 请求头{"Content-Type":"application/x-www-form-urlencode"}
视图中设置解析器:
class ParserView(APIView):
parser_classes = [JsonParser, FormParser] # 同时支持两种头的解析
def post(self,request)
调用request.data时会去执行解析操作
# 1.获取用户请求
# 2.获取用户请求体
# 3.根据请求头 和 解析器parser_classer=[JsonParser, FormParser]支持的头进行比较
# 5.使用合适的解析器解析请求体,赋值给request.data
3.2 源码执行流程
drf中使用解析器,是由 request.data 去触发调用解析器类
# 请求进来时dispatch()中的initialize_request()方法封装所有解析器类实例到request对象
# 调用request.data
# request.py: Request
@property
def data(self):
self._load_data_and_files()
def _load_data_and_files(self):
self._parse()
def _parse(self): # self 就是请求对象request
# 获取用户提交的请求头Content-Type
media_type = self.content_type
# 请求头匹配解析器,选择合适的解析器类;方法下面详解
parser = self.negotiator.select_parser(self, self.parsers)
# 调用选好的解析器的parser方法进行解析
parser.parse(stream, media_type, self.parser_context)
def select_parser(self, request, parsers):
# content_type:用户提交的头
# media_type:解析器设置的头
# 使用 content_type 和我们配置的解析器类中的 media_type 进行匹配
for parser in parsers:
if media_type_matches(parser.media_type, request.content_type):
return parser
return None
序列化
将模型对象转化为字典格式,再转化为json来进行数据传输
1. django中的序列化
def get(request):
roles = models.Role.objects.all().values('id','title')
roles = list(roles)
ret = json.dumps(roles,ensure_ascii=False)
return HttpResponse(ret)
2. drf序列化
2.1 使用
2.1.1 自定义字段的显示
Serializer:
user_type_choice = (
(1,'普通用户'),
(2,'vip'),
(3,'svip'),
)
class User(models.Model):
username = ...
pasword = ...
user_type = IntegerField(choices=user_type_choice)
group = models.ForeignKey('UserGroup') # 所在组
roles = models.ManyToManyField('Role') # 喜欢的角色
class UserSerializer(serializers.Serializer):
xxx = serializer.CharField(source='user_type') # row.user_type
# get_user_type_display() 用来取choice字段的显示值
ooo = serializer.CharField(source='get_user_type_display') # row.get_user_type_display()
gp = serializers.Charfield(source="group.id")
rls = serializers.SerializerMethodField() # 自定义显示
x1 = MyField(source='username') # 自定义字段类
-
source
source 一般是指定对应的模型字段,若指定,则序列化类属性可随意命名xxx
-
source指向的值,内部会判断其是否可执行,诸如callable(arg):
是则加()执行:如get_user_type_display() 获取对应的用户类型中文
否则取值: user_type 对应数据库存的数字 ;
-
source若指向的是外键字段,如group,则显示为一个object样子的字符串,
可以通过如 group.id 取到关联的模型的字段值 ,内部会根据 ''." 来做切分,如果连续多个关联模型字段:group. x.y.z 则会一直取下去,最终再判断是否可执行
-
-
serializers.SerializerMethodField()
用于自定义模型字段的显示
def get_rls(self, row): role_obj_list = row.roles.all() ret = [] for item in role_obj_list: ret.append({'id':item.id,'title':item.title}) return ret
-
通过自定义字段类 来自定义字段显示 (不常用)
class Myfield(serializers.CharField): def to_representation(self, value): print(value) return value
ModelSerializer:
class ModelUserSerializer(serializers.ModelSerializer):
# 加额外字段
ooo = serializer.CharField(source='get_user_type_display')
class Meta:
model = models.User
# fields = "__all__"
fields = ['username', 'pwd', 'ooo','group']
# read_only_fields = ['user'] # 只用于显示
extra_kwargs = {'user':{'min_length':6},'pwd':{'validators': [PasswordValidator(666)]}}
-
extra_kwargs
表示为指定的序列化字段Field添加参数;一般在类中定义字段时直接传参
2.1.2 深度序列化关联表
当模型中有foreignkey 和 manytomany 这中关联外键字段时,使用 depth
参数可以对关联表进行深度序列化
class ModelUserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
# fields = "__all__"
fields = ['username', 'pwd','group']
depth = 1 # 序列化外键字段,展示关联表的所有字段值
# depth = 0 表示只取当前模型的字段值,每加一层表示向下取关联模型的字段值
# 推荐取值范围 0~5, 太深的连表操作影响效率
2.1.3 生成hypermedialink
可以根据模型的外键字段的id,在序列化返回的结果中再生成一个url,用于访问关联表的字段值
class UserSerializer(serializers.ModelSerializer):
group = serializers.Hypermedialink(view_name='gp',lookup_field='group_id',lookup_url_kwarg='pk') # lookup_url_kwarg 默认pk
class UserView(APIView):
def get(self,request)
users = User.objects.all()
ser = UserSerializer(instance=users,many=True,context={'request':request})
return HttpReponse(json.dumps(ser.data,ensure_ascil=False))
url配置:
url(r'^group/(?P<pk>\d+)$', views.GroupView.as_view(), name='gp')
最后会根据group的id,反向生成一个url: group/1,类似于drf的通用试图List返回的数据那个 preurl 和nexturl
2.2 源码流程
class UserView(APIView):
def get(self,request)
users = User.objects.all()
# 单个对象,交给Serializer类处理 self.torepresentation
# queryset,ListSerializer类处理 self.torepresentation
ser = UserSerializer(instance=users,many=True)
ser.data
通过ser.data可以往底层一步步查看序列化源码执行流程:
# serializers.py: Serializer
@property
def data(self):
ret = super(Serializer, self).data
return ReturnDict(ret, serializer=self) #序列结果封装为有序字典
@property
def data(self):
# 分支1:序列化展示
self._data = self.to_representation(self.instance)
# 分支2:反序列化校验
self._data = self.to_representation(self.validated_data)
def to_representation(self, instance):
# 循环序列化器定义的所有Field
for field in fields:
attribute = field.get_attribute(instance)
# fields.py: Field
def get_attribute(self, instance):
return get_attribute(instance, self.source_attrs)
# self.source_attrs就是传入的source分割后的结果:[group,title]
def bind(self, field_name, parent):
# 该函数对我们传入的source做了一些操作
if self.source is None:
self.source = field_name
# self.source_attrs is a list of attributes that need to be looked up
# when serializing the instance, or populating the validated data.
if self.source == '*':
self.source_attrs = []
else:
# 上面说到的按传入source进行分割后再去关联表取值
self.source_attrs = self.source.split('.')
def get_attribute(instance, attrs):
if isinstance(instance, Mapping):
instance = instance[attr]
else:
instance = getattr(instance, attr)
......
# 如果为函数则加括号执行,如get_user_type_display
if is_simple_callable(instance):
instance = instance()
3. drf反序列验证
3.1 使用
class XXValidator(object):
def__init__(self, base):
self.base = base
def __call__(self, value):
if not value.startswith(self.base):
message = f'标题需以{self.base}开头'
raise serializer.ValidationError(message)
def set_context(self,serializer_field):
# 执行验证之前调用,可不定义
pass
class UserGroupSerializer(serializers.Serializer):
# error_message 自定义验证不通过的错误信息
# validators 也可以指定方法
title = serializer.CharField(error_message={'required':'标题不能为空'}, validators=[XXValidator('小屁孩')])
自定义验证规则的几种方法:
- 使用 validators 参数,可使用类和函数
- 方法 validate__字段名 ,单独验证某个字段
- 方法 validate,验证所有字段
方法中验证不通过抛异常:
raise exceptions.ValidationError('error message')
3.2 源码执行流程
ser.is_valid() 会触发字段校验
# serializers: BaseSerializer
def is_valid(self, raise_exception=False):
self.run_validation(self.initial_data)
# fields: Field
def run_validation(self, data=empty):
# 先执行字段内部的验证,包括(字段本身正则,validate_xx钩子函数)
value = self.to_internal_value(data)
# 字段类中使用了validators参数的进一步验证
self.run_validators(value)
# serializers: Serializer
def to_internal_value(self, data):
......
for field in fields:
# 获取自定义的 validate_字段 钩子方法
validate_method = getattr(self, 'validate_' + field.field_name, None)
# 先字段本身的正则校验
validated_value = field.run_validation(primitive_value)
# 若validate_method有定义,调用 validate_字段 方法进一步校验
validated_value = validate_method(validated_value)
# fields: Field (先调用Serializer中run_validators,再调用其父类Field中的先调用Serializer中run_validators)
def run_validators(self, value):
# 循环调用传入字段的validators列表,validators=[XXValidator('小屁孩')
for validator in self.validators:
validator.set_context(self)
# 调用validator进行校验
validator(value)
分页
1. 分页类型
- a. 看第n页, 每页显示n条数据
- b. 在第n个位置,向后查看n条数据(类似mysql的limit,offset)
- c. 加密分页,上一页和下一页
2. 分页时的问题
一般做翻页时,每次扫描前面所有数据,取最后指定n条查看,越往后扫描越多,加载会越慢
解决办法,只允许选择上一页和下一页:
每次翻页时,取到记录的最大id和最小id,根据这个id直接跳到指定位置获取n条数据展示
-
这种方式也可解决App,小程序中翻页数据顺序错乱的问题 (同一时刻大量用户插入记录,会导致翻页时可能新数据展示在旧数据之后)
-
加密分页器
CursorPagination
,就是采取这种方式,数据量大时翻页效率快
3. drf分页器
3.1 drf内置3种分页类
-
PageNumberPagination
: 看第n页, 每页显示n条数据 -
LimitOffsetPagination
:在第n个位置,向后查看n条数据 -
CursorPagination
:加密分页,只能上一页和下一页,内部根据当前页最大id,最小id来翻页,不用每次扫面前面所有记录;实现的部分源码如下:def paginate_queryset(self, queryset, request, view=None): if self.cursor.reverse != is_reversed: kwargs = {order_attr + '__lt': current_position} else: kwargs = {order_attr + '__gt': current_position}
3.2 使用分页器
class Pager1View(APIView):
def get(self, request, *args, **kwargs):
roles = Role.objects.all()
# 使用其它分页类型只需替换分页器类即可
pg = PageNumberPagination()
# 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
ser = PageSerializer(instance=pager_roles,many=True)
# return Response(ser)
# 使用封装好分页的http响应
return pg.get_paginated_response(ser.data)
3.3自定义分页器
- 默认的PageNumberPagination分页器无法调整每页显示条数,需配置page_size_query_param
- CursorPagination 对分页页码做了加密,只能通过当前页来获取next页的页码,若使用
get_paginated_response()
将返回下一页的链接- url长这样:"/api/user/?cursor=cD02"
# 继承PageNumberPagination
class MyPagination(PageNumberPagination):
page_size = 2 # 每页显示条数
page_size_query_param = 'size'
max_page_size = 5 # 每页最大显示条数
page_query_param = 'page'
# 继承LimitOffsetPagination
class MyPagination(LimitOffsetPagination):
default_limit = 2
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
# 继承CursorPagination 加密分页,
class MyPagination(CursorPagination):
cursor_query_param = 'cursor'
page_size = 10
invalid_cursor_message = _('Invalid cursor') # 错误页码返回信息
ordering = '-id' # 根据指定字段排序,默认'-created'
page_size_query_param = 'size'
max_page_size = 20
视图
1. drf中可以继承的视图类
-
view : django的视图
-
APIView(View) : drf封装的基础视图
-
GenericAPIView(APIView) :多了几个方法,基本无意义
-
GenericViewSet(ViewSetMixin, generics.GenericAPIView) :路由配置上视图名需和请求方法对应:as_view({'get': 'list','post': 'create'})
-
ModelViewSet : 增删改查的视图集,继承如下
ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet) # 路由上需要对应两种路由,带id参数(查详情,删,改) 和 不带的(查列表,创建)
-
增删改查也可单独继承,分开使用,都需继承核心GenericViewSet
2.视图中的核心方法
我们视图中常使用的方法,大都在views.py 和 generics.py 文件中
generics.py:GenericAPIView
def get_queryset(self)
"""返回queryset查寻集"""
def get_object(self)
"""返回指定记录对象,默认根据id,可使用look_up参数配置"""
def get_serializer(self, *args, **kwargs)
"""使用一个序列化器类创建对象返回"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
def get_serializer_class(self)
"""选择一个序列化器类,用它为’查询‘和’创建‘分配不同的序列化器"""
def get_serializer_context(self)
"""封装一些参数,如request"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
def filter_queryset(self, queryset)
"""返回过滤后的queryset,配合filter过滤器使用"""
def paginator(self)
"""返回一个分页对象"""
def paginate_queryset(self, queryset)
"""返回分页后的数据集queryset"""
self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data)
"""返回一个封装了分页信息(previous,next,count)的响应对象"""
路由
1. drf中的路由类
-
SimpleRouter
-
DefaultRouter
使用视图集 ModelViewSet 时,可以自动生成路由
from rest_framework import routers
router = router.DefaultRouter()
# 会自动生成五个url,列表('/xxx/'),详情('/xxx/?P<pk>\d+/'),增('/xxx/'),删('/xxx/?P<pk>\d+/'),改('/xxx/?P<pk>\d+/')
router.register(r'xxx', views.UserView)
urlpatterns = [
url(r'^', include(router.urls))
]
渲染器
渲染器:控制返回响应数据的格式和显示样式
1 drf中的渲染器
-
JSONRenderer : api接口返回给前端 json 形式的数据
-
BrowsableAPIRenderer : 浏览器直接访问视图时,展示的样式(我们经常看到的一个drf响应json数据的界面,可以去drf静态模板文件中【base.html】,改成自己喜欢的)
-
AdminRenderer:浏览器直接访问视图时,数据记录显示为一个表格样式
-
HTMLFormRenderer
一般只需要使用 JSONRenderer 就行了,其他都是景上添花,无太大意义!!!
配置渲染器后,在路由后面通过 format参数可使用对应样式,如 ?format=json或admin
其它drf组件使用,及源码分析,后面再记录
作者: Deaseyy
出处: http://www.cnblogs.com/Deaseyy/
新手一枚,请大佬们多多赐教!
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接