Django框架之多對多表關係建立與Ajax

Django框架之多對多表關係建立與Ajax

一、表關係建立多對多三種建立方式

一、全自動(較爲經常使用)css

Django ORM會自動幫你建立第三張表只需定義兩張表類使用多對多外鍵鏈接html

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)

好處:第三張表自動建立前端

不足:第三張表沒法擴展額外的字段git

二、存手動github

第三張表本身手動建立,外鍵鏈接兩張表,能夠擴展額外的字段,可是不能用ORM查詢語句,無正向反向的概念了ajax

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查詢的時候不方便django

三、半自動(推薦使用)json

手動建立第三張表,在關聯的兩張表中都建立多對多的外鍵,使得能夠擴展任意的字段bootstrap

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方法對第三張表操做

二、Ajax

一、Ajax簡介

Ajax不是一門新的語言,它其實就是基於js寫的一個功能模塊

異步提交,局部刷新(最大的優勢):不加載整個頁面的狀況下,能夠跟服務器交換數據並更新部分網頁內容,在不知不覺的過程當中完成了請求響應過程

應用:在用戶輸入關鍵字的時候,會自動去數據庫中進行查詢和進行校驗操做,最後返回出結果,整個過程頁面沒有刷新,只是刷新局部的位置

二、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)
            }
        })

三、Content-Type先後端傳輸數據編碼格式

一、三種數據編碼格式:

  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

二、form表單

一、默認是urlencoded編碼格式傳輸數據

前端以username=Jason&password=123展現,Django後端會自動解析打包到request.POST中,而針對文件數據只能獲取文件名,並不能真正獲取文件數據

二、能夠修改成formdata數據格式

指定enctype="multipart/form-data"

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

django後端針對符合urlencoded編碼格式數據(普通鍵值對)仍是統一解析到request.POST中,會針對formdata數據格式的文件數據會自動解析到request.FILES中

三、Ajax提交

Ajax默認也是urlencoded編碼格式,也能夠修改提交數據格式爲json數據格式

四、Ajax發送json格式數據及發送文件

一、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))

二、Ajax發送文件

內置對象FormData:既能夠發普通鍵值對數據也能夠發文件數據

Ajax發送文件數據須要藉助於內置對象

一、首先須要生成一個內置對象

var myFormData = new FormData

二、向生成的對象中添加數據

三、發送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)
            }    

五、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"}}]

六、批量插入數據

咱們以前學過的批量插入數據方式經過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條數據

七、自定義分頁器及使用

一、先引用分頁器組件,封裝好的類,存入本地文件夾中

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

八、Ajax結合sweetalert實現刪除二次確認

一、首先須要從sweetalert 鏈接找到Bootstrap-sweetalert包,將包下載到本地

咱們只須要使用dist文件中的 js 和 css 文件

二、從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>
相關文章
相關標籤/搜索