Django从入门到放弃三 -- cookie,session,cbv加装饰器,ajax,django中间件,redis缓存等

学习地址:Django从入门到放弃 - 刘清政 - 博客园

一、CBV写法 ( class base views )  一般指在views.py文件中定义的各种类

       FBV ( function base views ) 一般指在views.py文件中定义的各种函数

1.1、CBV写法例子:

   CBV源码分析:views.Index.as_view() 具体是怎么实现的

   as_view 是一个classmethod方法,会将Index类传进去,

   as_view 函数内有个闭包函数 view ,view函数内会实例化Index类(self = cls(**initkwargs)),拿到一个Index类对象。

   调用Index类对象的self.dispatch方法,如果Index类里有这个方法就调用,否则就调用基类(也就是View类),

   View类 的 dispatch方法里面定义了请求方法:http_method_names = ['get', 'post', 'put', 'patch']这个方法就是对应了各种不同的HTTP请求方式。然后通过一个getattr反射方法拿到"Index类对象"下"get"方法返回值(就是自己写的get函数的内存地址)。然后加括号执行这个"get"函数,并拿到"get"函数的返回值,然后返回。

    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

  写一个CBV的例子:

在urls.py文件中:
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),

    # CBV测试,访问views.py文件下的CBTtest类,需要使用"CBTtest.as_view()"的方法
    path('test/', views.CBTtest.as_view()),    # as_view()拿到的是CBTtest类里面某个函数的名称[详细信息查看View类里面的as_view函数,最终返回一个get|post函数的内存地址]以及一个request对象。

]

# 在views.py文件中:

from django.views import View   # View类
class CBTtest(View):

    # GET请求访问这个函数
    def get(self,request):
        print(request.method)
        return render(request, 'test.html')

    # POST请求访问这个函数
    def post(self,request):
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            return HttpResponse('登录成功')
        else:
            return render(request, 'test.html')

# test.html
<form action="" method="post">
    <p>用户名:<input type="text" name="name"></p>
    <p>密码:<input type="text" name="pwd"></p>
    <p><input type="submit" value="提交"></p>
</form>

1.2、CBV写法总结

    1、定义请求方式的函数:函数名必须为父类View里面定义的http_method_names的名称。
    2、http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    3、根据前台发过来的请求的方式"GET或者POST"请求,就会响应对应名为"GET或者POST"的函数(每次响应一个函数)。
    4、"GET或者POST"的函数写法与FBV的函数写法一致。
    5、在URL里面配置 "path('test/', views.CBTtest.as_view())"

二、cookie的使用

2.1、什么是cookie

    Cookie是key-value键值对,存放在客户端,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!

  Cookie大小上限为4KB,一个服务器最多在客户端浏览器上保存20个Cookie,一个浏览器最多保存300个Cookie。

2.2、Django中操作Cookie

  获取Cookie:
     request.COOKIES['key']
     request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

  参数:
     default: 默认值
     salt: 加密盐
     max_age: 后台控制过期时间

   2.2.1、小例子  --  使用cookie判断用户的登录状态,判断用户是否需要重新登录


# 在views.py文件中
# 写一个功能,如果用户登录时cookie数据验证失败则重新登录一次。
def test_cookie_login(request):

    if request.method == 'POST':
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            obj=redirect('/index/')             
            obj.set_cookie('is_login',True)
            obj.set_cookie('name',name)
            return obj

    return render(request,'test.html')

# index函数通过判断用户的cookie是否在客户端浏览器上存在(没有过期),用户是否需要再次登录。
def index(request):
    print(request.COOKIES)
    is_login=request.COOKIES.get('is_login')
    name=request.COOKIES.get('name')
    if is_login:  # 如果cookie验证成功就返回首页
        return render(request,'index.html',{'name':name})
    else:         # 否则就返回登录页面
        return redirect('/test_cookie_login/')

# 在urls.py文件中
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # cookie与session
    path('test_cookie_login/', views.test_cookie_login),
    re_path('^index/$', views.index ),

]

   2.2.2、小例子  --  用户当前访问的页面(/aaa/),在使用登录装饰器后(用户登录后),这里重定向的值是写死的,依然返回用户想要登录的页面(/aaa/)

