Python Django缓存,信号,序列化,文件上传,Ajax登录和csrf_token验证

时间:2022-10-10 22:42:02

本节内容

  1. models操作

  2. Django的缓存
  3. 请求方式

  4. 序列化

  5. Form 配合Ajax实现登录认证
  6. 上传文件

  7. 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',
]
}
}

python-memcached模块

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',
]
}
}

pylibmc模块

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)
})
}
}
})
})

index.html

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))

views.py

6. 上传文件

文件上传我知道三种方式:

第一种:form 表单提交文件, 页面会刷新

<form action="/upload/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file">
<input type="submit" value="提交">
</form>

upload.html

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('上传成功')

views.py

第二种: 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>

upload.html

第三种: 伪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="" onload="success()" style="width: 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>

index.html

#  接收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('上传成功')

views.py

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>

login.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')

views.py

方式二:

用一个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>

login.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')

views.py

方式三:

在请求页面时,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>

login.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')

views.py

待待续....