Ajax前后端发送数据、文件、分页器和案例、分页的原理、分页类的使用、cookie和session的介绍

前后端数据传输的编码格式(contentType)

前后端数据传输的请求方式有两种:get、post。主要研究post请求方式的编码格式
"""
get请求数据就是直接放在url?后面的:
  url?username=jason&password=123
"""

前后端传输数据的编码格式:

  urlencoded、form-data、json

可以朝后端发送post请求的方式:form表单、Ajax、postman

# 研究form表单发送post请求的时候数据的编码格式(请求头中的Content-Type:参数)

 1. 默认提交编码格式是:application/x-www-form-urlencoded,它的数据格式是:username=jason&password=123

问:针对application/x-www-form-urlencoded格式的数据,在Django后端是如何接收数据的

  Django后端针对符合urlcoded编码格式的数据都会自动帮你解析封装到request.POST中。

2. multipart/form-data编码格式,数据格式:boundary=----WebKitFormBoundary7iwnnLo3TDiHIAQz--->文件数据(二进制类型)

将编码格式从urlcoded改成form-data:form表单除了可以提交普通数据,还能够提交文件数据。

问:针对multipart/form-data格式的数据,Django后端是如何接收数据的

  针对普通数据django把数据封装到了request.POST中

  文件数据django把数据封装到了reuqest.FILES中

Ajax提交post请求的数据时,编码格式:application/x-www-form-urlencoded; 数据类型:a=1&b=2

django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中

Ajax提交json格式数据

前端向后端提交数据的时候一定要保证编码格式和数据的真正格式是一致的

提交json格式的数据必须满足两个条件:

  1. 你的编码格式必须是json格式的: 一定要把ajax默认提交的urlencode改为'application/json'

  2. 你的数据必须是jsonl类型:{"a":1}
对于json格式的数据,django后端不在做任何的封装,数据是纯原生的,发送过来的数据是二进制形式的,需要我们自己进行处理二进制数据

例子:

html文件中:

复制代码
<body>
<button class="btn btn-success" id="d1">提交</button>
<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            // 将{'username':'rui','age':18}对象序列化为json格式
            data:JSON.stringify({'username':'rui','age':18}),
            // 指定编码格式
            contentType:'json',
            //回调函数
            success:function (res) {
            }
        })

    })

</script>
</body>
复制代码

views文件:

复制代码
from django.shortcuts import render
import json
# Create your views here.
def index(request):
    if request.method =='POST':
        print(request.POST) # <QueryDict: {}>
        print(request.body) #数据格式是二进制 :b'{"username":"rui","age":18}'
        # 要解码并转成字符串
        json_bytes = request.body
        # json_str = json_bytes.decode('utf-8') #将二进制类型的数据解码为字符串类型
        # json_dict = json.loads(json_str) # 将字符串反序列化为json格式
        json_dict = json.loads(json_bytes) #  json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化
        print(json_dict) # {'username': 'rui', 'age': 18}
    return render(request,'index.html')
复制代码

结果:

 Ajax提交json格式数据需要注意点:

  1.contentType参数指定成:application/json

  2.数据是真正的json格式数据

  3.django后端不会帮你处理json格式数据需要你自己去request.body获取并处理

Ajax提交文件数据(重要)

Ajax提交文件需要借助于js内置对象FormData

html文件中:前端

复制代码
<body>
<p>
    username:<input type="text" id="d1" class="form-control">
</p>
<p>
    password:<input type="text" id="d2" class="form-control">
</p>
<p>
    上传文件:<input type="file" id="d3" class="form-control">
</p>
<button class="btn btn-danger" id="d4">提交</button>
<script>
    //绑定点击事件
    $('#d4').click(function () {
        // 1.需要先利用FormData内置对象
        var formDataObj = new FormData();
        // 2.在formDataObj对象中添加普通的键值对
        formDataObj.append('username',$('#d1').val()) //$('#d1').val()就是username具体的数据
        formDataObj.append('password',$('#d2').val())
        // 3.添加文件对象
        formDataObj.append('file',$('#d3')[0].files[0])// $('#d3')[0] 获取标签对象,$('#d3')[0].files[0]获取标签对象里的文件
        // 将对象基于Ajax发送给后端
        $.ajax({
            url:'',
            type:'post',
            data:formDataObj, //直接将对象放在data后面即可
            //ajax发送文件必须指定的两个参数
            contentType:false, //不需要使用任何编码,Django后端能够自动识别formdata对象
            processData: false, // 告诉浏览器不要对数据进行任何处理
            success:function (res) {

            }
        })
    })