# 在views.py文件中
# 用户登录装饰器
def login_auth(func):
    def inner(request,*args,**kwargs):
        next_url=request.get_full_path()            # 用户当前访问的页面(/test_order/),在使用登录装饰器后(用户登录后),依然返回用户想要登录的页面(/test_order/)
        is_login = request.COOKIES.get('is_login')
        if is_login:
            return func(request,*args,**kwargs)
        else:
            # 此时用户访问的地址格式为:http://127.0.0.1:8000/test_cookie_login/?next=/test_order/
            return redirect('/test_cookie_login/?next=%s' %next_url)  # 记录下用户当前访问的页面,传值给"next"
    return inner


# 写一个功能,如果用户登录时cookie数据验证失败则重新登录一次。
def test_cookie_login(request):
    if request.method == 'POST':
        url = request.GET.get('next')     # 通过request获取到数据"?next=/test_order/",拿到关键字'next'对应的数据"/test_order/"
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            obj=redirect(url)             # 用户在登录成功后,直接重定向到"/test_order/"目录下(这个目录是用户一开始就想要访问的目录)
            obj.set_cookie('is_login',True)
            obj.set_cookie('name',name)
            return obj
    return render(request,'test.html')


# 默认网站的首页,访问的时候需要先登录
@login_auth  # index=login_auth(index)
def index(request):
    name=request.COOKIES.get('name')
    return render(request,'index.html',{'name':name})  # index.html为网站首页


# 用户当前访问的页面(/test_order/),在使用登录装饰器后(用户登录后),依然返回用户想要登录的页面(/test_order/)
@login_auth
def test_order(request):
    return render(request,'order.html')  # order.html 为购物车页面


# 在urls.py文件中
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # re_path('^index/$', views.index ),

    # cookie与session
    re_path('test_cookie_login/', views.test_cookie_login),
    re_path('^index/$', views.index ),
    re_path('^test_order/$', views.test_order ),

]

 2.2.3、cookie的其他用法

    obj.set_cookie('is_login',True,max_age=5)     :设置cookie的缓存时间/超时时间(s)
    obj.set_cookie('is_login',True,path='/index/')   :表示指定cookie的路径(访问此路径时会产生cookie),非此路径将不产生cookie。
    obj.delete_cookie("is_login")                            :删除一个cookie(指定cookie的"key")

三、session的使用

3.1、什么是session?

      Session是key-value键值对,存放在服务端,Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。

3.2、Django中Session相关方法

    3.2.1、Session的生成

def test_session(request):

    # 客户端A,生成A_session
    request.session['username']='szq'    # 生成一个session_key与session_data

    # 客户端B,生成B_session
    request.session['age'] = '18'        # 生成一个session_key与session_data
    request.session['sex']='male'        #  生成一个session_data与request.session['age'] = '18'公用一个session_key

    '''
    request.session['username']='szq'  # 执行的操作的有:    
    
        1.生成随机字符串session_key(这个字符串是返回给客户端的,在不同的客户端会生成不同的生成随机字符串session_key)    
            # 在数据库内对应的就是:session_key(一个session_key里面可以包含多个字典)
            
        2.在django表里生成一条记录                                
            # 在数据库内对应的就是:session_data(一个字典类型的数据),数据是加密后的{'username':'szq'}
            
        4.把(session_key)随机字符串返回到客户端浏览器上
    '''

    return HttpResponse('ok')

    session在数据库内存在的格式如下:默认存放在数据库的django_session表

    session_key:一串随机字符串,这个字符串是返回给客户端的

    session_data:一个字典类型的数据,数据是加密后的 ["username"]="sudada"

    以上2者的对应关系:session_key:session_data

    3.2.1、Session的操作(增删改查等操作)


# 设置session['username']的值为123456
request.session['username'] = 123456

# 获取session['username']的值"szq"
request.session['username']       

# 删除session['username']这个key及对应的value
del request.session['username']          

# 获取"session['key']"的值:dict_keys(['sex', 'username', 'age'])
request.session.keys()            

# 获取"session['value']"的值:dict_values(['male', 'szq', '18'])
request.session.values()          

 # 获取"session['key:value']"的值:dict_items([('sex', 'male'), ('username', 'szq'), ('age', '18')])
request.session.items()          

# 获取当前客户端连接服务器会话session的key"of1op71gr1qaf9sj8ey31gmeehlkrynk"
request.session.session_key

# 检查会话session的key在数据库中是否存在
request.session.exists("0ynniz04nh3gd7plli88xyyhztu7hony")  

#删除当前会话的所有Session数据(只删数据库,不删除浏览器的cookie)
request.session.delete()                     

