django restfulwork 源码剖析
from django.views import View
class StudentView(View): def get(self,request):
return HttpResponse('GET') def post(self,request):
return HttpResponse('POST') def put(self,request):
return HttpResponse('POST') def delete(self,request):
return HttpResponse('DELETE')
那么url必须这么写:
from app01 import views
urlpatterns = [
url(r'^students/$', views.StudentView.as_view()),
]
使用postman进行测试:
查看源码预备知识:封装
#!/usr/bin/env python
# -*- coding: utf-8 -*- class Request(object): def __init__(self, obj):
self.obj = obj @property
def user(self):
return self.obj.authticate() class Auth(object): def __init__(self, name, age):
self.name = name
self.age = age def authticate(self):
return self.name class APIView(object): def dispatch(self):
self.f2() def f2(self):
a = Auth('charles', 18)
req = Request(a)
print(req.user) obj = APIView()
obj.dispatch()
CBV实现原理: 在View类中有一个dispatch方法,在每个请求到达之后,会先执行,获取请求的method,然后通过反射,执行子类中对应的方法;
from django.views import View
class StudentView(View): def dispatch(self, request, *args, **kwargs):
# return HttpResponse('dispatch')
func = getattr(self,request.method.lower())
ret = func(request, *args, **kwargs)
return ret def get(self,request):
return HttpResponse('GET') def post(self,request):
return HttpResponse('POST') def put(self,request):
return HttpResponse('POST') def delete(self,request):
return HttpResponse('DELETE')
由上面的例子可以看到,dispatch方法是所有使用CBV的视图必须使用到的方法,为了避免每一个类都实现这个方法,可以通过类的继承,来避免代码的重复:
在下面的例子中,基类MyBaseView实现了一个dispatch方法,子类StudentView实现了继承了MyBaseView和View两个类,在实例化StudentView,并执行其方法的时候,会先在StudentView中寻找dispatch方法,如果没有,会去MyBaseView中去寻找,MyBaseView没有父类,所以会看是self是谁,然后从self的另外一个父类View中去寻找; 顺序是StudentView-->MyBaseView-->View
from django.views import View class MyBaseView(object):
def dispatch(self,request, *args, **kwargs):
print('before')
ret = super(MyBaseView, self).dispatch(request, *args, **kwargs)
print('after')
return ret class StudentView(MyBaseView,View): def get(self,request,*args, **kwargs):
return HttpResponse('GET') def post(self,request,*args, **kwargs):
return HttpResponse('POST') def put(self,request,*args, **kwargs):
return HttpResponse('POST') def delete(self,request,*args, **kwargs):
return HttpResponse('DELETE')
二、Django中间件
1. 中间件执行顺序(中间件最多可以实现5个方法)
正常顺序: 执行所有process_request-->路由匹配-->执行所有process_view-->执行视图函数-->process_response
如果有报错: 执行所有process_request-->路由匹配-->执行所有process_view-->执行视图函数-->process_response
如果视图函数有render: 执行所有process_request-->路由匹配-->执行所有process_view-->执行视图函数-->process_response/process_render_template;
2. 使用中间件做过什么?
- 权限
- 用户登录验证
- django csrf token
那么用户的csrf token是如何实现的?
FBV:在django中,csrf token检测是在process_view方法中实现的,会检查视图是否使用@csrf_exempt,然后去请求体或者cookie中获取token;
如果不使用中间件做csrf token认证,那么可以加@csrf_protect,对指定的实视图做验证;
CBV: 有两种实现方法csrf_exempt
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator class StudentView(View): @method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(StudentView,self).dispatch(request, *args, **kwargs) def get(self,request,*args, **kwargs):
return HttpResponse('GET') def post(self,request,*args, **kwargs):
return HttpResponse('POST') def put(self,request,*args, **kwargs):
return HttpResponse('POST') def delete(self,request,*args, **kwargs):
return HttpResponse('DELETE')
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator @method_decorator(csrf_exempt, name='dispatch')
class StudentView(View):
def get(self,request,*args, **kwargs):
return HttpResponse('GET') def post(self,request,*args, **kwargs):
return HttpResponse('POST') def put(self,request,*args, **kwargs):
return HttpResponse('POST')
三、restful 规范
1.根据method 不同,做不同的操作
url(r'^order/$', views.order), def order(request):
if request.method == 'GET':
return HttpResponse('获取订单')
elif request.method == 'POST':
return HttpResponse('创建订单')
elif request.method == 'PUT':
return HttpResponse('更新订单')
elif request.method == 'DELETE':
return HttpResponse('删除订单')
参考:https://www.cnblogs.com/wupeiqi/articles/7805382.html
四、restframework
使用自定义的类,实现API 认证; 具体看源码,和CBV 执行流程类似;
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework import exceptions
class MyAuthenticate(object): def authenticate(self,request):
token = request._request.GET.get('token')
if not token:
raise exceptions.AuthenticationFailed('用户认证失败') def authenticate_header(self, val):
pass class DogView(APIView):
authentication_classes = [MyAuthenticate, ] def get(self,request,*args, **kwargs):
ret = {
'code': 1000,
'msg': 'xxx'
}
return HttpResponse(json.dumps(ret),status=201) def post(self,request,*args, **kwargs):
return HttpResponse('POST') def put(self,request,*args, **kwargs):
return HttpResponse('POST') def delete(self,request,*args, **kwargs):
return HttpResponse('DELETE')
需要掌握的内容:
1.中间件
2.CBV
3.csrf
4.规范
5.djangorestframework
- 如何验证(基于数据库实现用户认证)
-源码流程(面向对象回顾流程)
五、restframework之登录
问题: 有些API用户登录之后才可以访问,有些不需要用户登录;
先创建两张表
class UserInfo(models.Model): # 用户表,存储用户信息
user_type_choices = (
(1,'普通用户'),
(2,'VIP'),
(3,'SVIP'),
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=64) class UserToken(models.Model): # 存储用户登录成功之后的token
user = models.OneToOneField(to='UserInfo')
token = models.CharField(max_length=64)
编写API
url(r'^api/v1/auth/$', views.AuthView.as_view()) from django.http import JsonResponse # Create your views here. from rest_framework.views import APIView
from api import models def md5(user):
import hashlib
import time ctime = str(time.time())
m = hashlib.md5(bytes(user, encoding='utf-8'))
m.update(bytes(ctime, encoding='utf-8'))
return m.hexdigest() class AuthView(APIView):
"""
用于用户登录认证
""" def post(self, request, *args, **kwargs):
ret = {'code': 10000, 'msg': None }
try:
user = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用户名或密码错误'
# 为登录用户创建token
token = md5(user)
# 用户token存在就更新,不存在就创建
models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
ret['token'] = token # 将token返回给用户
except Exception as e:
ret['code'] = 1002
ret['msg'] = '请求异常'
return JsonResponse(ret)
使用postman发送请求进行验证
六、 rest framework之基于token实现基本用户认证
上面的例子是用户通过访问登录认证的API,获取返回的token,并将token存储到token表中;
用户拿到这个token之后,就可以访问其他的API了;
from rest_framework.views import APIView
from rest_framework.views import exceptions
from rest_framework.authentication import BaseAuthentication
from api import models ORDER_DICT = {
1: {
'name': 'charles',
'age': 18,
'gender': '男',
'content': '....'
},
2: {
'name': '男',
'age': 19,
'gender': '男',
'content': '......'
},
} class Authtication(object): def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在restframework内部会将整个两个字段赋值给request,以供后续继续使用
return (token_obj.user, token_obj) def authenticate_header(self, request):
pass class OrderView(APIView):
"""
订单相关业务
"""
authentication_classes = [Authtication, ] # 在访问API的时候,先走这个用户认证的类 def get(self, request, *args, **kwargs):
ret = {'code': 1000, 'msg': None, 'data': None}
# request.user --> token_obj.user
# request.auth --> token_obj
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
七、rest framework之认证基本流程源码分析
1、请求进来之后,会先执行dispatch()方法;
class OrderView(APIView):
"""
订单相关业务
"""
authentication_classes = [Authtication, ] def get(self, request, *args, **kwargs):
self.dispatch() # 使用pycharm进入dispatch方法,查看源码
2、先对request进行封装
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs) # 对request进行封装,点击继续查看该方法 def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
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):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes] # 通过列表生成式,执行self.authentication_classes方法,因为子类中没有这个方法,
那么会执行父类中的这个方法; 点击继续查看; authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 这个是存在于父类中的; 我们可以在自己的实例化的子类中使用自定义的类,替代这个类; 默认升是
BaseAuthentication
3、认证
self.initial(request, *args, **kwargs) # 继续点击查看; self.perform_authentication(request) # 实现认证; def perform_authentication(self, request):
request.user # 执行request.user @property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
# 获取认证对象,进行进一步认证
self._authenticate()
return self._user def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
# 循环所有authenticator对象
for authenticator in self.authenticators:
try:
# 执行authenticate方法
# 1.如果authenticate 方法抛出异常,self._not_authenticated执行
# 2. 没有抛出异常,有返回值,必须是元祖:(request.user, request.auth)
# 3. 返回None,我不管,下一个认证进行处理;
# 4.如果都返回None,执行self._not_authenticated(),返回(AnonymousUser, None)
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise if user_auth_tuple is not None:
self._authenticator = authenticator
# request.user和request.auth
self.user, self.auth = user_auth_tuple
return def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None.
"""
self._authenticator = None if api_settings.UNAUTHENTICATED_USER: # AnonymousUser 匿名用户
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN:
self.auth = api_settings.UNAUTHENTICATED_TOKEN() # None
else:
self.auth = None
简要流程图如下:
八、rest framework之匿名用户配置
1.认证类的全局配置(全局使用)
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 认证类的默认配置 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK': # 获取settings的REST_FRAMEWORK 的配置项
api_settings.reload()
在settings中定义 REST_FRAMEWORK 配置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication']
} # 将认证的类,放入到上面配置的路径里面 # 视图函数中不包含上述的认证的类,并且要将登陆的API的authentication_classes设为空;
class AuthView(APIView):
authentication_classes = []
def post(self, request, *args, **kwargs):
pass class OrderView(APIView):
"""
订单相关业务
""" def get(self, request, *args, **kwargs):
ret = {'code': 1000, 'msg': None, 'data': None}
# request.user --> token_obj.user
# request.auth --> token_obj
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
2、在用户没有登录(匿名用户的时候)
def _not_authenticated(self):
"""
Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None.
"""
self._authenticator = None if api_settings.UNAUTHENTICATED_USER: # 获取settings中的用户默认用户是啥
self.user = api_settings.UNAUTHENTICATED_USER()
else:
self.user = None if api_settings.UNAUTHENTICATED_TOKEN: # 获取settings中的默认token是啥
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
else:
self.auth = None
没有通过认证,默认为匿名用户
class UserInfo(APIView): authentication_classes = []
def get(self,request, *args, **kwargs):
ret = {'code': 1000, 'msg': None, 'data': None}
print(request.user)
return JsonResponse(ret # 打印结果
AnonymousUser # 在settings.py中增加如下配置,未登录时,用户和auth都为None,方面后续判断用户是否登录;
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication'],
'UNAUTHENTICATED_USER':None, #request.user
'UNAUTHENTICATED_TOKEN':None # request.auth
}
九、rest framework之内置基本认证
1.BaseAuthentication 基类,可以规范自定义的认证的类
from rest_framework.authentication import BaseAuthentication, BasicAuthentication class FirstAuthtication(BaseAuthentication): def authenticate(self, request):
pass def authenticate_header(self, request):
pass class Authtication(BaseAuthentication): def authenticate(self, request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在restframework内部会将整个两个字段赋值给request,以供后续继续使用
return (token_obj.user, token_obj) def authenticate_header(self, request):
return 'Basic realm="api"' # api信息加入请求头
2.其他认证 BasicAuthentication
十、rest framework之权限的基本使用
使用方法和自定义认证类非常类似
1.定义权限类
# 定义权限类
class MyPermisson(object): def has_permission(self, request, view):
if request.user.user_type != 3:
return False
return True class MyPermisson1(object):
def has_permission(self, request, view):
if request.user.user_type == 3:
return False
return True
2. 使用自定义类
class OrderView(APIView):
"""
订单相关业务
"""
permission_classes = [MyPermisson, ] def get(self, request, *args, **kwargs):
ret = {'code': 1000, 'msg': None, 'data': None}
# request.user --> token_obj.user
# request.auth --> token_obj
十一、 rest framework之权限源码流程
# 查看dispatch中检测权限的方法
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():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
定义全局的权限检测类,并使用全局配置定义全局权限检测的类
class SVIPPermisson(object):
message = '必须是SVIP才能访问'
def has_permission(self, request, view):
if request.user.user_type != 3:
return False
return True #定义配置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication'],
'UNAUTHENTICATED_USER':None, #request.user
'UNAUTHENTICATED_TOKEN':None, # request.auth
'DEFAULT_PERMISSION_CLASSES':['api.utils.permission.SVIPPermisson'] # request.auth
}
十二、rest framework之权限的内置类
django 内置的权限类在实际生成的环境不建议使用,但是建议继承,可以帮助规范自定义权限类的方法名称;
除了BasePermission之外,还有其他的类:
#实现代码:
from rest_framework.permissions import BasePermission class SVIPPermisson(BasePermission):
message = '必须是SVIP才能访问'
def has_permission(self, request, view):
if request.user.user_type != 3:
return False
return True class MyPermisson1(BasePermission):
def has_permission(self, request, view):
if request.user.user_type == 3:
return False
return True
十三、rest framework之访问频率控制基本实现
import time VISIT_RECODE = {} # 放在全局变量中,重启之后,就会变空,可以放到缓存中
from rest_framework.throttling import BaseThrottle
class VisitThrottle(object):
"""60秒内只能访问3次"""
def __init__(self):
self.histoy = None def allow_request(self, request, view):
# 1.获取用户IP
remote_addr = request.META.get('REMOTE_ADDR')
print(remote_addr)
ctime = time.time()
if remote_addr not in VISIT_RECODE:
VISIT_RECODE[remote_addr] = [ctime, ]
return True
history = VISIT_RECODE.get(remote_addr)
self.histoy = history
while history and history[-1] < ctime -60: # 如果记录的时间戳超过60s以内,就删除;
history.pop() if len(history) < 3:
history.insert(0, ctime)
return True
# return True # 表示可以继续访问
# return False # 表示访问频率太高,被限制 def wait(self):
"""还有等多少秒才能访问 return 10 等10S才能访问"""
ctime = time.time()
return 60 - (ctime - self.histoy[-1]) class AuthView(APIView):
authentication_classes = []
throttle_classes = [VisitThrottle,]
十四、rest framework之访问频率控制源码流程
源码流程和上述认证与权限源码流程类似,下面使用全局配置类控制访问频率:
将上述代码挪到目录api.utils.thottle.VisitThrottle
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication'],
'UNAUTHENTICATED_USER': None, # request.user
'UNAUTHENTICATED_TOKEN': None, # request.auth
'DEFAULT_PERMISSION_CLASSES':['api.utils.permission.SVIPPermisson'], # request.auth
'DEFAULT_THROTTLE_CLASSES': ['api.utils.thottle.VisitThrottle'] # 全局生效
}
十五、rest framework之基于内置类实现访问频率控制
实际上,内置的访问频率类已经实现了上述的方法,可以通过配置来自定义访问频率,VISIT_RECODE 是放置在缓存中的:
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle class VisitThrottle(SimpleRateThrottle):
scope = 'visit' def get_cache_key(self, request, view):
return self.get_ident(request) # 获取用户IP class UserThrottle(SimpleRateThrottle):
scope = 'user' def get_cache_key(self, request, view):
return request.user.username ###配置###
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.FirstAuthtication', 'api.utils.auth.Authtication'],
'UNAUTHENTICATED_USER': None, # request.user
'UNAUTHENTICATED_TOKEN': None, # request.auth
'DEFAULT_PERMISSION_CLASSES':['api.utils.permission.SVIPPermisson'], # request.auth
'DEFAULT_THROTTLE_CLASSES': ['api.utils.thottle.VisitThrottle'], #默认为对匿名用户做限制
'DEFAULT_THROTTLE_RATES': {
'visit': '3/m', # 一分钟访问3次
'user': '10/m'
}
} ###同时对登录用户做限制#####
from api.utils.thottle import UserThrottle
class OrderView(APIView):
"""
订单相关业务
"""
# permission_classes = [SVIPPermisson, ]
throttle_classes = [UserThrottle, ] # 只有用户登录才可以查看订单,使用另外一个访问频率限制类;
十六、版本
1、在url中通过GET传参:
使用自定义的类解析版本
class ParamVersion(object):
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get('version')
return version class UserView(APIView):
versioning_class = ParamVersion
permission_classes = []
authentication_classes = []
throttle_classes = []
def get(self, request, *args, **kwargs):
print(request.version)
return HttpResponse('用户列表') #get请求如下: http://127.0.0.1:8080/api/users/?version=v3
使用内置的类解析版本参数,还可以通过配置定义默认的版本以及允许的版本:
from rest_framework.versioning import QueryParameterVersioning, class ParamVersion(object):
def determine_version(self, request, *args, **kwargs):
version = request.query_params.get('version')
return version class UserView(APIView):
versioning_class = QueryParameterVersioning
permission_classes = []
authentication_classes = []
throttle_classes = []
def get(self, request, *args, **kwargs):
print(request.version)
return HttpResponse('用户列表') #####settings#####
REST_FRAMEWORK = {
'DEFAULT_VERSION' : 'v1', # 默认的版本
'ALLOWED_VERSIONS' : ['v1', 'v2'], # 允许请求的版本
'VERSION_PARAM': 'version', # 版本的参数的key
}
2、在URL中传参(推荐使用): 版本在使用的时候,无需自定义,使用下面的方式就可以实现了;
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view()),
] from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
class UserView(APIView):
versioning_class = URLPathVersioning # 除了在这儿设置之外,还可以在配置中设置
permission_classes = []
authentication_classes = []
throttle_classes = []
def get(self, request, *args, **kwargs):
print(request.version)
return HttpResponse('用户列表') ###settings.py######在配置中设置
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION' : 'v1',
'ALLOWED_VERSIONS' : ['v1', 'v2'],
'VERSION_PARAM': 'version'
}
十七、rest framework框架之版本源码
# 可以在视图中反向解析URL
from django.urls import reverse
class UserView(APIView):
# versioning_class = ParamVersion
# versioning_class = URLPathVersioning
permission_classes = []
authentication_classes = []
throttle_classes = []
def get(self, request, *args, **kwargs):
# 1.获取版本
print(request.version)
# 2.获取处理版本的对象
print(request.versioning_scheme) # 3.反向生成URL(REST FRAMEWORK)
url1 = request.versioning_scheme.reverse(viewname='uuu', request=request)
print(url1) # 4.反向生成URL
url2 = reverse(viewname='uuu', kwargs={'version': 2})
print(url2)
return HttpResponse('用户列表') ###打印结果
<rest_framework.versioning.URLPathVersioning object at 0x04335D50>
http://127.0.0.1:8080/api/v2/users/
/api/2/users/
十八、解析器
1.解析器预备知识(post提交的数据,会保存在request.body中,转换为QueryDict才能被request.post获取到)
#点击查看源码,
from django.core.handlers.wsgi import WSGIRequest
elif self.content_type == 'application/x-www-form-urlencoded':
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() # #如果想通过request.POST获取到post提交的数据,那么必须满足如下两个条件:
django:request.POST/ request.body
1. 请求头要求:
Content-Type: application/x-www-form-urlencoded
PS: 如果请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)。
2. 数据格式要求:
name=charles&age=18&gender=男
# 如果不满足上述条件,那么就必须使用request.body将字节转换为str,然后再做解析:
如:
a. form表单提交,请求头和数据都满足上述条件:
<form method...>
input... </form> b. ajax提交
$.ajax({
url:...
type:POST,
data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
}) 情况一: #数据满足,请求头不满足
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男
})
# body有值;POST无
情况二:# 数据和请求头都不满足
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...}
})
# body有值;POST无
# json.loads(request.body)
# rest framework 解析器
#JSONParser支持解析请求头为application/json的数据
#FormParser 支持解析请求头为content-type:application/x-www-form-urlencoded的数据 from rest_framework.parsers import JSONParser,FormParser
class ParserView(APIView):
# parser_classes = [JSONParser,FormParser,] #查看请求头,自动匹配解析器
"""
JSONParser:表示只能解析content-type:application/json头
JSONParser:表示只能解析content-type:application/x-www-form-urlencoded头
""" def post(self, request, *args, **kwargs):
"""
允许用户发送JSON格式数据
a. content-type: application/json
b. {'name':'alex',age:18}
:param request:
:param args:
:param kwargs:
:return:
"""
"""
1. 获取用户请求
2. 获取用户请求体
3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较
4. JSONParser对象去请求体
5. request.data
"""
print(request.data) # 解析后的数据 return HttpResponse('ParserView')
通过request.data可以看到解析器的源码,分析得到,解析器可以通过配置定义全局的解析器:
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES' : ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser']
} 使用非默认的解析器使用配置如下:
class ParserView(APIView):
parser_classes = [JSONParser,FormParser,] # 自己的视图类中使用的解析器
""" #除了之外,还有如下的解析器:
class FormParser(BaseParser):
"""
Parser for form data.
"""
media_type = 'application/x-www-form-urlencoded' def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as a URL encoded form,
and returns the resulting QueryDict.
"""
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
data = QueryDict(stream.read(), encoding=encoding)
return data class MultiPartParser(BaseParser):
"""
Parser for multipart form data, which may include file data.
"""
media_type = 'multipart/form-data' def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as a multipart encoded form,
and returns a DataAndFiles object. `.data` will be a `QueryDict` containing all the form parameters.
`.files` will be a `QueryDict` containing all the form files.
"""
parser_context = parser_context or {}
request = parser_context['request']
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
meta = request.META.copy()
meta['CONTENT_TYPE'] = media_type
upload_handlers = request.upload_handlers try:
parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding)
data, files = parser.parse()
return DataAndFiles(data, files)
except MultiPartParserError as exc:
raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) class FileUploadParser(BaseParser):
"""
Parser for file upload data.
"""
media_type = '*/*'
errors = {
'unhandled': 'FileUpload parse error - none of upload handlers can handle the stream',
'no_filename': 'Missing filename. Request should include a Content-Disposition header with a filename parameter.',
} def parse(self, stream, media_type=None, parser_context=None):
"""
Treats the incoming bytestream as a raw file upload and returns
a `DataAndFiles` object. `.data` will be None (we expect request body to be a file content).
`.files` will be a `QueryDict` containing one 'file' element.
"""
parser_context = parser_context or {}
request = parser_context['request']
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
meta = request.META
upload_handlers = request.upload_handlers
filename = self.get_filename(stream, media_type, parser_context) if not filename:
raise ParseError(self.errors['no_filename']) # Note that this code is extracted from Django's handling of
# file uploads in MultiPartParser.
content_type = meta.get('HTTP_CONTENT_TYPE',
meta.get('CONTENT_TYPE', ''))
try:
content_length = int(meta.get('HTTP_CONTENT_LENGTH',
meta.get('CONTENT_LENGTH', 0)))
except (ValueError, TypeError):
content_length = None # See if the handler will want to take care of the parsing.
for handler in upload_handlers:
result = handler.handle_raw_input(stream,
meta,
content_length,
None,
encoding)
if result is not None:
return DataAndFiles({}, {'file': result[1]}) # This is the standard case.
possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size]
chunk_size = min([2 ** 31 - 4] + possible_sizes)
chunks = ChunkIter(stream, chunk_size)
counters = [0] * len(upload_handlers) for index, handler in enumerate(upload_handlers):
try:
handler.new_file(None, filename, content_type,
content_length, encoding)
except StopFutureHandlers:
upload_handlers = upload_handlers[:index + 1]
break for chunk in chunks:
for index, handler in enumerate(upload_handlers):
chunk_length = len(chunk)
chunk = handler.receive_data_chunk(chunk, counters[index])
counters[index] += chunk_length
if chunk is None:
break for index, handler in enumerate(upload_handlers):
file_obj = handler.file_complete(counters[index])
if file_obj is not None:
return DataAndFiles({}, {'file': file_obj}) raise ParseError(self.errors['unhandled']) def get_filename(self, stream, media_type, parser_context):
"""
Detects the uploaded file name. First searches a 'filename' url kwarg.
Then tries to parse Content-Disposition header.
"""
try:
return parser_context['kwargs']['filename']
except KeyError:
pass try:
meta = parser_context['request'].META
disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
filename_parm = disposition[1]
if 'filename*' in filename_parm:
return self.get_encoded_filename(filename_parm)
return force_text(filename_parm['filename'])
except (AttributeError, KeyError, ValueError):
pass def get_encoded_filename(self, filename_parm):
"""
Handle encoded filenames per RFC6266. See also:
http://tools.ietf.org/html/rfc2231#section-4
"""
encoded_filename = force_text(filename_parm['filename*'])
try:
charset, lang, filename = encoded_filename.split('\'', 2)
filename = urlparse.unquote(filename)
except (ValueError, LookupError):
filename = force_text(filename_parm['filename'])
return filename
引申内容如下:
1. http 状态码
2. http请求方法
3. http 请求头
十九、 rest framework框架之序列化
数据库表结构如下:
class UserGroup(models.Model):
title = models.CharField(max_length=32) class UserInfo(models.Model):
user_type_choices = (
(1,'普通用户'),
(2,'VIP'),
(3,'SVIP'),
)
user_type = models.IntegerField(choices=user_type_choices)
group = models.ForeignKey('UserGroup') username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=64) roles = models.ManyToManyField('Role') class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo')
token = models.CharField(max_length=64) class Role(models.Model):
title = models.CharField(max_length=32)
1、序列化基本使用
a. django的序列化
如果是django的QuerySet对象,直接使用json.dumps进行处理,是会报错的,使用django的序列化工具不太好用,一版我们使用values/value_list转换为列表之后,再进行序列化:
import json
class RoleView(APIView):
def get(self, request, *args, **kwargs):
roles = models.Role.objects.all().values('id', 'title')
roles = list(roles)
ret = json.dumps(roles, ensure_ascii=False) # ensure_ascii=False 表示如果有中文,不是输出字节码,而是中文字符
return HttpResponse(ret)
b.使用rest framework的序列化工具
b1.
from rest_framework import serializers class RolesSerializer(serializers.Serializer): # 下面的字段必须是数据库的字段
id = serializers.IntegerField()
title = serializers.CharField() class RoleView(APIView):
def get(self, request, *args, **kwargs):
roles = models.Role.objects.all()
ser = RolesSerializer(instance=roles, many=True) # 如果QuerySet不是一个对象,使用many=True,如果是一个对象,如.first()/.last(),那么使用many=False
ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
b2.
上述序列化的是简单的CharField字典,如果字段是choice/ForeignKey/ManyToMany,那么如何序列化呢?
class UserInfoSerializer(serializers.Serializer):
xxxx = serializers.CharField(source='user_type') # 显示choice的id
ooo = serializers.CharField(source='get_user_type_display') # 显示choice的value
username = serializers.CharField()
password = serializers.CharField()
gp = serializers.CharField(source='group.title') # source指定序列化的字段 # rls = serializers.CharField(source='roles.all')
rls = serializers.SerializerMethodField() # ManyToMany 可以指定方法,由方法返回需要被序列化展示的内容 def get_rls(self, row): # 方法名为get_名称(这里是rls)
roles_obj_list = row.roles.all()
ret = []
for item in roles_obj_list:
ret.append({'id': item.id, 'title': item.title})
return ret class UserInfoView(APIView): def get(self, request, *args, **kwargs): users = models.UserInfo.objects.all()
ser = UserInfoSerializer(instance=users, many=True) ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
参考: http://www.cnblogs.com/wupeiqi/articles/7805382.html
b3.
使用rest framework ModelSerializer也可以使用上述的序列化的功能,但是更省事:
class UserInfoSerializer(serializers.ModelSerializer):
ooo = serializers.CharField(source='get_user_type_display')
rls = serializers.SerializerMethodField() class Meta:
model = models.UserInfo
# fields = "__all__" # 显示所有字段,但是外键部分只显示ID
fields = ['id', 'username', 'password', 'ooo', 'rls', 'group'] def get_rls(self, row):
roles_obj_list = row.roles.all()
ret = []
for item in roles_obj_list:
ret.append({'id': item.id, 'title': item.title})
return ret
在序列化的时候,上面的CharField可以使用自定义的类(一般不使用):
class MyField(serializers.CharField):
pass class UserInfoSerializer(serializers.ModelSerializer):
ooo = serializers.CharField(source='get_user_type_display')
rls = serializers.SerializerMethodField()
x1 = MyField(source='username') class Meta:
model = models.UserInfo
# fields = "__all__" # 显示所有字段,但是外键部分只显示ID
fields = ['id', 'username', 'password', 'ooo', 'rls', 'group', 'x1'] def get_rls(self, row):
roles_obj_list = row.roles.all()
ret = []
for item in roles_obj_list:
ret.append({'id': item.id, 'title': item.title})
return ret class MyField(serializers.CharField): def to_representation(self, value):
print(value)
return 'xxxxx' # 返回值, 这里将返回显示的值写死了,而不是从数据库中去获取
b4.
使用depth, 可以自动序列化连表
class UserInfoSerializer(serializers.ModelSerializer): class Meta:
model = models.UserInfo
# fields = "__all__" # 显示所有字段,但是外键部分只显示ID
fields = ['id', 'username', 'password', 'roles', 'group']
depth = 1 # 建议值为0~3,默认为0
b5.
自动生成链接
urls.py
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/userinfo/$', views.UserInfoView.as_view()),
url(r'^(?P<version>[v1|v2]+)/usergroup/(?P<xxx>\d+)$', views.UserGroupView.as_view(), name='gp'),
]
views.py
class UserInfoSerializer(serializers.ModelSerializer):
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='xxx') #lookup_field 从数据库取值
class Meta:
model = models.UserInfo
fields = ['id', 'username', 'password', 'roles', 'group']
depth = 0 class UserInfoView(APIView): def get(self, request, *args, **kwargs): users = models.UserInfo.objects.all()
ser = UserInfoSerializer(instance=users, many=True, context={'request': request}) ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret) class UserGroupSerializer(serializers.ModelSerializer): class Meta:
model = models.UserGroup
fields = "__all__" # 显示所有字段,但是外键部分只显示ID
# fields = ['id', 'username', 'password', 'roles', 'group']
# depth = 0 class UserGroupView(APIView): def get(self, request, *args, **kwargs):
pk = kwargs.get('xxx')
obj = models.UserGroup.objects.filter(pk=pk).first()
print(obj)
ser = UserGroupSerializer(instance=obj, many=False) ret = json.dumps(ser.data, ensure_ascii=False)
return HttpResponse(ret)
引申知识点: 如何判断一个变量是否是函数
import types
def func(arg):
# if callable(arg):
if isinstance(arg, types.FunctionType):
print(arg())
else:
print(arg) func(123)
func(lambda :"666")
二十、验证用户请求数据
这里我们使用的解析器是: ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser']
所以提交的验证数据为:
class XXValidator(object):
def __init__(self, base):
self.base = base def __call__(self, value, *args, **kwargs): # 这里的value是用户提交的数据
if not value.startswith(self.base):
message = '标题必须以 %s 开头' %self.base
raise serializers.ValidationError(message) def set_context(self, seralizer_field):
pass
class GroupSerializer(serializers.Serializer):
title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[XXValidator('老男人')]) # validator表示自定义验证规则, class GroupView(APIView): def post(self, request, *args, **kwargs): ser = GroupSerializer(data=request.data) # request.data 获取请求体中的数据
if ser.is_valid():
print(ser.validated_data)
else:
print(ser.errors) # 输出 {'title': ['标题必须以 老男人 开头']}
return HttpResponse('提交数据')
验证钩子
class GroupSerializer(serializers.Serializer):
title = serializers.CharField(error_messages={'required': '标题不能为空'}, validators=[XXValidator('老男人')]) def validate_title(self, value): # 这里的value是验证的消息,是ser.validated_data,数据通过验证,会走这个钩子函数
from rest_framework import exceptions
# raise exceptions.ValidationError('哈哈哈')
print(value, "xxxxx")
return value
二十一、渲染器
from api.utils.serializers.pager import PagerSerializer
from rest_framework.response import Response
class Pager1View(APIView): def get(self, request, *args, **kwargs): roles = models.Role.objects.all()
ser = PagerSerializer(instance=roles, many=True)
return Response(ser.data) # 使用渲染器显示接口数据
为什么会展示上面的内容呢?
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer # 默认使用的渲染器是这里的全部的渲染器 class TestView(APIView):
# renderer_classes = [JSONRenderer,BrowsableAPIRenderer] # 可以在这里定义该视图使用的渲染器,
def get(self, request, *args, **kwargs):
# 获取所有数据
roles = models.Role.objects.all() # 创建分页对象
# pg = CursorPagination()
pg = MyCursorPagination() # 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) # 对数据进行序列化
ser = PagerSerialiser(instance=pager_roles, many=True) return Response(ser.data) # 也可以使用全局的配置,配置默认的渲染器
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES":[
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
]
}
当然,我们可以继承上面的渲染器,然后自定制自己的显示页面等内容:
class BrowsableAPIRenderer(BaseRenderer):
"""
HTML renderer used to self-document the API.
"""
media_type = 'text/html'
format = 'api'
template = 'rest_framework/api.html' # 这里页面的内容,我们可以进行在子类中替换,哈哈哈哈
filter_template = 'rest_framework/filters/base.html'
code_style = 'emacs'
charset = 'utf-8'
form_renderer_class = HTMLFormRenderer
二十二、分页器
#自定义序列化的类
#pager.py
from rest_framework import serializers
from api import models class PagerSerializer(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = "__all__"
22.1
#分页
REST_FRAMEWORK = {
'PAGE_SIZE': 2, # 定义每页分页的大小
} class Pager1View(APIView): def get(self, request, *args, **kwargs): #获取所有数据
roles = models.Role.objects.all() # 创建分页对象
pg = PageNumberPagination() # 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) # 返回的是分页的对象 # 对分页的数据进行序列化
ser = PagerSerializer(instance=pager_roles, many=True)
print(pager_roles)
return Response(ser.data)
22.2
除此之外,我们还可以自定义分页的大小,通过自定义的类来实现:
class MyPageNumberPagination(PageNumberPagination):
page_size = 2
page_query_param = 'page' # 查询分页时使用的参数
page_size_query_param = 'size' # 是否可以自定义查询分页的大小 max_page_size = 5 # 每个分页的最大值 class Pager1View(APIView): def get(self, request, *args, **kwargs): #获取所有数据
roles = models.Role.objects.all() # 创建分页对象
pg = MyPageNumberPagination() # 自定义的分页类 # 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self) # 对分页的数据进行序列化
ser = PagerSerializer(instance=pager_roles, many=True)
print(pager_roles)
return Response(ser.data)
22.3
如果返回为:
ser = PagerSerializer(instance=pager_roles, many=True)
print(pager_roles)
# return Response(ser.data)
return pg.get_paginated_response(ser.data)
则显示如下的内容:
22.4
另外使用LimitOffsetPagination也可以实现上述功能
from rest_framework.pagination import LimitOffsetPagination
class LimitOffsetPagination(BasePagination):
"""
A limit/offset based style. For example: http://api.example.org/accounts/?limit=100
http://api.example.org/accounts/?offset=400&limit=100 # offset 是从0开始的
"""
default_limit = api_settings.PAGE_SIZE
limit_query_param = 'limit'
limit_query_description = _('Number of results to return per page.')
offset_query_param = 'offset'
offset_query_description = _('The initial index from which to return the results.')
max_limit = None
template = 'rest_framework/pagination/numbers.html'
22.5 加密分页
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination class MyPageNumberPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询页的ID
page_size = 2
ordering = '-id' # 排序
page_size_query_param = None
max_page_size = None
二十三、 rest framework之视图
23.1 GenericAPIView
从源码看,GenericAPIView是继承了APIView, 实现的功能和APIView没有任何区别,做了解即可:
继承的顺序是View-->APIView--> GenericView class APIView(View): # The following policies ma class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
实现代码如下:
from rest_framework.pagination import PageNumberPagination
from api.utils.serializers.pager import PagerSerializer
from rest_framework.generics import GenericAPIView
class View1View(GenericAPIView):
queryset = models.Role.objects.all()
serializer_class = PagerSerializer
pagination_class = PageNumberPagination
def get(self, request, *args, **kwargs):
# 获取数据
roles = self.get_queryset() # models.Role.objects.all()
pager_roles = self.paginate_queryset(roles) # 序列化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
23.2 GenericViewSet
与上面的GenericAPIView不同的是,重写了as_view()方法;
# 继承了ViewSetMixin和GenericAPIView两个类,ViewSetMixin中重写了as_view方法; class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass class ViewSetMixin(object):
"""
This is the magic. Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource. For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions... view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
""" @classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The suffix initkwarg is reserved for displaying the viewset type.
# eg. 'List' or 'Instance'.
cls.suffix = None # Setting a basename allows a view to reverse its action urls. This
# value is provided by the router through the initkwargs.
cls.basename = None
实现代码:
urls.py
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list'})), # method为GET时,去寻找视图类中的list方法;
views.py
from rest_framework.viewsets import GenericViewSet
class View1View(GenericViewSet):
queryset = models.Role.objects.all()
serializer_class = PagerSerializer
pagination_class = PageNumberPagination
def list(self, request, *args, **kwargs): # list方法必须要实现
# 获取数据
roles = self.get_queryset() # models.Role.objects.all()
pager_roles = self.paginate_queryset(roles) # 序列化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
23.3 ModelViewSet
ModelViewSet 继承了多个类: 每个类实现了一个特定的方法,在实现的时候,无需在视图中实现这些方法,只需要在as_view中指定方法名就可以了
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
实现代码:
urls.py
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list', 'post':'create'})), # 获取列表和创建数据无需传递id
url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)$', views.View1View.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update', 'patch': 'partial_update'})), # 因为update、delete等操作,需要传递id,所以在实现的时候,需要两个路由;
views.py
from rest_framework.viewsets import ModelViewSet
class View1View(ModelViewSet):
queryset = models.Role.objects.all()
serializer_class = PagerSerializer
pagination_class = PageNumberPagination
当然了,也可以只继承mixins中的任意一个或者多个类;
总结使用:
a. 增删改查 使用ModelViewSet
b. 增删 使用CreateModelMixin,DestroyModelMixin GenericViewSet
c. 复杂逻辑 使用GenericViewSet 或 APIView
二十四、路由
一般一个视图,我们最多写四个url就够了
# http://127.0.0.1:8000/api/v1/v1/?format=json
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
# http://127.0.0.1:8000/api/v1/v1.json
url(r'^(?P<version>[v1|v2]+)/v1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list','post':'create'})),
url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)\.(?P<format>\w+)$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
如果嫌麻烦,可以使用全自动路由:
from api import views
from rest_framework import routers router = routers.DefaultRouter()
router.register(r'xxxxx', views.View1View)
router.register(r'rt', views.View1View) urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
]
如果写单个url就自己写,如果写全部的增删改查就自动生成;
二十五、content-type
在做前后端分离的时候,涉及跨域的问题,解决办法:
1. jsonp
2. cors: - 将这个响应头放在中间件中进行实现;
content-type: 是django内置的组件,帮助开发者做连表操作。
from django.db import models # Create your models here. from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation class Course(models.Model):
"""
普通课程
"""
title = models.CharField(max_length=12)
# 仅用于反向查找
price_policy_list = GenericRelation("PricePlicy") class DegreeCourse(models.Model):
"""
学位课程
"""
title = models.CharField(max_length=32)
price_policy_list = GenericRelation("PricePlicy") class PricePlicy(models.Model):
"""
价格策略
"""
price = models.IntegerField()
period = models.IntegerField() content_type = models.ForeignKey(ContentType, verbose_name='关联的表的名称')
object_id = models.IntegerField(verbose_name='关联的表中的数据行的ID')
# 快速实现content_type 操作
content_object = GenericForeignKey('content_type', 'object_id') # 会自动找到obj 对象的id和关联的表中的数据行的ID,并进行赋值 obj = DegreeCourse.objects.filter(title='python').first()
PricePlicy.objects.create(price=9.9, period=30, content_object=obj)
视图函数
from app01 import models def test(request):
obj1 = models.DegreeCourse.objects.filter(title='python').first()
models.PricePlicy.objects.create(price=9.9, period=30, content_object=obj1) obj1 = models.Course.objects.filter(title='rest framework').first()
models.PricePlicy.objects.create(price=9.9, period=30, content_object=obj1)
course = models.Course.objects.filter(id=1).first()
price_policys = course.price_policy_list.all()
print(price_policys)
return HttpResponse('....')
django restfulwork 源码剖析的更多相关文章
- Django对中间件的调用思想、csrf中间件详细介绍、Django settings源码剖析、Django的Auth模块
目录 使用Django对中间件的调用思想完成自己的功能 功能要求 importlib模块介绍 功能的实现 csrf中间件详细介绍 跨站请求伪造 Django csrf中间件 form表单 ajax c ...
- django session源码剖析
首先要明白,session和cookie,session是保存在服务器端,cookie存储在浏览器上,我们称为客户端,客户端向服务端发送请求时,会将cookie一起发送给服务端.服务端接收到请求后,会 ...
- Django admin源码剖析
单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...
- Django Rest Framework源码剖析(八)-----视图与路由
一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...
- Django Rest Framework源码剖析(七)-----分页
一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...
- Django Rest Framework源码剖析(六)-----序列化(serializers)
一.简介 django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似). ...
- Django Rest Framework源码剖析(五)-----解析器
一.简介 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数据格式,比如xml,所以需要解析这类数 ...
- Django Rest Framework源码剖析(四)-----API版本
一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...
- Django Rest Framework源码剖析(三)-----频率控制
一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...
随机推荐
- npm run dev--The 'mode' option has not been set, webpack will fallback to 'production' for this value
npm run dev时报警告: warning configurationThe 'mode' option has not been set, webpack will fallback to ' ...
- python字符串截取、查找、分割
Python 截取字符串使用 变量[头下标:尾下标],就可以截取相应的字符串,其中下标是从0开始算起,可以是正数或负数,下标可以为空表示取到头或尾. # 例1:字符串截取 str = '1234567 ...
- redhat 开课啦
今天是三八女神节. 终于开课啦,为考取RHCE准备.
- 创建maven工程的时候卡死的解决办法
在idea的maven,runner,properties里面添加 archetypeCatalog=internal
- 关于Rabbitmq的routingkey的作用
对于消息发布者而言它只负责把消息发布出去,甚至它也不知道消息是发到哪个queue,消息通过exchange到达queue,exchange的职责非常简单,就是一边接收发布者的消息一边把这些消息推到qu ...
- [转][SerialPort]测试用例
private void Form1_Load(object sender, EventArgs e) { var s = SerialPort.GetPortNames().OrderBy(r =& ...
- CentOS7.5 GlusterFS 分布式文件系统集群环境搭建
环境准备: 系统版本:CentOS Linux release 7.5.1804 (Core) glusterfs:3.6.9 userspace-rcu-master: 硬件资源: 10.200.2 ...
- 洛谷题解 CF777A 【Shell Game】
同步题解 题目翻译(可能有童鞋没读懂题面上的翻译) 给你三张牌0,1,2. 最初选一张,然后依次进行n次交换,交换规则为:中间一张和左边的一张,中间一张和右边一张,中间一张和左边一张...... 最后 ...
- uniapp仿h5+fire自定义事件触发监听
仿h5+fire自定义事件触发监听 uni-app调用 event.js 源码记录(点击查看) 1.js下载地址 [event.js](https://ext.dcloud.net.cn/plugin ...
- JavaScript数组方法--every、some、fill
接上一篇,JavaScript数组方法--concat.push,继续其他的数组方法. every:every() 方法测试数组的所有元素是否都通过了指定函数的测试. 先看其使用方法: functio ...