</script>
</body>
复制代码

如图:

 views文件:后端

复制代码
# 接收文件数据
def ad_file(request):
    # 判断当前请求是否是ajax请求 返回布尔值
    if request.is_ajax():
        print(request.is_ajax()) # True
        if request.method == 'POST':
            print(request.POST)  #<QueryDict: {'username': ['rui'], 'password': ['123']}>
            print(request.FILES)  # <MultiValueDict: {'file': [<InMemoryUploadedFile: meizi.jpeg (image/jpeg)>]}>
    return render(request, 'ad_file.html')
复制代码

如图:

 Ajax结合layer弹窗实现删除的二次确认

我们使用第三方的UI弹窗:layer、sweetalert、:https://layuiweb.com/layer/index.htm---------------->layer

删除作者操作:

html页面:

复制代码
{% extends 'home.html' %}


{% block content %}
    <h1 class="text-center">作者列表展示</h1>
    <a href="/authoradd/" class="btn btn-info">添加作者</a>
    <table class="table table-striped table-hover">
        <thead>
        <tr>
            <th>姓名</th>
            <th>年龄</th>
            <th>电话</th>
            <th>地址</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for foo in author_queryset %}
            <tr class="tr_{{ foo.id }}">
                <td>{{ foo.name }}</td>
                <td>{{ foo.age }}</td>
                <td>{{ foo.author_detail.phone }}</td>
                <td>{{ foo.author_detail.addr }}</td>
                <td>
                    <a href="/authoradd/?id={{ foo.pk }}" class="btn btn-success">修改</a>
                    {#                    <a href="/authordelete/?id={{ foo.pk }}" class="btn btn-danger">删除</a>#}
                    <button class="btn btn-danger" delete_id="{{ foo.pk }}">删除</button>
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
{% endblock %}
{% block js %}
    <script>
        // 给删除按钮绑定点击事件
        $(".btn").click(function () {
            //删除的逻辑:当我们点击删除俺就的时候,应该获取点击行的id值,然后把这个id值传给后端,后端接收id值
            var d_id = $(this).attr('delete_id'); // $(this):是$(".btn")对象,delete_id:自定义属性,获取的是作者的id
            var _this = $(this); //$(this):还是$(".btn")对象,定义为新的变量方便后面的使用
            // 获取到id值之后,就发送Ajax请求,做一个二次确认
            layer.confirm('你确认要删除这条数据吗?', {
                btn: ['确定'] //按钮d
            }, function () {
                // 发送ajax请求 做个二次确认
                $.ajax({
                    url:'/authordelete/', // 这边不能不能为空(空就是当前地址),要指定删除的路由地址
                    type:'post',
                    data:{'id':d_id},//  'id':是传给后端的,d_id这个是具体的实际值,那么在后端获取时的id名应该和'id'一样
                    success:function (res) {
                        if(res.code == 200){
                            {#layer.msg(res.msg, {icon:1})#}
                            {#    location.reload(); //这种删除会立马刷新页面不会看到弹窗 #}
                            {#layer.msg(res.msg);#}
                            {#_this.parent().parent().remove(); //  一个parent就是往上找一个父辈#}
                            // 找到对应的tr行,然后删除这个tr行
                             layer.msg(res.msg); //接收后端返回的’删除成功‘
                            $(".tr_"+ d_id).remove(); // ".tr_"+id:字符串的拼接,结果="tr_{{ foo.id }}",d_id是最初获取的要被删除的id值
                        }
                    }
                });
            });
        })
    </script>
{% endblock %}
复制代码

views页面:

# 删除作者,
def author_delete(request):
    id = request.POST.get('id') # 'id':从后端拿到id值
    models.Author.objects.filter(pk=id).delete()
    return JsonResponse({'code': 200, 'msg': '删除成功'})

 批量插入数据(bulk_create)

以下是将数据一个一个插入数据库就会很慢,因为要访问数据库加入一条数据在访问数据库再加入数据,如果数量过多会导致数据库崩溃

  将需要添加的数据先放进一个列表里,然后再用数据库进行添加,节省事件,当你想要批量插入数据的时候 使用orm给你提供的bulk_create能够大大的减少操作时间

 结果:

 推导分页的原理

分页:当我们要展示的数据特别多的时候,一页展示不完,这个时候就需要将腰斩是的额数据分成多页展示。

分页中需要的参数:

  1. 总数据的数量

  2. 每页展示的数据数量

  3.展示的页数

  4. 总页数 = 总数据量 / 每页展示的多少条数据

  5. 当前第几页(前端传到后端的)

计算总页数:使用divmod(总数据量,每页展示的数据量),有余数,总页数就加1

divmod(100,10)
(10, 0)  # 10页
divmod(101,10)
(10, 1)  # 11页
divmod(99,10)
(9, 9)  # 10页
# 余数只要不是0就需要在第一个数字上加一

 views中:大体逻辑

复制代码
def index(request):
    # 当前是第几页
    current_page = request.GET.get('page')  # 从前端页面传来的page值,类型是字符串,GET方式可以通过url往后端传值
    try:
        current_page = int(current_page)
    except Exception:
        current_page = 1
    user_list = models.UserInfo.objects.all()  # 获取全部用户的信息
    # 总数据量:
    all_count = user_list.count()  # 1000,user_list中获取数据总量

    per_page_num = 10  # 每页展示的数据
    start_page = (current_page - 1) * per_page_num  # 展示的数据的起始位置
    end_page = current_page * per_page_num  # 展示的数据的终止位置
    # 计算总页码数据
    page_num, more = divmod(all_count, per_page_num)  # divmod:得到一个元组结果有两个参数,一个是商一个是余数
    if more:  # 有余数,页码数量+1
        page_num += 1
    html = ''
    # current_page = 15
    # 10-21
    xxx = current_page
    if current_page < 6:  # 判断页码是否小于6,以防出现负数页面爆出
        current_page = 6
    for i in range(current_page - 5, current_page + 6):  # 循环 ,current_page=6:range(1,11),一次展示11个页码
        if xxx == i:
            html += '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i) #当前页码做高亮处理
        else:
            html += '<li><a href="?page=%s">%s</a></li>' % (i, i)
  user_list = user_list[start_page:end_page] # 进行切分出要展示的数据
 return render(request, 'index.html', locals())
复制代码

分页类的使用

现成的分页类样式是基于bootstrap,必须得导入bootstrap

以后我们针对像分页类这种第三方工具,我们一般在Django中创建一个utils文件夹保存

在该文件夹内对模块进行功能性划分,utils可以在每个应用下创建,具体结合实际情况

在utils文件夹中建立一个mypage.py文件来复制分页封装类

代码:

复制代码
class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property # 将方法伪装成属性来进行使用
    def start(self): # 展示的数据起始位置
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self): # 展示的数据的终止位置
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)
复制代码

cookie和session的介绍(重要)

HTTP协议的特性之一:无状态

背景信息:

  1. 早期的时候一些网站都是静态网站,不需要登录,比如:新闻类、博客等

  2. 随着技术的发展以及用户的要求,诞生了诸如支付宝、淘宝、京东等电商网站,这些网站就必须要求用户登录,如果不登录,上家怎么知道是谁买的东西? 登录的目的其实就是上架可以识别这个用户是谁?

  3. 诞生了保存用户状态的技术:cookie和session

以登录功能为例,分析cookie的原理:

  比如说:进入到一个淘宝网站,第一次肯定是需要登录的,如果登录成功,淘宝网站不保存你的用户信息,那么之后每一次进行网站页面,都需要在重新登录。

  通过利用cookie就可以解决上述的问题:

    在第一次登录成功之后,Django后端让浏览器把你的用户名和密码保存在浏览器中,下次在访问淘宝页面的时候,浏览器会自动把它之前保存的用户名和密码在一块提交到Django后端,Django后端每次拿到浏览器发过去的用户名和密码再次做验证。

cookie的原理:第一次登录成功之后,数据保存在浏览器上面,后面通过比较浏览器提交给后端的用户名和密码再次做验证。很明显的问题是:数据不够安全。

解决上述数据不安全的问题:使用session,其实就是做了优化,把原本存在浏览器的数据保存到后端了。

session的原理:

  第一次登录成功之后,把用户信息保存到后端,其中,Django默认是吧用户信息保存到数据表:django_session表中了。

1. 先生成一个随机字符串

2. 把用户的信息保存到django_session表中

 3. Django后端会把随机字符串告诉浏览器,让浏览器保存起来

4. 以后用户每次访问页面的时候,浏览器每次都要把随机字符串提交过来,Django后端拿到随机字符串,去django_session 表中查询数据,如果查到了,就说明以前登录成功了,如果查不到,就说明还没有登录。相当于在表中执行了:select * from django_session where session_key = ''

但是如果都把用户信息保存到django_session表中:一旦数据量很大,查询就是致命的

解决问题:

  需要用到token
  token就是一个随机字符串------->保存着用户信息---------->字符串返回给前端------>每次都把token提交过来------>后端做验证.

  加密和解密都是后端做的,前端只需要每次把这个串来回传递就行了。

跟面试相关的:

  1. 保存在浏览器上的数据都称之为是cookie

  2. session是保存在服务端(后端)的

  3. session的数据相对安全,cookie不够安全

  4. session是基于cookie工作的

  5. django让浏览器保存cookie,用户有权可以设置浏览器不保存

  6. session离开cookie一定就不能工作了 ,这句话是不对的!!!!!

Django操作cookie

cookie的作用:保存用户信息,将数据保存在了浏览器中

三板斧:return HttpResponse、return render、return redirect

这样使用:

  1. obj = HttpResponse

      return obj

  2. obj = render

   return obj

  3. obj = redirect

   return obj

# 操作cookie的时候,就用到了这个obj对象。

设置cookie:

# 设置cookie:
obj.set_cookie('username', 'kerry', max_age=5);
# 会在浏览器中保存这个键值对:username:kerry,控制台----->Application
# 过期时间:max_age=秒数


# 获取cookie
request.COOKIES.get('username')

#登录的原理:

  第一次输入用户名和密码,后端做验证,验证成功之后,把用户的信息保存起来,保存在cookie中,下次再访问页面的时候,浏览器会自动把cookie信息提交到后端,后在做验证。

判断用户是否登录:判断cookie值是否存在,存在就登录,不存在就没有登录

复制代码
def home(request):
    '''访问这个home页面,必须登录之后才能范围,否则不让访问?'''
    # 判断用户是否登录了?
    # 就是判断是否有cookie
    # print(request.COOKIES.get('username'))
    # if request.COOKIES.get('username'):
    #     return HttpResponse("登录之后才能看到我哦")
    # else:
    #     return redirect('/login/')
    return HttpResponse("登录之后才能看到我哦")
复制代码

以登录功能为例:

 认证登录装饰器:

复制代码
 set_cookie('key', 'value', max_age=5,expires=5)   # 设置cookie

#参数:
#● key, 键
#● value=’’, 值
#● max_age=None,:
#    超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None ,这个cookie会延续到浏览器关闭为止
#expires=None:
#    超时时间(IE requires expires, so set it if hasn’t been already.)path=’/‘, Cookie生效的路径,/ 表示根路径
复制代码

baidu.com------------->一级域名-------------->解析出来很多个二级域名
www.baidu.com www1.baidu.com www2.baidu.com ly.baidu.com

买了服务器,有了IP:

127.0.0.1/index/-------->hello.com----->域名解析

127.0.0.1  hello.com-----》DNS解析-----》127.0.0.1

参数:

  secure=False, 浏览器将通过HTTPS来回传cookie

  httponly=False 只能http协议传输,无法被JavaScript获取

获取cookie和设置cookie也可以通过js实现

清空cookie:

obj.delete_cookie('username')# 使用场景:退出登录(注销功能)

Django操作session

session的数据是保存在后端的,保存在后端的载体也有很多种,比如:可以把数据保存在数据库、文件、Redis等

Django的默认保存位置在数据库中,在django_session表中,这张表是默认生成的

设置session:

# 设置session
def set_session(request):
    request.session['username'] = 'hua'
    return HttpResponse('set_session')

 sessionkey其实是一个随机字符串,session的默认过期时间是14天

当设置多个session值的时候,session_key是不变的,变的是session_Data,django_Session表中只存在一条记录(一台电脑的一个浏览器) ,优点:节省了MySQL的空间。当用另一个浏览器登录的时候才会在多一条记录。

# 设置session
def set_session(request):
    request.session['username'] = 'hua'
    request.session['age'] = '18'
    return HttpResponse('set_session')

后端:django_session表

前端页面:

设置session发生的事情:

  1. 后端会生成一个随机字符串session_key

  2. 会把用户设置的信息保存在django_session表中,数据也做了加密处理

  3.把数据封装到了request.session中了

  4. Django后端把随机字符串保存到浏览器中

  5. 随机字符串保存在浏览器的key = sessionid(默认命名)

 获取session

print(request.session.get('key'))

# 获取session
def get_session(request):
    print(request.session.get('username')) # hua
    print(request.session.get('age')) # 18
    return HttpResponse('get_session')

后端:

 前端:

 

获取session发生的事:

  1. 浏览器会自动把sessionid回传到Django后端

  2. Django后端获取到sessionid,然后去数据表根据session_key查询

    查询到了,说明之前已经登录过;查不到,就返回None

  3. 查询出来的数据默认是加密的,Django后端把数据解密之后封装到request.session中

    在取session值的时候,就从request.session中取

清空session:

# 清空session
def del_session(request):
    request.session.delete()#清空session,只删服务端数据,浏览器的数据还是保留的
    request.session.flush() #  把服务端和浏览器的都删掉了
    return HttpResponse('del_session')

 

后端:表中的数据清空了

 前端:

前端:当使用flush删除,浏览器的数据也会被清理掉

 

设置过期时间:

request.session.set_expiry(秒数)

session的默认过期时间是14天

 session的相关参数:

复制代码
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']


# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 会话session的key
request.session.session_key

# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()

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

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

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

Django中的Session配置

复制代码
1. 数据库Session,把session存在django_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存在文件中
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'   # 引擎

其他公用设置项:# session保存到cookie的相关操作
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添加装饰器

如图直接加装饰器:

报错: FBV装饰器是可以那样加的

 CBV加装饰器需要先导入一个类:from django.utils.decorators import method_decorator

方式一:

 

复制代码
#装饰器:
def login_auth(func):
    def inner(request, *args, **kwargs):
        if request.COOKIES.get('username'):
            return func(request, *args, **kwargs)
        else:
            return redirect('/login/')  # /login/是路由
    return inner

from django.views import View
from django.utils.decorators import method_decorator
class Login(View):
    @method_decorator(login_auth)
    def get(self, request):  # get请求方式
        return HttpResponse('get')
   @method_decorator(login_auth)
    def post(self, request):  # post请求方式
        return HttpResponse('post')
复制代码

 结果:

 方式二:

复制代码
# CBV
from django.views import View
from django.utils.decorators import method_decorator

# 方式二:
@method_decorator(login_auth,name='get') #给get方法加装饰器 ,name后面跟的是方法的名字
@method_decorator(login_auth,name='post') #给post方法加装饰器
class Login(View):
    # 必须登录之后才能访问:get访问
    # @method_decorator(login_auth) 
    def get(self, request):  # get请求方式
        return HttpResponse('get')

    def post(self, request):  # post请求方式
        return HttpResponse('post')
复制代码

方式三:重写dispatch

复制代码
from django.views import View
from django.utils.decorators import method_decorator
class Login(View):
    @method_decorator(login_auth) # 此时name就不用加具体方法的名字,会自动给这个类里面所有的方法加装饰器
    def dispatch(self, request, *args, **kwargs):
        super(Login,self).dispatch(request, *args, **kwargs)

    def get(self, request):  # get请求方式
        return HttpResponse('get')

    def post(self, request):  # post请求方式
        return HttpResponse('post')
复制代码

 

中间件

中间件它的执行位置在Web服务网关接口之后,在路由匹配之前执行的

Django中自带的有七个中间件

复制代码
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',
]
复制代码

每一个中间件都有自己独立的功能,它也支持我们自己自定义中间件

只要是跟全局相关的都可以用中间件来实现

如何自定义中间件,可以先看Django自带的中间件的源码:

复制代码
class SecurityMiddleware(MiddlewareMixin):
    def process_request(self, request):
        pass
    def process_response(self, request, response):
        pass

class SessionMiddleware(MiddlewareMixin):
    def process_request(self, request):
        pass
     def process_response(self, request, response):
        psss

class CsrfViewMiddleware(MiddlewareMixin):
    def process_request(self, request):
        pass
    def process_response(self, request, response):
        pass
复制代码

中间件就是一个类,然后这个类都继承了MiddlewareMixin

类中的几个方法:

  process_request (掌握)

  process_response(掌握)

  process_view

  process_template

  process_exception

这几个方法并不是都要全写,而是,需要几个就写几个方法

自定义中间件步骤:

  1. 在项目名下或者任意的应用名下创建一个文件夹

  2. 在这个文件夹下面创建一个py文件

  3. 在该py文件中写一个自定义的类必须要继承MiddlewareMixin

  4. 写完之后紧接着一定要去配置文件中注册中间件

第一步:导入:
from django.utils.deprecation import MiddlewareMixin

第二步:自定义中间件

复制代码
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware1(MiddlewareMixin):
    def process_request(self,request):
        print('第一个自定义中间件:process_request')
    def process_response(self,request,response):#响应必须要有返回值,不然前端页面会报错
        print("第一个自定义中间件:process_response")
        return response

class MyMiddleware2(MiddlewareMixin):
    def process_request(self,request):
        print('第二个自定义中间件:process_request')
    def process_response(self,request,response):#必须要有返回值,不然前端页面会报错
        print("第二个自定义中间件:process_response")
        return response

class MyMiddleware3(MiddlewareMixin):
    def process_request(self,request):
        print('第三个自定义中间件:process_request')
    def process_response(self,request,response): #必须要有返回值,不然前端页面会报错
        print("第三个自定义中间件:process_response")
        return response
复制代码

第三步:在views中定义一个视图函数

def func(request):
    print('func')
    return HttpResponse('func')

 第四步:在settings.py的MIDDLEWARE里注册自己定义的中间件

 

结果:

 当做拦截处理时:

复制代码
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class MyMiddleware1(MiddlewareMixin):
    def process_request(self, request):
        print('第一个自定义中间件:process_request')
        # return HttpResponse('第一个自定义中间件:process_request') # 做了拦截,只会执行统计表的process_response
    def process_response(self, request, response):  # 响应必须要有返回值,不然前端页面会报错
        print("第一个自定义中间件:process_response")
        return response

class MyMiddleware2(MiddlewareMixin):
    def process_request(self, request):
        print('第二个自定义中间件:process_request')
        return HttpResponse('第二个自定义中间件:process_request')  # 做了拦截,只会执行统计表的process_response
    def process_response(self, request, response):  # 必须要有返回值,不然前端页面会报错
        print("第二个自定义中间件:process_response")
        return response

class MyMiddleware3(MiddlewareMixin):
    def process_request(self, request):
        print('第三个自定义中间件:process_request')
    def process_response(self, request, response):  # 必须要有返回值,不然前端页面会报错
        print("第三个自定义中间件:process_response")
        return response
复制代码

后端打印结果:

前端页面:

 

总结一下:

  1. 中间件的process_request方法是在执行视图函数之前执行的。
  2. 当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的。
  3. 不同中间件之间传递的request都是同一个对象

 

多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。

 crsf跨站请求

本质上是:解决form表单提交数据的问题,问题是如何识别表单是我的,而不是人家的

  在form表单中加了一个随机标识,每次提交表单的时候,后端先验证随机标识,如果这个随机表示对上了,说明这个表单就是我们自己的,如果对不上,直接forbidden

username
shenfenzheng
<input type='text' name='正规的'>

# 冒牌的
<input type='text' >

<input type='hidden' name='冒牌的'> #这个是隐藏看不到的,就会进入冒牌

如何避免:后端要做验证,提交过来的数据是不是我之前返回的表单

  1. 在form表单中加一个标签 {% csrf_token %}
  2. ajax提交的时候
    1.在传递的参数加一个key值:csrfmiddlewaretoken: '{{csrf_token}}'
    2.使用js文件来解决,去copyjs文件放在自己的文档中

通过后台验证随机串来确认提交过来的数据是否是之前返回的表单

 加了下面这句话后前端会出现一段随机串:

 ajax(第一和第二种方式):

 第三种方式:

在static文件夹下创建一个js文件

使用django官方提供的js文件

复制代码
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');


// 每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置。

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});
复制代码