# 删除当前的会话数据并删除会话的Cookie(数据库和cookie都删)
request.session.flush()                       
        # 这用于确保前面的会话数据不可以再次被用户的浏览器访问
        # 例如,django.contrib.auth.logout() 函数中就会调用它。

# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效,再次访问时会生成一个新的session
    * 如果value是个datatime或timedelta,session就会在这个时间后失效,再次访问时会生成一个新的session
    * 如果value是0,用户关闭浏览器session就会失效,再次访问时会生成一个新的session
    * 如果value是None,session会依赖全局session失效策略。

3.3、Django中的Session配置

1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其他公用设置项:放在setting.py文件内
SESSION_COOKIE_NAME = "sessionid"           # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"                   # Session的cookie保存的路径(默认),设置为"/"则所有路径都生效
SESSION_COOKIE_DOMAIN = None                # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False               # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True              # 是否Session的cookie"只支持http传输"(默认)
SESSION_COOKIE_AGE = 1209600                # Session的cookie失效日期(默认2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False     # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False          # 是否每次请求都保存Session,默认修改之后才保存(默认)

四、cbv加装饰器

   1.在CBV内部的函数加装饰器

   2.直接在类上使用装饰器(需要指定"实例化对象"的名称)

# 在urls.py文件中

urlpatterns = [
    path('admin/', admin.site.urls),

    # CBV装饰器测试
    path('test_cbv/', views.CBTtest.as_view()),
    re_path('test_cookie_login/', views.test_cookie_login),

]

# 在views.py文件中
# 用户登录装饰器
def login_auth(func):
    def inner(request, *args, **kwargs):
        next_url = request.get_full_path()  # 用户当前访问的页面(/test_cbv/),在使用登录装饰器后(用户登录后),依然返回用户想要登录的页面(/test_cbv/)
        is_login = request.COOKIES.get('is_login')
        if is_login:
            return func(request, *args, **kwargs)
        else:
            # 此时用户访问的地址格式为:http://127.0.0.1:8000/test_cookie_login/?next=/test_cbv/
            return redirect('/test_cookie_login/?next=%s' % next_url)  # 记录下用户当前访问的页面,传值给"next"
    return inner


# 登录接口
def test_cookie_login(request):
    if request.method == 'POST':
        url = request.GET.get('next')         # 通过request获取到数据"?next=/test_cbv/",拿到关键字'next'对应的数据"/test_cbv/"
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            obj = redirect(url)             # 用户在登录成功后,直接重定向到"/test_cbv/"目录下(这个目录是用户一开始就想要访问的目录)
            obj.set_cookie('is_login',True)
            obj.set_cookie('name',name)
            return obj
    return render(request,'login.html')


# CBV装饰器的用法及写法--方式一(在类实例化的函数里使用)
from django.views import View
from django.utils.decorators import method_decorator


class CBTtest(View):
    # GET请求访问这个函数
    @method_decorator(login_auth)
    def get(self,request):
        print(request.method)
        return render(request, 'login.html')

    # POST请求访问这个函数
    @method_decorator(login_auth)
    def post(self,request):
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            return HttpResponse('登录成功')
        else:
            return render(request, 'login.html')

# CBV装饰器的用法方式二(在类上面印有)
@method_decorator(login_auth,name='get')
@method_decorator(login_auth,name='post')
class CBTtest(View):
    # GET请求访问这个函数
    def get(self,request):
        print(request.method)
        return render(request, 'login.html')

    # POST请求访问这个函数
    def post(self,request):
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        if name == 'szq' and pwd == '123':
            return HttpResponse('登录成功')
        else:
            return render(request, 'login.html')

五、ajax提交数据,提交json格式数据

  5.1、什么是ajax?

   1、ajax(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)。
      同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
      异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
2、ajax除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

5.2、ajax应用场景?

5.3、ajax的优点

    1、ajax使用Javascript技术向服务器发送异步请求
    2、ajax无须刷新整个页面

5.4、ajax使用例子1 --  使用ajax(GET|POST)请求一个登陆界面

        后端获取数据时,使用request.GET.get或者request.POST.get的方式获取数据。

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # 测试AJAX
    re_path('test_ajax/', views.test_ajax),
    re_path('ajax/', views.ajax),
    re_path('index/', views.index),
]

# views.py文件中
import json
def test_ajax(request):
    return render(request,'ajax.html')

