Django框架之多对多表关系创建与Ajax

Django框架之多对多表关系创建与Ajax

1、表关系创建多对多三种创建方式

1、全自动(较为常用)

Django ORM会自动帮你创建第三张表只需定义两张表类使用多对多外键连接

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author')  # 会自动帮你创建第三张表
class Author(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

好处:第三张表自动创建

不足:第三张表无法扩展额外的字段

2、存手动

第三张表自己手动创建,外键连接两张表,可以扩展额外的字段,但是不能用ORM查询语句,无正向反向的概念了

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
​
class Author(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
​
class Book_Author(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):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    # 多对多的外键字段需要再指定两个关键字参数through='第三张表',through_fields = ('本表名','另一种关系表名')
    authors = models.ManyToManyField(to="Author", through="Book_Author", through_fields=('book', 'author'))class Author(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    # 多对多的外键字段需要再指定两个关键字参数through='第三张表',through_fields = ('本表名','另一种关系表名')
   # books = models.ManyToManyField(to="Book", through='Book_Author', through_fields=('author', 'book')) # 不需要两张表同时写

class Book_Author(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方法对第三张表操作

2、Ajax

1、Ajax简介

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

异步提交,局部刷新(最大的优点):不加载整个页面的情况下,可以跟服务器交换数据并更新部分网页内容,在不知不觉的过程中完成了请求响应过程

应用:在用户输入关键字的时候,会自动去数据库中进行查询和进行校验操作,最后返回出结果,整个过程页面没有刷新,只是刷新局部的位置

2、Ajax基本语法结构(重点)

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

3、Content-Type前后端传输数据编码格式

1、三种数据编码格式:

  1. application/urlencoded

    前端代码以 username=jason&password=123格式展示

    Django后端会针对该种格式的数据会自动解析打包到request.POST

  2. multipart/form-data

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

    后端会针对formdata数据格式的文件数据会自动解析到request.FILES

  3. application/json

    前后的数据交互编码格式一定要一样,是json格式的发送要进行转码

    Django后端针对json格式数据,不会做任何的处理,而是放进request.body中需要自己手动进行转换,先解码,再反序列化(json.loads能够自动解码并序列化

    json_bytes = request.body
    json_dict = json.loads(json_bytes)  # 拿到真实的字典数据

提交数据两种方式:form表单和Ajax

2、form表单

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

前端以username=Jason&password=123展示,Django后端会自动解析打包到request.POST中,而针对文件数据只能获取文件名,并不能真正获取文件数据

2、可以修改为formdata数据格式

指定enctype="multipart/form-data"

<form action="" method="post" enctype="multipart/form-data">

django后端针对符合urlencoded编码格式数据(普通键值对)还是统一解析到request.POST中,会针对formdata数据格式的文件数据会自动解析到request.FILES中

3、Ajax提交

Ajax默认也是urlencoded编码格式,也可以修改提交数据格式为json数据格式

4、Ajax发送json格式数据及发送文件

1、Ajax发送json数据格式数据

可以修改提交的数据格式为:contentType:'application/json'

    $('#d1').on('click', function () {
        $.ajax({
            url:'',
            type:'post',
            // 修改contentType参数
            contentType:'application/json',
            // 改成json格式的传输,需要将数据序列化成json格式
            data:JSON.stringify({'username':'tank','password':123}),
            success:function (data) {
                alert(data)
            }
        })
    })

后端需要自己处理数据,将拿到的数据解码再反序列化才能拿到真实字典数据

        # 自己处理json格式数据
        json_bytes = request.body
        # 先解码
        # json_str = json_bytes.decode('utf-8')
        # 再反序列化
        # json_dict = json.loads(json_str)
# 扩展 json.loads能够自动解码并序列化
        json_dict = json.loads(json_bytes)
        print(json_dict,type(json_dict))

2、Ajax发送文件

内置对象FormData:既可以发普通键值对数据也可以发文件数据

Ajax发送文件数据需要借助于内置对象

1、首先需要生成一个内置对象

var myFormData = new FormData

2、向生成的对象中添加数据

3、发送Ajax请求

        // 需要先生成一个内置对象
        var myformdata = new FormData();
        // 向内置对象中添加普通键值对
        myformdata.append('username','jason');
        // 向对象中添加文件数据,获取input中传入的文件
        myformdata.append('myfile',$('#d1')[0].files[0]);
        // 发送Ajax请求
        $.ajax({
            url:'',
            type:'post',
            data:myformdata,
            // 发送formdata数据需要指定两个关键性参数为false
            processData:false,  // 让浏览器不要对数据做任何操作
            contentType:false,  // 不要使用任何编码,FormData自带编码格式Django能识别该对象
            success:function (data) {
                alert(data)
            }    

5、Django内置的序列化功能

Django内置的序列功能借助 serializers 模块,可以序列化对象

以前我们将后端将数据传递给前端通过json序列化

from app01 import models
import json
​
def xlh(request):
    book_query = models.Book.objects.all()
    book_list = []
    for book_obj in book_query:
        book_list.append({
            'name': book_obj.name,
            'price': book_obj.price
        })
    res = json.dumps(book_list)
    return render(request, 'xlh.html', locals())

我们可以使用Django内置的序列化功能模块 serializers ,直接序列化,展示的结果更加多将类名,主键都能拿到

from django.core import serializersdef xlh(request):
    book_query = models.Book.objects.all()
    res = serializers.serialize('json', book_query)
return HttpResponse(res)
# [{"model": "app01.book", "pk": 1, "fields": {"name": "jason", "price": "123"}}]

6、批量插入数据

我们之前学过的批量插入数据方式通过for循环再利用create方法,但是每次都要走数据库,效率很低

def insert_data(request):
    for i in range(1,501):
        models.Book.objects.create(name='第%s本书'%i)
    book_query = models.Book.objects.all()
    return render(request, 'insert.html', locals())
  # 每次循环一次都需要去数据库插入一条,500条大约需要一分钟

我们可以通过先将数据添加到一个列表中再使用 bulk_create 方法插入数据,这样就只会走一次数据库,效率很高

def insert_data(request):
    book_list = []
    for i in range(1,9999):
        book_list.append(models.Book(name='新添加的第%s书'%i))
    models.Book.objects.bulk_create(book_list)  # 批量插入数据
    book_query = models.Book.objects.all()
    return render(request, 'insert.html', locals())
  # 效率很高,大约1秒插入9999条数据

7、自定义分页器及使用

1、先引用分页器组件,封装好的类,存入本地文件夹中

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 = 1if 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)
分页器组件

后端代码:

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

前端代码:

{{ page_obj.page_html|safe }}

8、Ajax结合sweetalert实现删除二次确认

1、首先需要从sweetalert 连接找到Bootstrap-sweetalert包,将包下载到本地

我们只需要使用dist文件中的 js 和 css 文件

2、从sweetalert forBootstrap中找到需要使用的样式,引用其代码

我们以删除二次确认为例:

后端代码:

from django.http import JsonResponse
import time
def show_user(request):
    """
    前后端如果是通过ajax进行交互 那么交互的媒介一般情况下都是一个字典
    """
    if request.method == 'POST':
        # 模仿删除等待效果
        time.sleep(3)
        # 定义一个字典,通过Ajax传给后端的data
        back_dic = {"code":1000,'msg':''}
        # 拿到需要删除的数据id
        delete_id = request.POST.get('delete_id')
        # 将数据删除
        models.Userinfo.objects.filter(pk=delete_id).delete()
        back_dic['msg'] = '删除成功,准备跑路!!!'
        # 以json数据格式将字典返回给前端Ajax中的data
        return JsonResponse(back_dic)
    user_queryset = models.Userinfo.objects.all()
    return render(request,'show_user.html',locals())

前端代码:

首先需要引入sweetalert文件中的css和js文件

    {% 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>

调用sweetalert中样式代码,加以修改

<script>
    // 给删除按钮绑定点击事件
    $('.cancel').click(function () {
        // 定义变量指代某一条数据
        var $aEle = $(this);
        // 引用sweetalert中的删除样式代码
        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");
                                // 方式1  删除后自动刷新网页
                                // window.location.reload()
                                // 方式2 DOM操作动态修改,将删除的那条数据移除
                                $aEle.parent().parent().remove()  // 将标签直接移除
                            }else{
                                swal('发生了未知的错误', "error");
                            }
                        }
                    });
                    
                } else {
                    swal("怂笔", "你成功的刷新我对你的认知", "error");
                }
            });
    })
</script>

 

posted @ 2020-01-12 15:01  Mr沈  阅读(324)  评论(0编辑  收藏  举报