Django框架进阶6 多对多三种创建方式, Ajax, Content-Type前后端传输数据编码格式, Ajax发送文件数据, django内置的序列化功能, Ajax结合sweetalert实现删除二次确认, 批量插入数据, 自定义分页器, ajax结合sweetalert实现删除二次确认, getJSON

多对多三种创建方式

1.全自动(较为常用)

    class Book(models.Model):
        title = models.CharField(max_length=32)
        authors = models.ManyToManyField(to='Author')
        # orm就会自动帮你创建第三张表
        
    class Author(models.Model):
        name = models.CharField(max_length=32)
        
    '''
    好处:第三张表自己创建
    不足之处:第三张表无法扩展额外的字段
    '''

2.纯手动(了解)

    class Book(models.Model):
        title = 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')
        create_time = models.DateField(auto_now_add=True)
    
        """
        好处在于第三表可以扩展额外的字段
        不足之处:orm查询的时候会带来不便
        """            

3.半自动(推荐)

    class Book(models.Model):
        title = 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)
        books = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('author', 'book'))


    class Book2Author(models.Model):
        book = models.ForeignKey(to='Book')
        author = models.ForeignKey(to='Author')
        create_time = models.DateField(auto_now_add=True)


    """
    好处在于第三步可以扩展任意的额外字段 还可以利用orm 正反向查询
    不足之处:无法利用
        add
        set
        remove
        clear
    虽然无法使用了 但是你还可以自己直接操作第三表
    """

 

Ajax(异步提交、局部刷新)

同步与异步

同步  任务提交之后原地等待任务的返回结果  进程表现上来说  阻塞

异步  任务提交之后不需要原地等待返回结果  直接执行下一行代码  继承表现上来说  非阻塞任务的结果肯定是需要的  是通过异步回调机制  callback()

 

与后端进行交互的方式

1.浏览器窗口输入url回车    GET

2.a标签href属性填写url点击     GET

3.form表单          GET/POST

4.Ajax            GET/POST

 

Ajax并不是一门新的语言  它其实就是基于js写的一个功能模块而已

由于原生js书写ajax较为繁琐  所以我们直接学jQuery封装好的ajax模块操作

 

初识ajax

案例:页面上有三个input框  一个按钮

用户在前两个框中输入数字  点击按钮保证页面不刷新的情况下将数据发到后端做计算

将计算好的结果再发给前端展示到第三个input框中

ajax基本语法结构

