• 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
  • 相关阅读:
    C#3.0入门系列(八)之GroupBy操作
    C#3.0入门系列(七)之OR工具介绍
    C#3.0入门系列(九)之GroupBy操作
    C#3.0入门系列(十二)Lambda表达式中Lifting
    C# 3.0入门系列(四)之Select操作
    图示offsetWidth clientWidth scrollWidth scrollTop scrollLeft等属性的细微区别
    C# 3.0入门系列(二)
    一步一步学Linq to sql(四):查询句法
    Linq To Sql进阶系列
    Linq To Sql进阶系列(二)M:M关系
  • 原文地址:https://www.cnblogs.com/40kuai/p/6994352.html
Copyright © 2020-2023  润新知