导入js:

 csrf跨站请求的相关装饰器

 Django中有一个中间件对crsf跨站做了验证,只要把csrf的这个中间件打开,意味着所以的方法都要被验证(get请求不需要验证)

需要先导入这个:

from django.views.decorators.csrf import csrf_exempt,csrf_protect

在视图函数中:

  1.只有几个视图函数做验证(csrf中间件注释了,对需要做验证的函数加装饰器)

  2.只有几个视图函数做验证(csrf中间件没有注释,对不需要做验证的函数加装饰器)

csrf_protect: 哪个视图函数加了这个装饰器,这个函数就会做验证(当中间件被注释时,也会做验证)

from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect
def index(request):
    return render(request,'index.html')

当前端页面发起post请求时,需要做验证,结果如图:

 csrf_exempt:哪个视图函数加了这个装饰器,这个函数就不会做验证

# 先导入这个
from django.views.decorators.csrf import csrf_exempt,csrf_protect 
# 'django.middleware.csrf.CsrfViewMiddleware',没有注释:
@csrf_exempt
def func(request):
    print('func不需要验证')
    return render(request,'func.html')

 

前端页面:当提交post请求时不需要再做验证,即使中间件没有被注释

 后端结果:

 通过访问func来跳转到login页面

复制代码
@csrf_protect
def func(request):
    print('func不需要验证')
    return render(request,'func.html')

