Django ContentType 及 corf跨域
Posted jassin-du
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django ContentType 及 corf跨域相关的知识,希望对你有一定的参考价值。
ContentType是什么?
Django ContentTypes是由Django框架提供的一个核心功能,它对当前项目中所有基于Django驱动的model提供了更高层次的抽象接口。
ContentTypes能做什么
当使用django-admin初始化一个django项目的时候,可以看到在默认的INSTALL_APPS已经包含了django.contrib.contenttypes:
INSTALLED_APPS = [ ‘django.contrib.admin‘, ‘django.contrib.auth‘, ‘django.contrib.contenttypes‘, ‘django.contrib.sessions‘, ‘django.contrib.messages‘, ‘django.contrib.staticfiles‘, ‘app01‘, ]
而且注意django.contrib.contenttypes是在django.contrib.auth之后,这是因为auth中的permission系统是根据contenttypes来实现的。
# contrib放这项目里面所有的app
从from django.contrib.contenttypes.models import ContentType进去看ContentType的源码 @python_2_unicode_compatible class ContentType(models.Model): app_label = models.CharField(max_length=100) model = models.CharField(_(‘python model class name‘), max_length=100) objects = ContentTypeManager() class Meta: verbose_name = _(‘content type‘) verbose_name_plural = _(‘content types‘) db_table = ‘django_content_type‘ unique_together = ((‘app_label‘, ‘model‘),) def __str__(self): return self.name
我们可以看到ContentType就是一个简单的django model,而且它在数据库中的表的名字为django_content_type。
实例
课程有专题课(单纯的课程),学位课(带有更多增益性服务)、价格策略(分阶段标价格,如一个月9.9,两个月29.9,三个月59.9等)
问题一:表结构设计
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey,GenericRelation class DegreeCourse(models.Model): name = models.CharField(max_length=32) class Course(models.Model): name = models.CharField(max_length=32) # 数据库不生成,只用于链表查询 policy_list = GenericRelation("PricePolicy") class PricePolicy(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() # 不在数据库中生成,只用于帮助你做数据操作 content_object = GenericForeignKey(‘content_type‘, ‘object_id‘) period = models.CharField(max_length=32) price = models.FloatField()
问题2:为专题课1添加3个价格策略
PricePolicy.objecs。create(period=‘10’,price=9.9,object_id=1,content_type_id=8)关于content_type_id如何取值
问题3:要显示所有价格策略,并将对应显示对应课程名
问题4:给你课程ID,获取课程信息+该课程的所有价格策略
加字段
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey,GenericRelation class DegreeCourse(models.Model): name = models.CharField(max_length=32) class Course(models.Model): name = models.CharField(max_length=32) # 数据库不生成,只用于链表查询 policy_list = GenericRelation("PricePolicy") class PricePolicy(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() # 不在数据库中生成,只用于帮助你做数据操作 content_object = GenericForeignKey(‘content_type‘, ‘object_id‘) period = models.CharField(max_length=32) price = models.FloatField() # PricePolicy.objects.create(period=‘10‘,price=9.9,object_id=1,content_type_id=8) # course_obj = Course.objects.get(id=1) # PricePolicy.objects.create(period=‘10‘,price=9.9,content_object=course_obj)
corf跨域
目标:由api解决跨域,本质添加响应头
1:简单请求
def service(request): # if request.method == ‘OPTIONS‘: # obj = HttpResponse() # return obj # else: v1 = request.GET.get(‘v1‘) v2 = request.GET.get(‘v2‘) result = v1 + v2 # obj = HttpResponse(result) return HttpResponse(result)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> </head> <body> <input type="text" name="v1"> <input type="text" name="v2"> <input type="button" value="点我" onclick="getData()"> <script src="jquery-3.2.1.min.js"></script> <script> function getData() { $.ajax({ url:"http://127.0.0.1:8000/service/?v1=jassin&v2=666" type:‘GET‘, success:function (arg) { alert(arg); } }) } </script> </body> </html>
在返回数据需要加上允许哪些域名通过
def service(request): v1 = request.GET.get(‘v1‘) v2 = request.GET.get(‘v2‘) result = v1 + v2 obj = HttpResponse(result) # 返回的数据 # obj["Access-Control-Allow-Origin"] = "*" # 对域名不限制 # 允许哪些域名通过 obj["Access-Control-Allow-Origin"] = "http://localhost:63342" return obj:
2:当用户自定义请求头将请求变成了复杂请求
服务端--->需要先预检自定义请求头,通过后,再发起数据相关请求
<script> function getData() { $.ajax({ url:"http://127.0.0.1:8000/service/?v1=jassin&v2=666", type:‘GET‘, // 自定义了请求头 headers:{ k1:‘v1‘ }, success:function (arg) { alert(arg); } }) } </script>
用户请求将变成:OPTIONS
解决:
def service(request): if request.method == ‘OPTIONS‘: # 进行预检 obj = HttpResponse() obj["Access-Control-Allow-Headers"] = "k1"# 允许请求头为k1通过 obj["Access-Control-Allow-Origin"] = "*" # 允许所有域名访问 return obj else: v1 = request.GET.get(‘v1‘) v2 = request.GET.get(‘v2‘) result = v1 + v2 obj = HttpResponse(result) # 返回的数据 # obj["Access-Control-Allow-Origin"] = "*" # 允许哪些域名通过 obj["Access-Control-Allow-Origin"] = "http://localhost:63342" return obj
3:由于请求方式不是简单的get请求将请求方式变成复杂请求
当请求方式为PUT
<script> function getData() { $.ajax({ url:"http://127.0.0.1:8000/service/?v1=jassin&v2=666", type:‘PUT‘, // 自定义了请求头 headers:{ k1:‘v1‘ }, success:function (arg) { alert(arg); } }) } </script>
解决
def service(request): if request.method == ‘OPTIONS‘: # 进行预检 obj = HttpResponse() obj["Access-Control-Allow-Headers"] = "k1"# 允许请求头为k1通过 obj["Access-Control-Allow-Origin"] = "*" # 允许所有域名访问 obj["Access-Control-Allow-Methods"] = "PUT" # 允许请求方式为put通过 return obj else: v1 = request.GET.get(‘v1‘) v2 = request.GET.get(‘v2‘) result = v1 + v2 obj = HttpResponse(result) # 返回的数据 # obj["Access-Control-Allow-Origin"] = "*" # 允许哪些域名通过 obj["Access-Control-Allow-Origin"] = "http://localhost:63342" return obj
简单请求和复杂请求
条件: 1、请求方式:HEAD、GET、POST 2、请求头信息: Accept Accept-Language Content-Language Last-Event-ID Content-Type 对应的值是以下三个中的任意一个 application/x-www-form-urlencoded multipart/form-data text/plain 注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
简单请求和非简单请求的区别?
简单请求:一次请求
非简单请求:
两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
关于“预检”
请求方式:OPTIONS - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息 - 如何“预检” => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers
注意:
只有域名,才有*,允许所有的域名通过的方式,别的都需单独添加
了解
基于cors实现AJAX请求:
a、支持跨域,简单请求
服务器设置响应头:Access-Control-Allow-Origin = ‘域名‘ 或 ‘*‘
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="submit" onclick="XmlSendRequest();" /> </p> <p> <input type="submit" onclick="JqSendRequest();" /> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function XmlSendRequest(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { var result = xhr.responseText; console.log(result); } }; xhr.open(‘GET‘, "http://c2.com:8000/test/", true); xhr.send(); } function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: ‘GET‘, dataType: ‘text‘, success: function(data, statusText, xmlHttpRequest){ console.log(data); } }) } </script> </body> </html> HTML
class MainHandler(tornado.web.RequestHandler): def get(self): self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com") self.write(‘{"status": true, "data": "seven"}‘)
b、支持跨域,复杂请求
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
- “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
- “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
- “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="submit" onclick="XmlSendRequest();" /> </p> <p> <input type="submit" onclick="JqSendRequest();" /> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function XmlSendRequest(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { var result = xhr.responseText; console.log(result); } }; xhr.open(‘PUT‘, "http://c2.com:8000/test/", true); xhr.setRequestHeader(‘k1‘, ‘v1‘); xhr.send(); } function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: ‘PUT‘, dataType: ‘text‘, headers: {‘k1‘: ‘v1‘}, success: function(data, statusText, xmlHttpRequest){ console.log(data); } }) } </script> </body> </html> HTML
class MainHandler(tornado.web.RequestHandler): def put(self): self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com") self.write(‘{"status": true, "data": "seven"}‘) def options(self, *args, **kwargs): self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com") self.set_header(‘Access-Control-Allow-Headers‘, "k1,k2") self.set_header(‘Access-Control-Allow-Methods‘, "PUT,DELETE") self.set_header(‘Access-Control-Max-Age‘, 10) Tornado
c、跨域获取响应头
默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="submit" onclick="XmlSendRequest();" /> </p> <p> <input type="submit" onclick="JqSendRequest();" /> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function XmlSendRequest(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { var result = xhr.responseText; console.log(result); // 获取响应头 console.log(xhr.getAllResponseHeaders()); } }; xhr.open(‘PUT‘, "http://c2.com:8000/test/", true); xhr.setRequestHeader(‘k1‘, ‘v1‘); xhr.send(); } function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: ‘PUT‘, dataType: ‘text‘, headers: {‘k1‘: ‘v1‘}, success: function(data, statusText, xmlHttpRequest){ console.log(data); // 获取响应头 console.log(xmlHttpRequest.getAllResponseHeaders()); } }) } </script> </body> </html> HTML
class MainHandler(tornado.web.RequestHandler):
def put(self):
self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com")
self.set_header(‘xxoo‘, "seven")
self.set_header(‘bili‘, "daobidao")
self.set_header(‘Access-Control-Expose-Headers‘, "xxoo,bili")
self.write(‘{"status": true, "data": "seven"}‘)
def options(self, *args, **kwargs):
self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com")
self.set_header(‘Access-Control-Allow-Headers‘, "k1,k2")
self.set_header(‘Access-Control-Allow-Methods‘, "PUT,DELETE")
self.set_header(‘Access-Control-Max-Age‘, 10)
Tornado
d、跨域传输cookie
在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。
如果想要发送:
- 浏览器端:XMLHttpRequest的withCredentials为true
- 服务器端:Access-Control-Allow-Credentials为true
- 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <p> <input type="submit" onclick="XmlSendRequest();" /> </p> <p> <input type="submit" onclick="JqSendRequest();" /> </p> <script type="text/javascript" src="jquery-1.12.4.js"></script> <script> function XmlSendRequest(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4) { var result = xhr.responseText; console.log(result); } }; xhr.withCredentials = true; xhr.open(‘PUT‘, "http://c2.com:8000/test/", true); xhr.setRequestHeader(‘k1‘, ‘v1‘); xhr.send(); } function JqSendRequest(){ $.ajax({ url: "http://c2.com:8000/test/", type: ‘PUT‘, dataType: ‘text‘, headers: {‘k1‘: ‘v1‘}, xhrFields:{withCredentials: true}, success: function(data, statusText, xmlHttpRequest){ console.log(data); } }) } </script> </body> </html> HTML
class MainHandler(tornado.web.RequestHandler): def put(self): self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com") self.set_header(‘Access-Control-Allow-Credentials‘, "true") self.set_header(‘xxoo‘, "seven") self.set_header(‘bili‘, "daobidao") self.set_header(‘Access-Control-Expose-Headers‘, "xxoo,bili") self.set_cookie(‘kkkkk‘, ‘vvvvv‘); self.write(‘{"status": true, "data": "seven"}‘) def options(self, *args, **kwargs): self.set_header(‘Access-Control-Allow-Origin‘, "http://www.xxx.com") self.set_header(‘Access-Control-Allow-Headers‘, "k1,k2") self.set_header(‘Access-Control-Allow-Methods‘, "PUT,DELETE") self.set_header(‘Access-Control-Max-Age‘, 10) Tornado
以上是关于Django ContentType 及 corf跨域的主要内容,如果未能解决你的问题,请参考以下文章