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

本节内容

  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 = ""
View Code

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)),
        ]
View Code

c.局部视图使用

    a. 引入TemplateTag

        {% load cache %}

    b. 使用缓存

        {% cache 5000 缓存key %}
            缓存内容
        {% endcache %}
View Code

***自定义:利用中间件/装饰器/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="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>
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

待待续....

最新内容可以看我的blog: 40kuai
原文地址:https://www.cnblogs.com/40kuai/p/6994352.html