本节内容
-
models操作
- Django的缓存
-
请求方式
-
序列化
- Form 配合Ajax实现登录认证
-
上传文件
- Ajax csrf_token验证方式
1 models操作
单表查询:
curd(增删改查)
多表查询:
# 一次性查出所有的数据,进行一次跨表查询 只能在有ForeignKey的表中正向使用,
select_realated():
进行一次跨表查询
user_list = Models.UserInfo.objetcts.all()
for row in user_list:
print(row.username,row.email,row.part_id,row.part.title) # 如果像这样查询(row.part.title将会进行一次跨表查询)
user_list = Models.UserInfo.objetcts.all().select_realated('part') # 只会进行一次跨表查询
# 进行两次单表查询 (进行多次单表查询,Django会自动进行关系关联)
prefetch_realated():
user_list = Models.UserInfo.objetcts.all().prefetch_realated('part')
# 第一句 : Models.UserInfo.objetcts.all()
# 第二句 : Models.Part.objetcts.filter(id__in=[上一句查询数据的part_id列表])
for row in user_list:
print(row.username,row.email,row.part_id,row.part.title) # 使用方法和之前一样,
# user_list = Models.UserInfo.objetcts.all().prefetch_realated('part','usertype','part__ut')
# 第一句:Models.UserInfo.objetcts.all()
# 第二句:Models.Part.objetcts.filter(id__in=part_id)
# 第三句:Models.UserInfo.objetcts.filter(id__in=usertype_id)
其他:
only为指定取的数据,
defer为指定不取的数据
通过这个方式取出的数据为queryset类型,如果要使用对象中其他没有查询的字段,多进行多余的sql查询
models.UserInfo.objetcts.all().only('id','name')
models.UserInfo.objetcts.all().defer('id','name')
2 缓存
Django中提供六中缓存方式:
1.开发调试 # 不缓存
2.内存
3.文件
4.数据库
5.Memcache缓存(python-memcache模块)
6.Memcache缓存(pylibmc模块)
2.1 缓存的配置
a.开发调试缓存配置(settings中配置)
# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 'VERSION': 1, # 缓存key的版本(默认1) 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } } # 自定义key def default_key_func(key, key_prefix, version): """ Default function to generate keys. Constructs the key used by all other methods. By default it prepends the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): """ Function to decide which key function to use. Defaults to ``default_key_func``. """ if key_func is not None: if callable(key_func): return key_func else: return import_string(key_func) return default_key_func
b.内存
# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } # 注:其他配置同开发调试版本
c.文件
# 此缓存将内容保存至文件 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } } # 注:其他配置同开发调试版本
d.数据库
# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 } } # 注:执行创建表命令 python manage.py createcachetable
e.Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
2.2 缓存的应用
a.全站使用
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
b.单独视图使用
方式一: from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ... 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
c.局部视图使用
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %} 缓存内容 {% endcache %}
***自定义:利用中间件/装饰器/simple_tag/自定义操作***
3 信号
Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
3.1 django的内置信号
Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发
有两种方法可以把信号和接受器连接到一起
1.connect方法:
class callback(sender,**kwargs) print('开始添加数据了。记录好日志信息') from django.db.models.signals import pre_save pre_save.connect(callback)
2.装饰器方法:
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def callback(sender, **kwargs): print("请求结束")
3.绑定特定的发送者
上面的配置,每次都会调用,如果不想接受说有人的信号,就需要配置sender关键字参数
每一类的信号都对应着特定的发送者,所以要绑定发送者也得绑定对应的发送者类型
下面我们以pre-save为例子绑定特定的发送者(模型):
######### 装饰器绑定 ######### from django.db.models.signals import pre_save from django.dispatch import receiver @receiver(pre_save,sender=models.UserInfo) def callback(sender, **kwargs): print("对象添加前") print(sender,'#',kwargs) ######### connect绑定 ######### from django.db.models.signals import pre_save def callback(sender, **kwargs): print("对象添加前") print(sender,'#',kwargs) pre_save.connect(callback,sender=models.UserInfo)
4. 预防重复的信号
使用dispatch_uid关键字参数来预防重复的信号
例:
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
3.2 自定义信号
1. 定义信号
所有的信号都是django.dispatch.Signal的实例,参数providing_args是一个信号提供给监听器的参数名的列表,比如:
这段代码定义了一个pizza_done的信号,参数有toppings和size
import django.dispatch pizza_done = django.dispatch.Sigal(providing_args=["toppings","size"])
2.注册信息
def callback(sender, **kwargs): print("callback") print(sender,kwargs) pizza_done.connect(callback)
3.触发信号
from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456) # 有两个方法发送信号 # Signal.send(sender,**kwargs) # Signal.send_robust(sender,**kwargs) # sender参数是必须的,关键字参数可选 #这两种方法都返回一个 元组对[(receiver,respose),...] 的 列表 ,一个代表被调用的receiver回调函数和他们的response的列表 #这两种方法的区别在于send不会捕捉任何的异常,(放任错误的传播),而send_robust则是捕捉所有的异常,并确保每个接收器都知道这个信号(发生错误了)(如果发生错误的话,错误实体和发生错误的接收器作为一个元组对一起返回给那个列表
由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
4.断开信号
Signal.disconnect([receiver=None,sender=None,weak=True,dispatch_uid=None) 和监听信号类似 receiver参数用来指明那个接收器被断开,如果使用了dispatch_uid的话,receiver可以为None 总结,你可以使用django自带的信号,也可以自定义自己的信号,信号可以connect,可以send也可以disconnect等等
3.请求方式
在django中请求方式分为三种
a.普通的请求,返回渲染后的html所有数据 b.Ajax请求,render生成局部HTML c.Ajax请求,HttpResponse返回数据,前端自己进行渲染(通常用作接口,为别人提供数据) 1.render和HttpResponse的关系? render返回: a.模板+数据进行替换 b.HttpResponse(数据) HttpResponse: a.HttpResponse(数据) 2.Ajax请求时,响应内容只能用HttpResponse吗? 不是
4. 序列化
4.1 序列化方式
Django数据的序列化方式一: from django.core import serializers user_list = models.UserInfo.objetcts.all() data = serializers.serialize("json",ret) return HttpResponse(data) [{"model":"app01,userinfo","pk":1,"fields":{"username":"helei","pwd":"123456"}}] model:为数据取自那个表 pk:主键 fields:数据字典 Django数据的序列化方式二: user_list = models.UserInfo.objetcts.values("username","pwd") data = list(user_list) import json return HttpResponse(json.dumps(data)) Django数据的序列化方式三: # json.dumps()方法会把元祖变为列表 user_list = models.UserInfo.objetcts.values_list("username","pwd") data = list(user_list) import json return HttpResponse(json.dumps(data))
4.2 序列化的使用
一般后台传递数据给前端,数据需要进行序列化, 最常用的方法是定义一个类来存放要传递的数据。例如:
class BaseReponse: def __init__(self): self.status = False # 定义数据状态 self.data = None # 存放数据信息 self.error = None # 错误信息大概流程
利用类来填充并序列化传递数据
后端传递序列化数据一般为:
import json reponse = BaseReponse() try: user_list = models.UserInfo.objetcts.values_list('username','pwd') user_list = list(user_list) reponse.status = True reponse.data = user_list except Exception as e: reponse.status = False reponse.error = '获取数据失败' data = json.dumps(reponse.__dict__) return HttpResponse(data)
前端接收数据:
data = JSON.parse(data) if (data.status){ $.each(data.data,function(index,row){ # console.log(index,row); var tag = document.createElement('li'); tag.innerHTML = row.username; $('connect ul').append(tag) }) }else{ alert(data.error) }
5. Form + Ajax实现登录认证
$('#sbm').click(function(){ $.ajax({ url:'/login', type:'POST', dataType:'JSON', data:{'username':$('#username').val(),'pwd':$('#pwd').val()}, success:function(arg){ if(arg.status){ pass }else{ $.each(arg.error,function(k,v){ tag = document.createElement('span'); tag.innerHTML = v.0; $('#' + k).after(tag) }) } } }) })
class BaseReponse(objects): def __init__(self): self.status = False self.error = None self.data = None def login(request): if request.method = 'POST': response = BaseReponse() try: obj = forms.LoginForm(request.POST)# 参数传递给forms进行验证 if obj.is_valid():# 验证数据 v = models.UserInfo.objects.filter(**obj.cleaned_data).count()# 查询数据库数据 if v: response.status = True else: response.status = False response.error = '用户名或密码错误' else: response.status = False response.error = obj.errors except Exception as e: response.status = False response.error = '请求失败' return HttpResponse(json.dumps(response.__dict__,ensure_ascii=False))
6. 上传文件
文件上传我知道三种方式:
第一种:form 表单提交文件, 页面会刷新
<form action="/upload/" method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file"> <input type="submit" value="提交"> </form>
class Upload(View): def get(self,request): return render(request,'upload.html') def post(self,request): file_obj = request.FILES.get('file') file_name = file_obj.name file = open(file_name,'wb') for chunk in file_obj.chunks(): file.write(chunk) return HttpResponse('上传成功')
第二种: Ajax 方式文件上传 (FormData) 较新的浏览器支持
后台处理同上↑
<script> $('#btn1').click(function(){ var fm = new FormData(); fm.append('file_obj',document.getElementByid('file').file[0]); $.ajax({ url:'/upload/', type:'POST', data:fm, processData: false, contentType: false, success:function(arg){ console.log(arg) } }) }) </script>
第三种: 伪Ajax操作 Ajax结合ifram 完成文件上传
注意事项,
1.form 中添加
target="ifr" enctype="multipart/form-data"
2.form 中配置的target是在指定使用那个iframe进行提交数据,例如配置的为target='ifr',该form会找name为'ifr'的iframe标签
3.iframe中返回的数据需要提取的时候需要使用如下方法
$('#ifr').contents().find('body').html()
<form id="frm" action="/upload/" method="post" target="ifr" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file"> <input type="submit" value="提交"> </form> <iframe id="ifr1" name="ifr" frameborder="1" onload="success()" style=" 300px;height: 200px;">helei</iframe> <script src="/static/jquery.js"></script> <script> function success() { var data = $("#ifr1").contents().find('body').html(); console.log(data); alert(data) } </script>
# 接收iframe传来的post的数据 class Upload(View): def post(self,request): file_obj = request.FILES.get("file") print(file_obj) file_name = file_obj.name file = open(file_name,'wb') for chunk in file_obj.chunks(): file.write(chunk) return HttpResponse('上传成功')
7.Ajax 的csrf_token 认证方式
方式一:
手动,根据ajax的键值对中多发送一个token的key和value
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <h1>Login</h1> <form action="/login/" method="post"> {% csrf_token %} <input type="text" id="username"><br> <input type="text" id="password"><br> <input type="button" value="提交" id="btn"><br> </form> <script src="/static/jquery.js"></script> <script> $('#btn').click(function () { var value = $("[name='csrfmiddlewaretoken']").val(); var username = $("#username").val(); var password = $("#password").val(); console.log(value); $.ajax({ url:'/login/', type:'POST', data:{'username':username,'password':password,'csrfmiddlewaretoken':value}, success:function (data) { alert(data) } }) }) </script> </body> </html>
class Login(View): def get(self,request): return render(request,'login.html') def post(self,request): username = request.POST.get('username') password = request.POST.get('password') print(request.POST) print(username,password) return HttpResponse('ok')
方式二:
用一个form标签包含要发送的数据标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <h1>Login</h1> <form id="f11"> {% csrf_token %} <input type="text" name="username"><br> <input type="text" name="password"><br> <input type="button" value="提交" id="btn"> </form> <script src="/static/jquery.js"></script> <script> $('#btn').click(function () { console.log($('#f11').serialize()); $.ajax({ url:'/login/', type:'POST', data:$('#f11').serialize(), success:function (data) { alert(data) } }) }) </script> </body> </html>
class Login(View): def get(self,request): return render(request,'login.html') def post(self,request): username = request.POST.get('username') password = request.POST.get('password') print(request.POST) print(username,password) return HttpResponse('ok')
方式三:
在请求页面时,cookie中也会保存一份csrftoken的信息。利用jquery.cookie插件可以获取csrftoken数据,并使用。
安装jquery cookie插件,导入jquery.cookie的js文件
$.cookie('csrftoken')
$.ajax({
header:{'X-CSRFToken':$.cookie('csrftoken')}
})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <h1>Login</h1> {% csrf_token %} <input type="text" id="username"><br> <input type="text" id="password"><br> <input type="button" value="提交" id="btn"> <script src="/static/jquery.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $('#btn').click(function () { var username = $("#username").val(); var password = $("#password").val(); $.ajax({ url:'/login/', type:'POST', headers:{'X-CSRFToken':$.cookie('csrftoken')}, data:{'username':username,'password':password}, success:function (data) { alert(data) } }) }) </script> </body> </html>
class Login(View): def get(self,request): return render(request,'login.html') def post(self,request): username = request.POST.get('username') password = request.POST.get('password') print(request.POST) print(username,password) return HttpResponse('ok')
待待续....