// ajax基本语法
$.ajax({
    // 1.到底朝哪个后端提交数据
    url:'',  // 控制数据的提交路径  有三种写法 跟form表单的action属性一致
    // 2.指定当前请求方式
    type:'post',
   // 3.请求头设置
   headers:{'Authorization':'JWT XXXX'} // 4.提交的数据 data:{'i1':$('#i1').val(),'i2':$('#i2').val()}, // 5.ajax是异步提交 所以需要给一个回调函数来处理返回的结果 success:function (data) { // data就是异步提交的返回结果 // 将异步回调的结果通过DOM操作渲染到第三个input框中 $('#i3').val(data) } })

例:

templates/index.html

<!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>
<input type="text" id="i1"> + <input type="text" id="i2"> = <input type="text" id="i3">
<button id="d1">按钮</button>

<script>
    $('#d1').click(function () {
        // 获取两个框里面内容  朝后端提交异步请求
        // ajax基本语法
        $.ajax({
            // 1.到底朝那个后端提交数据
            url:'', // 控制数据的提交路径  有三种写法  跟form表单的action属性一样
            // 2.指定当前请求方式
            type:'post',
            // 3.提交的数据
            data:{'i1':$('#i1').val(),'i2':$('#i2').val()},
            // 4.ajax是异步提交  所以需要给一个回调函数来处理返回的结果
            success:function (data) {   // data就是异步提交的返回结果
                // 将异步回调的结果通过DOM操作渲染到第三个input框中
                $('#i3').val(data)
            }
        })
    })
</script>
</body>
</html>

app01/views.py

from django.shortcuts import render,HttpResponse

def index(request):
    if request.method == 'POST':
        i1 = request.POST.get('i1')
        i2 = request.POST.get('i2')
        # i1 和 i2 是字符串类型  需要先做类型转换
        i3 = int(i1) + int(i2)
        return HttpResponse(i3)
    return render(request,'index.html')

content-Type前后端传输数据的编码格式

  urlencoded
  formdata
  application/json

 

form表单

  默认是urlencoded编码格式传输数据

   urlencoded数据格式

      username=jason&password=123

      django后端针对该格式的数据  会自动解析并帮你打包到request.POST中

  formdata数据格式

      django后端针对符合urlencoded编码格式数据(普通键值对)还是统一解析到request.POST

      而针对formdata文件数据就会自动解析放到request.FILES

ajax提交

  ajax默认的也是urlencoded编码格式

    前后端数据交互   编码格式与数据格式一定要一致

  application/json

    django后端针对json格式数据  并不会做任何的处理

    因为没有人拿,数据就以二进制的形式存放在request.body中

$('#d2').on('click',function () {
    $.ajax({
            rl:'',
            type:'post',
            // 修改content-Type参数
            contentType:'application/json',
            data:JSON.stringify({'username':'jason','password':123}),  // 将数据序列化成json格式字符串
            success:function (data) {
            alert(data)
            }
       })
})

例:

templates/ab_ct.html

<button id="d2">ajax发送json格式数据</button>
<script>
    $('#d2').on('click',function () {
        $.ajax({
            url:'',
            type:'post',
            // 修改content-Type参数
            contentType:'application/json',
            data:JSON.stringify({'username':'jason','password':123}),   // 将数据序列化成JSON格式字符串
            success:function (data) {
                alert(data)
            }
        })
    })

</script>
View Code

app01/views.py

from django.shortcuts import render,HttpResponse
import json

def ab_ct(request):
    if request.method == 'POST':
        # print(request.POST)
        # print(request.FILES)
        # print(request.body)
        #自己处理json格式数据
        json_bytes = request.body
        # 先解码
        # json_str = json_bytes.decode('utf8')

        # 扩展 json.loads能够自动解码并序列化
        json_dict = json.loads(json_bytes)
        print(json_dict,type(json_dict))

    return render(request,'ab_ct.html')

 

ajax中dataType属性:

# 前端
$.ajax({ url:
'', # 控制后端提交路径 type:'', # 控制请求方式 data:{}, # 控制提交的数据 dataType:"JSON", # 有此句,django后端用HttpResponse返回json格式字符串,args会自动反序列化,拿到的是前端js的对象类型,而如果是用JsonResponse返回的那么args会自动返序列化成前端js的对象类型(如果此行不写,django后端用HttpResponse返回json格式字符串,args不会自动反序列化,拿到的还是json格式字符串string字符类型.如果是用JsonResponse返回,返回还是前端js的对象类型) success:function(args){ # 异步回调机制 }) # 后端 def index(request): if request.method == 'POST': back_dic = {'msg':'hahaha'} return HttpResponse(json.dumps(back_dic)) # 需要加dataType:"JSON" return JsonResponse(back_dic) # 不需要 return render(request,'index.html') # 后续在写ajax请求的时候建议你加上dataType参数

 

ajax发送文件(******)

内置对象FormData

即发普通键值对也发文件

<input type="file" name="myfile" id="i1">
<button id="d3">ajax发送文件数据</button>
<script>
     $('#d3').click(function () {
         // 1 需要先生成一个内置对象
            var myFormData = new FormData();
            // 2 传普通键值对  当普通键值对较多的时候 我们可以利用for循环来添加
            myFormData.append('username','jason');
            myFormData.append('password',123);
            // 3 传文件
            myFormData.append('myfile',$('#i1')[0].files[0]);  // 获取input框内部用户上传的文件对象
            // 发送ajax请求
            $.ajax({
                url:'',
                type:'post',
                data:myFormData,
                // 发送formdata对象需要指定两个关键性的参数

                processData:false,  // 让浏览器不要对你的数据进行任何的操作
                contentType:false,  // 不要使用任何编码格式 对象formdata自带编码格式并且django能够识别该对象

                success:function (data) {
                    alert(data)
                }
            })
        })
</script>

 

序列化

django内置序列化组件

  接口文档 表名该大字典内都有哪些键值对

from django.shortcuts import render,HttpResponse,redirect
import json

# Create your views here.
from app01 import models
from django.core import serializers
def ab_se(request):
    user_queryset = models.Userinfo.objects.all()
    # user_list = []
    # for user_obj in user_queryset:
    #     user_list.append({
    #         'username':user_obj.username,
    #         'password':user_obj.password,
    #         'gender':user_obj.get_gender_display(),
    #                       })
    # res = json.dumps(user_list)
    res = serializers.serialize('json',user_queryset)   # 注意此处json为字符串
    # return render(request,'ab_se.html',locals())
    return HttpResponse(res)

注意:在 http://www.bejson.com/ 中可以在线JSON格式化校验

其结果为

 1 [{
 2     "model": "app01.userinfo",
 3     "pk": 1,
 4     "fields": {
 5         "username": "jason",
 6         "password": "123",
 7         "gender": 1
 8     }
 9 }, {
10     "model": "app01.userinfo",
11     "pk": 2,
12     "fields": {
13         "username": "tank",
14         "password": "234",
15         "gender": 2
16     }
17 }, {
18     "model": "app01.userinfo",
19     "pk": 3,
20     "fields": {
21         "username": "egon",
22         "password": "456",
23         "gender": 3
24     }
25 }, {
26     "model": "app01.userinfo",
27     "pk": 4,
28     "fields": {
29         "username": "sean",
30         "password": "678",
31         "gender": 4
32     }
33 }]
View Code

 

 批量插入

def ab_bc(request):
    # 先插入1000条数据
    # for i in range(1,1001):
    #     models.Book.objects.create(title='第%s本书'%i)
    book_list =[]
    for i in range(1,10001):
        book_list.append(models.Book(title='新的%s书'%i))
    models.Book.objects.bulk_create(book_list)  # 批量插入数据的方式

    book_queryset = models.Book.objects.all()
    return render(request,'ab_bc.html',locals())

 

自定义分页器

分页器组件

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:  最多显示的页码个数
        
        用法:
        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)

后端代码

current_page = request.GET.get('page', 1)    # 当前页数
all_count = book_queryset.count()     # 数据总条数
# 1 现生成一个自定义分页器类对象
page_obj = Pagination(current_page=current_page,all_count=all_count,pager_count=9)
# 2 针对真实的queryset数据进行切片操作
page_queryset = book_queryset[page_obj.start:page_obj.end]
return render(request,'ab_bc.html',locals())

前端代码

{% for book_obj in page_queryset %}
    <p>{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}

 

ajax结合sweetalert实现删除二次确认

弹窗组件参考:https://lipis.github.io/bootstrap-sweetalert/ 

代码:

views.py


from app01 import models
from django.http import JsonResponse
import time
def show_user(request):
    '''
    前后端如果是通过ajax进行交互  那么交互的媒介一般情况下都是一个字典
    :param request:
    :return:
    '''
    if request.method == 'POST':
        time.sleep(3)   # 模拟延迟3秒处理再删除
        back_dic = {'code':1000,'msg':''}
        delete_id = request.POST.get('delete_id')
        models.Userinfo.objects.filter(pk=delete_id).delete()
        back_dic['msg'] = '删除成功, 准备跑路!!!'
        return JsonResponse(back_dic)
    user_queryset = models.Userinfo.objects.all()
    return render(request,'show_user.html',locals())

models.py

from django.db import models

class Userinfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    gender_choices = ((1,'male'),(2,'female'),(3,'other'))
    gender = models.IntegerField(choices=gender_choices)

templates/show_user.html

<!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>
    {% load static %}
    <link rel="stylesheet" href="{% static 'dist/sweetalert.css' %}">
    <script src="{% static 'dist/sweetalert.min.js' %}"></script>
    <style>
        div.sweet-alert h2{
            padding-top: 10px;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h2 class="text-center">数据展示</h2>
            <table class="table-hover table table-stiped">
                <thead>
                    <tr>
                        <th>主键</th>
                        <th>用户名</th>
                        <th>密码</th>
                        <th>性别</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    {% for user_obj in user_queryset %}
                        <tr>
                            <td>{{ user_obj.pk }}</td>
                            <td>{{ user_obj.username }}</td>
                            <td>{{ user_obj.password }}</td>
                            <td>{{ user_obj.get_gender_display }}</td>
                            <td>
                                <a href="#" class="btn btn-primary btn-xs">编辑</a>
                                <a href="#" class="btn btn-danger btn-xs cancel" data_id = '{{ user_obj.pk }}' >删除</a>
                            </td>
                        </tr>
                    {% endfor %}
                    
                </tbody>
            </table>
        </div>
    </div>
</div>
<script>
    $('.cancel').click(function () {
        var $aEle = $(this);    {#  this 指代当前点击对象  #}
        swal({
              title: "你确定要删嘛?",
              text: "你如果删了,你就要准备跑路了",
              type: "warning",
              showCancelButton: true,
              confirmButtonClass: "btn-danger",
              confirmButtonText: "是,老子就是要删!",
              cancelButtonText: "惹不起惹不起!",
              closeOnConfirm: false,
              closeOnCancel: false,
                showLoaderOnConfirm: true   // 确认延迟参数
        },
        function(isConfirm) {
          if (isConfirm) {
              // 发送ajax请求
            $.ajax({
                url:'',
                type:'post',
                data:{'delete_id':$aEle.attr('data_id')},
                success:function (data) {   // 回调函数会自动将二进制的json格式数据 解码并反序列成js中的数据类型
                    if (data.code == 1000){
                        swal("删了!", "你可以跑路了.", "success");
                        // 方式一 页面刷新
                        {#window.location.reload()    #}
                        // 方式二 DOM操作动态修改
                        $aEle.parent().parent().remove()
                    }else{
                        swal('发生了未知的错误','error')    // 将标签直接移除
                    }
                }
            });
          } else {
            swal("怂逼", "你成功的刷新我对你的认知 :)", "error");
          }
        });
    })
</script>

</body>
</html>

 

getJSON

jQuery前端使用AJAX请求获得JSON数据,使用简洁,但是功能有限

// 向click_url发起get请求,返回json格式的data数据
$.getJSON(click_url, function (data) {
alert(data);
}

经测试,加入下方代码,可以让所有ajax方法和getJSON方法请求头变为设置的请求头

    $.ajaxSetup({   // 对应getJSON追加请求头
        headers: {
            'Authorization': 'Basic XXX',
        }
    });

 

posted @ 2020-01-13 01:07  战斗小人  阅读(266)  评论(0编辑  收藏  举报