zoukankan      html  css  js  c++  java
  • Django之ORM多对多表创建方式,AJAX异步提交,分页器组件等

    MTV与MVC

    MTV模型:

    ​ M:模型层(models.py),负责业务对象和数据库关系的映射(ORM)

    ​ T:模板层(Template),负责如何把页面展示给用户(HTML)

    ​ V:视图层(views.py),负责业务逻辑,并在适当的时候调用Model和Template

    MVC模型:

    ​ Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求。

    ​ M:模型层(models.py)

    ​ V:视图层(views.py)

    ​ C:控制层(Controller) urls.py

    多对多表的三种创建方法

    第一种自动创建第三张表

    # 多对多表三种创建方式
    	# 1.第一种 django orm自动帮我们创建
    		class Book(models.Model):
    		name = models.CharField(max_length=32)
    		authors = models.ManyToManyField(to='Author')
    
    
    		class Author(models.Model):
    			name = models.CharField(max_length=32)
    	
    	# 2.第二种纯手动创建第三张表
    	
    		class Book(models.Model):
    			name = models.CharField(max_length=32)
    
    		class Author(models.Model):
    			name = models.CharField(max_length=32)
    
    		class Book2Author(models.Model):
    			book = models.ForeignKey(to='Book')
    			author = models.ForeignKey(to='Author')
    			info = models.CharField(max_length=32)
    	
    	# 3.第三种半自动创建第三张表(可扩展性高,并且能够符合orm查询)
    		class Book(models.Model):
    			name = models.CharField(max_length=32)
    			# 第三种创建表的方式
    			authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
    
    		class Author(models.Model):
    			name = models.CharField(max_length=32)
    			# book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book'))
    
    
    		class Book2Author(models.Model):
    			book = models.ForeignKey(to='Book')
    			author = models.ForeignKey(to='Author')
    			info = models.CharField(max_length=32)
    
    前后端数据传输的编码格式contentType
    **u**rlencoded**
    	**对应的数据格式:name=jason&password=666**
    	**后端获取数据:request.POST**
    	**ps;django会将urlencoded编码的数据解析自动放到request.POST**
    **formdata**
    	**form表单传输文件的编码格式**
    	**后端获取文件格式数据:request.FILES**
    	**后端获取普通键值对数据:request.POST**
    **application/json**
    	**ajax发送json格式数据**
    	**需要注意的点**
    		编码与数据格式要一致**
    

    AJAX准备知识:JSON

    什么是JSON?

    • JSON指的是JavaScript对象表示方式(JavaScript Object Notation)
    • JSON是轻量级的文本数据交换格式
    • JSON独立与语言
    • JSON具有自我描述性,更易于理解
    • JSON使用JavaScript 语法来描述数据对象,但是JSON任然独立于愈合和平台,JSON解释器和JSON库支持许多不同的编程语言。

    合格的json对象(json只认识双引号字符串格式)

    ["one", "two", "three"]
    { "one": 1, "two": 2, "three": 3 }
    {"names": ["张三", "李四"] }
    [ { "name": "张三"}, {"name": "李四"} ] 
    

    不合格的json对象:

    { name: "张三", 'age': 32 }  // 属性名必须使用双引号
    [32, 64, 128, 0xFFF] // 不能使用十六进制值
    { "name": "张三", "age": undefined }  // 不能使用undefined
    { "name": "张三",
      "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
      "getName":  function() {return this.name;}  // 不能使用函数和日期对象
    }
    

    stringify 与parse 方法

    JavaScript 中关于JSON对象和字符串转换的两个方法:

    JSON.parse(): 用于将一个JSON字符串转换为JavaScript对象

    JSON.parse('{"name":"Howker"}');
    JSON.parse('{name:"Stack"}') ;   // 错误
    JSON.parse('[18,undefined]') ;   // 错误
    

    JSON.stringify():用于将JavaScript值转换为JSON字符串。

    JSON.stringify({"name":"Tonny"})
    

    AJAX

    ​ AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。

    ​ AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

    ​ AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

    ​ AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

    同步异步概念:

    • 同步提交:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
    • 异步提交:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。

    特点:

    1. 异步提交
    2. 局部刷新
    前端发送请求到后端的方法 请求方法
    浏览器窗口手动输入网址 get请求
    a标签的href属性 get(默认)/post请求
    form表单 get(默认)/post请求
    ajax get/post请求

    ajax基本语法

    ​ 提交的地址

    ​ 提交的方式

    ​ 提交的数据

    ​ 回调函数

    示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    <input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3">
    <button id="b1">Ajax Test</button>
    
    <script src="/static/jquery-3.3.1.min.js"></script>
    <script>
        $('#b1').click(function () {
            $.ajax({
                url:'',
                type:'POST',
                data:{i1:$('#i1').val(),i2:$('#i2').val()},
                success:function (data) {
                    $('#i3').val(data)
                }
            })
        })
    
    </script>
    </body>
    </html>
    
    # views.py
    def ajax_test(request):
        if request.method=='POST':
            i1=request.POST.get('i1')
            i2=request.POST.get('i2')
            ret=int(i1)+int(i2)
            return HttpResponse(ret)
        return render(request,'ajax_test.html')
    
    # url.py
    from django.conf.urls import url
    from app01 import views
    urlpatterns=[
        url(r'^ajax_test/',views.ajax_test),
    ]
    

    AJAX常见应用场景:

    搜索引擎根据用户输入的关键字,自动提示检索关键字。

    还有一个很重要的应用场景就是注册时候的用户名的查重。

    其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。

    • 整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
    • 当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!

    //ajax默认传输数据的编码格式也是urlencoded
    
    $('#d1').click({
        $.ajax({
        //提交的地址
        url:'/index',  // url可以不写,默认就是往当前页面打开的地址提交请求
        //提交方式
        type:'post',
        //提交的数据
        data:{'name':'qzk','password':'123'},  //数据一般会在前端页面中铜鼓选择器去筛选出来
        //回调函数
        success:functino(data){
        alert(data)
    }
    })
    })
    
    $('#d1').click({
        $.ajax({
        //提交的地址
        url:'',  // url可以不写,默认就是往当前页面打开的地址提交请求
        //提交方式
        type:'post',
        //提交的数据
        data:{
    	'i1':$('#i1').val(),
        'i2':$('#i2').val()
    },  //数据一般会在前端页面中铜鼓选择器去筛选出来
        //回调函数
        success:functino(data){
        $('#i3').val(data)
    }
    })
    })
    
    def index(request):
        if request.method=='POST':
            i1=request.POST.get("i1")
            i2=request.POST.get("i2")
            res=int(i1)+int(i2)
            return...
    

    AJAX的优缺点:

    优点:

    • AJAX使用JavaScript 技术想服务器发送异步请求;
    • AJAX请求无需刷新整个页面
    • 因为服务器响应内容不再是整个页面,,而是页面中的部分内容,所以AJAX性能高;
    • 两个关键点:1.局部刷新,2.异步请求

    最基本的jQuery发送ajax请求示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    
        <style>
            .hide {
                display: none;
            }
        </style>
    </head>
    <body>
    <p><input type="text" class="user"><span class="hide" style="color: red">用户名已存在</span></p>
    
    <script src="/static/jquery-3.3.1.min.js"></script>
    {#下面这一项是基于jQuery的基础上自动给我们的每一个ajax绑定一个请求头信息,类似于form表单提交post数据必须要有的csrf_token一样#}
    {#否则我的Django中间件里面的校验csrf_token那一项会认为你这个请求不是合法的,阻止你的请求#}
    <script src="/static/setup_Ajax.js"></script>
    <script>
        //给input框绑定一个失去焦点的事件
        $('.user').blur(function () {
            //$.ajax为固定用法,表示启用ajax
            $.ajax({
                //url后面跟的是你这个ajax提交数据的路径,向谁提交,不写就是向当前路径提交
                url:'',
                //type为标定你这个ajax请求的方法
                type:'POST',
                //data后面跟的就是你提交给后端的数据
                data:{'username':$(this).val()},
                //success为回调函数,参数data即后端给你返回的数据
                success:function (data) {
                    ret=JSON.parse(data);// 将一个json格式的字符串转成JavaScript对象
                    if (ret['flag']){
                        $('p>span').removeClass('hide');
                    }
                }
            })
        });
    </script>
    </body>
    </html>
    
    # views.py
    def index(request):
        if request.method=='POST':
            ret={'flag':False}
            username=request.POST.get('username')
            if username=='JBY':
                ret['flag']=True
                import json
                return HttpResponse(json.dumps(ret))
        return render(request,'index.html')
    

    示例二:发送文件格式数据(需要借助于内置对象 Formdata)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
        <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>
    </head>
    <body>
    <input type="file" name="myfile" id="i1">
    <button id="d1">加我加我~</button>
    <script>
        $('#d1').click(function () {  // 绑定一个点击事件
           let formdata = new FormData();
           // FormData对象不仅仅可以传文件还可以传普通的键值对
            formdata.append('name','jason');
            // 获取input框存放的文件
            //$('#i1')[0].files[0]
            formdata.append('myfile',$('#i1')[0].files[0]);
            $.ajax({
                url:'',
                type:'post',
                data:formdata,
                // ajax发送文件需要修改两个固定的参数
                processData:false,  // 告诉浏览器不要处理我的数据
                contentType:false,  // 不要用任何的编码,就用我formdata自带的编码格式,django能够自动识别改formdata对象
                // 回调函数
                success:function (data) {
                    alert(data)
                }
            })
        });
        // ajax传输文件需要借助于内置对象FormData
    
    </script>
    </body>
    </html>
    

    AJAX发送json格式的数据(contentType:'application/json')

    $('#d1').click(function () {
           $.ajax({
               url:'',  // url参数可以不写,默认就是当前页面打开的地址
               type:'post',
               contentType:'application/json',
               data:JSON.stringify({'name':'jason','hobby':'study'}),
               success:function (data) {
                   alert(data)
                   $('#i3').val(data)
               }
           });
    
    

    JS实现AJAX(了解)

    var b2 = document.getElementById("b2");
      b2.onclick = function () {
        // 原生JS
        var xmlHttp = new XMLHttpRequest();
        xmlHttp.open("POST", "/ajax_test/", true);
        xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlHttp.send("username=q1mi&password=123456");
        xmlHttp.onreadystatechange = function () {
          if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
            alert(xmlHttp.responseText);
          }
        };
      };
    

    AJAX请求如何设置csrf

    ​ 不论是ajax还是谁,只要是向我Django提交post请求的数据,都必须校验csrf_token来防伪跨站请求,那么如何在我的ajax中弄这个csrf_token呢,我又不像form表单那样可以在表单内部通过一句{% csrf_token %}就搞定了......

    方式一

    ​ 通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送。

    $.ajax({
      url: "/cookie_ajax/",
      type: "POST",
      data: {
        "username": "Tonny",
        "password": 123456,
        "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中
      },
      success: function (data) {
        console.log(data);
      }
    })
    

    方法二:

    ​ 通过获取返回的cookie中的字符串 放置在请求头中发送。

    ​ 注意:需要引入一个jquery.cookie.js插件。

    $.ajax({
      url: "/cookie_ajax/",
      type: "POST",
      headers: {"X-CSRFToken": $.cookie('csrftoken')},  // 从Cookie取csrf_token,并设置ajax请求头
      data: {"username": "Q1mi", "password": 123456},
      success: function (data) {
        console.log(data);
      }
    })
    

    方式三:

    ​ 或者用自己写一个getCookie方法:

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

    将下面的文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)

    批量插入数据

    """自己推到的过程"""
    
    def booklist(request):
        # 动态插入100条数据
        for i in range(100):
            models.Book2.objects.create(name='第%s本书'%i)
        l = []
        for i in range(10000):
            l.append(models.Book2(name='第%s本书'%i))
        models.Book2.objects.bulk_create(l)
        # 查询所有的书籍展示到前端页面
        """对批量数据进行分页显示(后期直接用模块的导入)"""
        # 数据的总条数
        all_count = models.Book2.objects.all().count()
        # 要访问的当前页
        current_page = request.GET.get('page', 1)  # 用户不传默认展示第一页
        current_page = int(current_page)
        # 每页展示多少条数据
        per_page_num = 10
    
        # 获取总页数
        pager_nums,more = divmod(all_count,per_page_num)
        if more:
            pager_nums += 1
    
        html = ''
        for i in range(1,pager_nums+1):
            html += '<li><a href="?page=%s">%s</a></li>'%(i,i)
        # 起始位置
        page_start = (current_page-1)*per_page_num
        # 终止位置
        page_end = current_page*per_page_num
    
        book_list = models.Book2.objects.all()[page_start:page_end]
    
        return render(request,'booklist.html',locals())
    
    

    分页使用

    class Pagination(object):
        def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param pager_count:  最多显示的页码个数
    
            用法:
            queryset = model.objects.all()
            page_obj = Pagination(current_page,all_count)
            page_data = queryset[page_obj.start:page_obj.end]
            获取数据用page_data而不再使用原始的queryset
            获取前端分页样式用page_obj.page_html
            """
            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)
    
    # views.py
    def booklist(request):
        book_list = models.Book2.objects.all()
        all_count = book_list.count()
        current_page = request.GET.get('page',1)
        page_obj = my_page.Pagination(current_page=current_page,all_count=all_count)
        page_queryset = book_list[page_obj.start:page_obj.end]
        return render(request,'booklist.html',locals())
    # 然后前端再对传过去的数据进行渲染
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
       <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <table class="table table-hover table-bordered table-striped">
                    <thead>
                        <tr>
                            <th>id</th>
                            <th>name</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for book in page_queryset %}
                        <tr>
                            <td>{{ book.pk }}</td>
                            <td>{{ book.name }}</td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
                {{ page_obj.page_html|safe }}  
            </div>
        </div>
    </div>
    </body>
    </html>
    
  • 相关阅读:
    Java基础知识(一)环境变量的设置、变量与常量、自动类型转换、自增长、赋值运算符
    ModelState
    DOM
    正则表达式常见形式
    通过JS设置一个元素的文本
    JS(三) 原型对象与原型链
    JS(二)call方法和apply方法
    第四周学习进度表
    敏捷开发方法综述
    数组02
  • 原文地址:https://www.cnblogs.com/qianzhengkai/p/11024356.html
Copyright © 2011-2022 走看看