#CBV
from django.views import View
class Login(View):
    def post(self):
        print('post')
        return HttpResponse('post')
复制代码

html:

结果:会对post请求方式做验证

 

"""

CBV针对于csrf_exempt:只有第三张方式才生效(重写dispatch),其余两种方式不行

CBV针对于csrf_protect:三种方式都可以

"""

1. 对post不做验证:csrf_exempt

方式一:

复制代码
def func(request):
    print('func不需要验证')
    return render(request,'func.html')

#CBV
from django.views import View
from django.utils.decorators import method_decorator
class Login(View):
    # 不让post做验证
    @method_decorator(csrf_exempt) # 方式一
    def post(self,requset):
        print('post')
        return HttpResponse('post')
复制代码

结果:

 方式二:

复制代码
def func(request):
    print('func不需要验证')
    return render(request,'func.html')

#CBV
from django.views import View
from django.utils.decorators import method_decorator
# # 方式二:
@method_decorator(csrf_exempt,name='post')
class Login(View):
    # 不让post做验证
    def post(self,requset):
        print('post')
        return HttpResponse('post')
复制代码

结果:

 方式三:

复制代码
def func(request):
    print('func不需要验证')
    return render(request, 'func.html')
from django.views import View
from django.utils.decorators import method_decorator
# 方式三:
class Login(View):
    # 不让post做验证
    @method_decorator(csrf_exempt) #第三中方式,重写dispatch
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, requset):
        print('post')
        return HttpResponse('post')
复制代码

路由:

 

结果:

 2.对post做验证

方式一:

复制代码
def func(request):
    print('func不需要验证')
    return render(request, 'func.html')

# CBV
from django.views import View
from django.utils.decorators import method_decorator
class Login(View):
    # 让post做验证
    @method_decorator(csrf_protect)
    def post(self,requset):
        print('post')
        return HttpResponse('post')
复制代码

 

方式二:

复制代码
def func(request):
    print('func不需要验证')
    return render(request, 'func.html')


# CBV
from django.views import View
from django.utils.decorators import method_decorator
# 方式二:
@method_decorator(csrf_protect,name='post')
class Login(View):
    # 让post做验证
    def post(self,requset):
        print('post')
        return HttpResponse('post')
复制代码

 

方式三:

复制代码
from django.views import View
from django.utils.decorators import method_decorator
# 方式三:
class Login(View):
    # 让post做验证
    @method_decorator(csrf_protect)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, requset):
        print('post')
        return HttpResponse('post')
复制代码

 

以上三种方式都可行,结果如图:

 

posted @   Maverick-Lucky  阅读(189)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
点击右上角即可分享
微信分享提示