def ajax(request):
    bak_msg={'user':None,'msg':'用户名密码错误'}

    # 前端ajax使用get或者post方式请求数据,后端使用request.GET|POST.get的方式获取数据
    name=request.GET.get('name')                  
    pwd=request.GET.get('pwd')
    if name == 'szq' and pwd == '123':
        bak_msg['user']=name
        bak_msg['msg']='登录成功'
    return HttpResponse(json.dumps(bak_msg))

def index(request):
    return render(request,'index.html')

   前端ajax.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    <p>用户名:<input type="text" name="name" id="name"></p>
    <p>密码:<input type="text" name="pwd"></p>
{#    <p><input type="submit" value="提交"></p>#}
</form>

<button id="btnl">提交</button>

{#针对服务端,是一个异步的请求#}
<script>

    $('#btnl').click(function () {
        var name=$('#name').val()            {# 获取用户名信息 #}
        var pwd=$('[name="pwd"]').val()      {# 获取密码信息 #}
        $.ajax({
            url: '/ajax/',                   {# 请求到服务器的URL #}
            type: 'get',                     {# 给服务器发送的请求方式,后端取数据需要从request.GET(POST)里面取数据 #}
            data:{'name':name,'pwd':pwd},    {# 请求服务器时携带的信息(用户名和密码) #}
            success: function (data) {       {# data拿到的是服务端"ajax"请求的返回结果(是一个异步的返回结果) #}
                {# alert(data)               {# 拿到服务器数据后的返回值 #}
                var msg=JSON.parse(data);    {# 拿到后端返回的json格式的数据,并转译 #}
                if (msg.user){               {# 如果用户存在,表示登录成功 #}
                    location.href='/index/'  {# 登录成功后,重定向到'/index/'页面 #}
                }else {
                    alert(msg.msg)           {# 登录失败返回的结果 #}
                }
            }
        })
    })

</script>

</body>
</html>

5.4、ajax使用例子2 --  使用ajax(JSON格式)请求一个登陆界面

        后端获取数据时,使用request.body的方式获取数据。

        后端获取数据时,需要将获取到的二进制字符串转译。

        后端获取数据时,需要把json格式的字符串转译成Python格式的。

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # 测试AJAX
    re_path('test_ajax/', views.test_ajax),
    re_path('ajax/', views.ajax),
    re_path('index/', views.index),
]

# views.py文件中
import json
def test_ajax(request):
    return render(request,'ajax.html')

def ajax(request):
    print(request.body)
    # b'{"name":"szq","pwd":"123"}' 获取到前端请求的数据(包含用户名和密码),是一个二进制格式,需要转译
    # 这里不是通过request.POST获取到数据了

    res=request.body.decode('utf-8')   # 把二进制格式的数据转译成正常
    res_dic=json.loads(res)            # 把json格式的数据转换成python格式

    bak_msg={'user':None,'msg':'用户名密码错误'}
    if res_dic['name'] == 'szq' and res_dic['pwd'] == '123':
        bak_msg['user']=res_dic['name']
        bak_msg['msg']='登录成功'


    return HttpResponse(json.dumps(bak_msg))

def index(request):
    return render(request,'index.html')

   前端ajax.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    <p>用户名:<input type="text" name="name" id="name"></p>
    <p>密码:<input type="text" name="pwd"></p>
{#    <p><input type="submit" value="提交"></p>#}
</form>

<button id="btnl">提交</button>

{#针对服务端,是一个异步的请求#}
<script>

    $('#btnl').click(function () {
        var name=$('#name').val()            {# 获取用户名信息 #}
        var pwd=$('[name="pwd"]').val()      {# 获取密码信息 #}
        $.ajax({
            url: '/ajax/',                   {# 请求到服务器的URL #}
            type: 'post',                    {# 给服务器发送的请求方式 #}

            {# 如果需要向后台提交json格式的字符串时,需要指定:contentType:'application/json' #}
            {# 当contentType为json格式的时候,后端取数据需要从request.body里面取数据 #}
            contentType:'application/json',

            {# 请求服务器时携带的信息(用户名和密码)json格式的字符串 #}
            {# 如果不加"JSON.stringify"那么后端获取到的数据格式就为b'name:szq,pwd:123',加上"JSON.stringify"之后,后端获取到的数据格式就为b'{"name":"szq","pwd":"123"}'#}
            data:JSON.stringify({'name':name,'pwd':pwd}),


            success: function (data) {       {# data拿到的是服务端"ajax"请求的返回结果(是一个异步的返回结果) #}
                {#alert(data)                {# 拿到服务器数据后的返回值 #}
                var msg=JSON.parse(data);    {# 拿到后端返回的json格式的数据,并转译 #}
                if (msg.user){               {# 如果用户存在,表示登录成功 #}
                    location.href='/index/'  {# 登录成功后,重定向到'/index/'页面 #}
                }else {
                    alert(msg.msg)           {# 登录失败返回的结果 #}
                }
            }
        })
    })

</script>

</body>
</html>

  5.5、Ajax---->服务器------>Ajax执行流程图

六、基于form表单上传文件

       前端form表单需要指定:enctype="multipart/form-data"

       后端获取form表单上传文件内容时,需要用"request.FILES.get"

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # from表单上传文件
    re_path('file_upload/', views.file_upload),
]

# 在views.py文件中
def file_upload(request):
    if request.method == "POST":

        # 上传一个图片时返回的数据(字典类型):<MultiValueDict: {'myfile': [<InMemoryUploadedFile: DDD.jpg (image/jpeg)>]}>
        print(request.FILES)

        # 查看这个字典类型数据里面'myfile'这个key对应的value的值的类型:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        print(type(request.FILES.get('myfile')))
        # 查看源码,InMemoryUploadedFile类
        from django.core.files.uploadedfile import InMemoryUploadedFile

        # 通过key拿到对应图片的数据内容
        myfile=request.FILES.get('myfile')

        # 通过图片的数据内容拿到图片的文件名
        file_name=myfile.name
        print(file_name)        # bbb.jpg

        # 保存这个图片到服务器
        with open(file_name,'wb')as f:
            for line in myfile:           # 这里循环myfile这个文件
                f.write(line)

        return HttpResponse('文件上传成功')

    return render(request,'file_upload.html')

  前端'file_upload.html'文件内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post" enctype="multipart/form-data">   {# 使用form表单上传数据时的固定用法 #}
    <p>用户名:<input type="text" name="name" id="name"></p>
    <p>文件:<input type="file" name="myfile"></p>
    <p><input type="submit" value="form表单提交"></p>
</form>

</body>
</html>

七、基于ajax上传文件

       前端ajax:var formdata=new FormData

       后端获取form表单上传文件内容时,需要用"request.FILES.get"

# 在urls.py文件中
urlpatterns = [
    path('admin/', admin.site.urls),

    # from表单上传文件
    re_path('file_upload/', views.file_upload),
]

# 在views.py文件中
def file_upload(request):
    if request.method == "POST":

        # 上传一个图片时返回的数据(字典类型):<MultiValueDict: {'myfile': [<InMemoryUploadedFile: DDD.jpg (image/jpeg)>]}>
        print(request.FILES)

        # 查看这个字典类型数据里面'myfile'这个key对应的value的值的类型:<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        print(type(request.FILES.get('myfile')))
        # 查看源码,InMemoryUploadedFile类
        from django.core.files.uploadedfile import InMemoryUploadedFile

        # 通过key拿到对应图片的数据内容
        myfile=request.FILES.get('myfile')

        # 通过图片的数据内容拿到图片的文件名
        file_name=myfile.name
        print(file_name)        # bbb.jpg

        # 保存这个图片到服务器
        with open(file_name,'wb')as f:
            for line in myfile:           # 这里循环myfile这个文件
                f.write(line)

        return HttpResponse('文件上传成功')

    return render(request,'file_upload.html')

 前端'file_upload.html'文件内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="/static/jquery-3.3.1.js"></script>
    <title>Title</title>
</head>
<body>

<form action="" method="post" enctype="multipart/form-data">   {# 使用form表单上传数据时的固定用法 #}
    <p>用户名:<input type="text" name="name" id="name"></p>
    <p>文件:<input type="file" name="myfile" id="myfile"></p>
</form>

<button id="btnl">ajax提交</button>

<script>

    $('#btnl').click(function () {

        var formdata=new FormData;
        formdata.append('name',$("#name").val());
        formdata.append('myfile',$("#myfile")[0].files[0]);

        $.ajax({
            url: '/file_upload/',
            type: 'post',
            contentType:false,
            processData:false,
            data:formdata,
            success: function (data) {
                alert(data)
            }
        })
    })

</script>

</body>
</html>

八、django中间件

8.1、什么是中间件?

       中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

8.2、中间件有什么用?  用户所有的请求都会先经过中间件,然后到达URL路由层,用户拿到数据的返回结果之后,也是先经过中间件(倒序),然后到达用户。

       1、如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。
      2、可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。
      3、Django默认的中间件:(在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下

# 每一个中间件都有具体的功能
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

    4、当用户发起请求的时候会依次经过所有的的中间件,这个时候的请求时process_request,最后到达views的函数中,views函数处理后,在依次穿过中间件,这个时候是process_response,最后返回给请求者。

8.3、自定义中间件

# 在settings.py文件中的MIDDLEWARE自定义中间件(app01目录下的mytest文件)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',

    # 自定义中间件
    'app01.mytest.MyMid1',
    'app01.mytest.MyMid2',
]

# mytest.py文件
from django.shortcuts import render,HttpResponse,redirect
from django.utils.deprecation import MiddlewareMixin

class MyMid1(MiddlewareMixin):
    def process_request(self,request):
        print('MyMid1 --- process_request')
        # return HttpResponse('ok')  # 这里不能返回,返回的话,后面的request和response就不会执行了。

    def process_response(self,request,response):   # response是一个<class 'django.http.response.HttpResponse'>
        print('MyMid1 --- process_response')
        return response                            # 用户请求后,必须要一个response数据返回

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('MyMid1 --- process_view')

class MyMid2(MiddlewareMixin):
    def process_request(self,request):
        print('MyMid2 --- process_request')

    def process_response(self,request,response):    # response是一个<class 'django.http.response.HttpResponse'>
        print('MyMid2 --- process_response')
        return response                             # 用户请求后,必须要一个response数据返回

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('MyMid2 --- process_view')

# 返回结果就是中间件的执行顺序
# MyMid1 --- process_request
# MyMid2 --- process_request
# MyMid1 --- process_view
# MyMid2 --- process_view
# MyMid2 --- process_response
# MyMid1 --- process_response

 下图进行分析上面mytest.py文件的过程:

    当最后一个"URL请求"process_request到达路由关系映射之后,请求会去"路由控制"process_view,然后依次往下,到达"视图函数",最后通过process_response依次返回到达用户。

九、中间件应用场景
    9.1、做IP访问频率限制
    某些IP访问服务器的频率过高,进行拦截,比如限制每分钟不能超过20次。

    9.2、URL访问过滤
    如果用户访问的是login视图(放过)
    如果访问其他视图,需要检测是不是有session认证,已经有了放行,没有返回login,这样就省得在多个视图函数上写装饰器了!

    9.3、作为延伸扩展内容,可以尝试着读一下以下两个自带的中间件:
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',

十、CSRF_TOKEN跨站请求伪造

使用Django自带的中间件:'django.middleware.csrf.CsrfViewMiddleware' 可以防止跨站请求伪造。

原理:在前端HTML页面加上"{% csrf_token %}",那么在访问的时候,会生成一个"<input type="hidden" name="csrfmiddlewaretoken" value="ODrQkcqNbmyYMREnjoCGUjwBuS0Cfz4m4VwzktZh1ryidjHUEFajE3BwdEgHpcWw">"随机字符串,每次访问生成的字符串都是不同的。前端HTML页面在POST提交时会携带这个随机字符串,然后提交到后端服务器,服务器会确认此次请求是否有这个随机字符串,如果有则可以访问,如果没有访问失败。

十一、Django缓存机制

Django缓存机制 - 刘清政 - 博客园

11.1、redis缓存配置,视图层面

# redis缓存,全局配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100},   # 连接池大小100个
            # "PASSWORD": "密码",
        }
    }
}

# view视图使用
import time
from django.views.decorators.cache import cache_page
@cache_page(5)   # 缓存的时间5s
def index(request):
    ctime=time.time()
    return HttpResponse(ctime)

# 此时请求页面,拿到一个数据,这个数据会在redis存5秒,之后会被删除。此时可以使用命令"keys *" 查看到对应的缓存数据。
127.0.0.1:6379> keys *
1) ":1:views.decorators.cache.cache_page..GET.b8d12f80e31bbf250e9284534b72f75a.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC"
2) "name"
3) ":1:views.decorators.cache.cache_header..b8d12f80e31bbf250e9284534b72f75a.en-us.UTC"

11.2、django操作redis,使用连接池的方式

# redis缓存,全局配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100},   # 连接池大小100个
            # "PASSWORD": "密码",
        }
    }
}

# view视图使用
from django_redis import get_redis_connection
def test(request):
    conn=get_redis_connection('default')  # 对接全局配置(setting.py里面的redis连接池)
    name=conn.get("name")                 # 拿到redis里面的"key"对应的"value"(字符串格式)
    return HttpResponse(name)

# 返回数据
sudada

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值