66、django之模型層(model)--多表相關操作(圖書管理小練習)

来源:http://www.cnblogs.com/liluning/archive/2017/10/31/7745072.html
-Advertisement-
Play Games

前面幾篇隨筆的資料庫增刪改查操作都是在單表的操作上的,然而現實中不可能都是單表操作,更多的是多表操作,一對一,一對多,多對多的表結構才是我們經常需要處理的,本篇將帶我們瞭解多表操作的一些相關操作。也會帶著大家做一個簡單的圖書管理的小練習。 ...


前面幾篇隨筆的資料庫增刪改查操作都是在單表的操作上的,然而現實中不可能都是單表操作,更多的是多表操作,一對一,一對多,多對多的表結構才是我們經常需要處理的,本篇將帶我們瞭解多表操作的一些相關操作。也會帶著大家做一個簡單的圖書管理的小練習。

 

本篇導航:

 

一、建表

本篇繼續以圖書管理的例子。

模型:書籍有書名、作者、出版日期、價格,出版社,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關係(one-to-many)。

1、準備工作

1)創建一個項目

2)提前在mysql資料庫里建立一個庫

3)修改相關setting配置。(詳情可以參考前面63、64、65三篇隨筆)

2、建表

你如果在建表時沒有添加主鍵,django會自動給我們的表添加一個主鍵id,是不是很棒0.0

1)語法

一對一:
models.OneToOneField() ===>models.ForeignKey(,unique="True")
一對多:
models.ForeignKey()
多對多:
models.ManyToManyField()

屬性:
related_name=""    
可選屬性用來給此次關聯起名,如果不用此屬性django會自動給一個名字後面查詢的例子會說明(是關聯的名字不是欄位名)

2)建表實例

#圖書表
class Book(models.Model) :
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    prince = models.DecimalField(max_digits=5,decimal_places=2)
    
    publish = models.ForeignKey("Publish")  #一對多
    
    authorlish = models.ManyToManyField("Author")   #多對多

    def __str__(self) :
        return self.title

#出版社
class Publish(models.Model) :
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

    def __str__(self) :
        return  self.name

#作者表
class Author(models.Model) :
    name = models.CharField(max_length=32)
    sex = models.CharField(max_length=32)
    age = models.IntegerField()
    tel = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    
    def __str__(self) :
        return  self.name
圖書、出版社、作者

這個圖書的小練習中的表沒有一對一關聯用法用其他兩個關聯基本相同

3、註意

1)主鍵id 欄位是自動添加的

2)對於外鍵欄位,Django 會在欄位名上添加"_id" 來創建資料庫中的列名

3)外鍵欄位 ForeignKey 有一個 null=True 的設置(它允許外鍵接受空值 NULL),你可以賦給它空值 None 。

4)在表與表的連接中有related_name屬性可以起名用於反向查詢,當然不寫有預設(表名_set),一對一關聯可以直接用表名 


 

二、template模版的設計

此次的小練習template是用Bootstrap框架搭建的,因為我們本篇主講的django模版層所以會貼出代碼逮捕做分析,對前端有問題的可以看以前相關隨筆哦。

相關隨筆推薦:

Bootstrap使用:http://www.cnblogs.com/liluning/p/7643879.html

靜態文件在模版中的使用:http://www.cnblogs.com/liluning/p/7724699.html

Bootstrap官網:http://v3.bootcss.com/

 

模版使用:http://v3.bootcss.com/examples/dashboard/


 

三、添加表記錄

1、一對多添加語法

方式1:
   publish_obj=Publish.objects.get(nid=1)
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",publish=publish_obj)
 
方式2:
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",publish_id=1)

推薦使用第一種方式 第二種方式在你知道外鍵publish_id確切的值時可以使用

2、多對多添加語法

book_obj=Book.objects.create(title="追風箏的人",publishDate="2012-11-12",prince=69,publish_id=1)
 
author_yuan=Author.objects.create(name="yuan",age=23...)
author_egon=Author.objects.create(name="egon",age=32...)
 
book_obj.authorlish.add(author_egon,author_yuan)    #  將某個特定的 model 對象添加到被關聯對象集合中。   =======    book_obj.authors.add(*[])
 
book_obj.authorlish.create()      #創建並保存一個新對象,然後將這個對象加被關聯對象的集合中,然後返回這個新對象。

當然我們綁定的關聯也可以解除:

book_obj.authorlish.remove()     # 將某個特定的對象從被關聯對象集合中去除。    ======   book_obj.authors.remove(*[])
book_obj.authorlish.clear()       #清空被關聯對象集合。

3、一對多添加和多對多添加實例

def addbook(request) :
    # 添加圖書頁面點擊提交
    if request.method == "POST" :
        # 提取添加數據
        title = request.POST.get("title")
        author = request.POST.getlist("author")
        publishDate = request.POST.get("publishDate")
        prince = request.POST.get("prince")
        publish = request.POST.get("publish")
        # 出版社object
        publish_obj = models.Publish.objects.filter(name=publish)[0]
        # 添加數據到資料庫
        book_obj = models.Book.objects.create(title=title, publishDate=publishDate, prince=prince ,publish=publish_obj)
        # 多對多添加
        for i in author :
            obj = models.Author.objects.filter(name=i)[0]
            book_obj.authorlish.add(obj)
        return redirect("/index/")

    # 作者與出版社querySet
    authorList = models.Author.objects.all()
    publishList = models.Publish.objects.all()
    return render(request,"addbook.html",{"authorList":authorList,"publishList":publishList})
View Code

一對一因為沒用用到不做演示 用法與其他兩個關聯相同

4、相關方法演示(例子選自官網)

1)add

把指定的模型對象添加到關聯對象集中。

b = Blog.objects.get(id=1)
e = Entry.objects.get(id=234)
b.entry_set.add(e)

2)create

創建一個新的對象,保存對象,並將它添加到關聯對象集之中。返回新創建的對象。

b = Blog.objects.get(id=1)
e = b.entry_set.create(
    headline='Hello',
    body_text='Hi',
    pub_date=datetime.date(2005, 1, 1)
)

3)remove

從關聯對象集中移除執行的模型對象

b = Blog.objects.get(id=1)
e = Entry.objects.get(id=234)
b.entry_set.remove(e)

4)clear

從關聯對象集中移除一切對象。

b = Blog.objects.get(id=1)
b.entry_set.clear()

 

四、查詢表記錄

查詢相關API前面的隨筆已經寫到 單表查詢還有雙下劃線查詢0.0

1、雙下劃線單表查詢

models.book.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值
 
models.book.objects.filter(id__in=[11, 22, 33])   # 獲取id等於11、22、33的數據
models.book.objects.exclude(id__in=[11, 22, 33])  # not in
 
models.book.objects.filter(name__contains="ven")  #模糊匹配
models.book.objects.filter(name__icontains="ven") # icontains大小寫不敏感
 
models.book.objects.filter(id__range=[1, 2])      # 範圍bettwen and
 
startswith,istartswith, endswith, iendswith 

2、基於對象的跨表查詢

1)一對多查詢

正向查詢

# 查詢id=1的書籍的出版社所在的城市
book_obj=Book.objects.get(id=1)
print(book_obj.publish.city) 
# book_obj.publish 是id=1的書籍對象關聯的出版社對象

反向查詢

# 查詢 人民出版社出版過的所有書籍
 
publish=Publish.objects.get(name="人民出版社")
book_list=publish.book_set.all()  # 與人民出版社關聯的所有書籍對象集合
for book_obj in book_list:
    print(book_obj.title)

2)多對多查詢

正向查詢

# 金瓶眉所有作者的名字
 
book_obj=Book.objects.filter(title="金瓶眉").first()
authors=book_obj.authorlish.all()
 
for author_obj in authors:
    print(author_obj.name)

反向查詢

# 查詢egon出過的所有書籍的名字
author_obj=Author.objects.get(name="egon")
book_list=author_obj.book_set.all() #與egon作者相關的所有書籍
 
for book_obj in book_list:
    print(book_obj.title)

3、查詢實例

1)views

def index(request) :
    # 查看主頁(圖書)
    bookList = models.Book.objects.all()
    return render(request,"index.html",{"bookList":bookList})
View Code

2)template——index.html

<div class="table-responsive table-bordered">
    <table class="table table-striped" id="student">
    {% block content %}
        <thead>
        <div class="row">
            {% block th %}
                <tr>
                    <th>編號</th>
                    <th>書名</th>
                    <th>作者</th>
                    <th>出版日期</th>
                    <th>價格</th>
                    <th>出版社</th>
                    <th>操作</th>
                </tr>
            {% endblock th %}
        </div>
        </thead>
        <tbody>
            {% block td %}
                {% for book_obj in bookList %}
                    <tr>
                        <td>{{ forloop.counter}}</td>
                        <td>{{ book_obj.title }}</td>
                        <td>
                            {% for author in book_obj.authorlish.all %}
                                {{ author.name }}
                            {% endfor %}
                        </td>
                        <td>{{ book_obj.publishDate|date:"Y-m-d" }}</td>
                        <td>{{ book_obj.prince }}</td>
                        <td>{{ book_obj.publish.name }}</td>
                        <td>
                           <a href="/editbook/{{ book_obj.id }}"><button class="btn btn-info">編輯</button></a>
                           <a href="/delbook/{{ book_obj.id }}"><button class="btn btn-danger">刪除</button></a>
                        </td>
                    </tr>
                {% endfor %}
            {% endblock td %}
        </tbody>
    {% endblock content %}
    </table>
</div>
View Code

4、基於雙下劃線的跨表查詢

Django 還提供了一種直觀而高效的方式在查詢中表示關聯關係那就是強大的雙劃線

# 練習1:  查詢人民出版社出版過的所有書籍的名字與價格(一對多)
    # 正向查詢 按欄位:publish
    queryResult=Book.objects.filter(publish__name="人民出版社").values_list("title","price")

    # 反向查詢 按表名:book
    queryResult=Publish.objects.filter(name="人民出版社").values_list("book__title","book__price")

# 練習2: 查詢egon出過的所有書籍的名字(多對多)
    # 正向查詢 按欄位:authors:
    queryResult=Book.objects.filter(authors__name="yuan").values_list("title")

    # 反向查詢 按表名:book
    queryResult=Author.objects.filter(name="egon").values_list("book__title","book__price")


# 練習3: 查詢人民出版社出版過的所有書籍的名字以及作者的姓名
    # 正向查詢
    queryResult=Book.objects.filter(publish__name="人民出版社").values_list("title","authors__name")
    # 反向查詢
    queryResult=Publish.objects.filter(name="人民出版社").values_list("book__title","book__authors__age","book__authors__name")


# 練習4: 手機號以151開頭的作者出版過的所有書籍名稱以及出版社名稱
queryResult=Book.objects.filter(authors__authorDetail__telephone__regex="151").values_list("title","publish__name")

練習四需要在本建表實例上添加一個authorDetail作者詳細信息表將電話號等詳細信息放進去與作者表建立一對一關聯

5、聚合查詢與分組查詢

1)聚合:aggregate()

# 計算所有圖書的平均價格
from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))
#{'price__avg': 34.35}

需要使用什麼函數都要通過import導入 例如常用函數:Avg,Sum,Count,Max,Min

字典的key預設(欄位名__函數名)也可以自己起名字average_price

Book.objects.aggregate(average_price=Avg('price'))

一次也查詢多個可以

Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))

2)分組:annotate() 

#統計每一本書的作者個數
bookList=Book.objects.annotate(authorsNum=Count('authors'))
for book_obj in bookList:
    print(book_obj.title,book_obj.authorsNum)

annotate的返回值是querySet

3)練習

# 統計每一個出版社的最便宜的書
queryResult=Book.objects.values("publish__name").annotate(MinPrice=Min('price'))

# 統計每一本以py開頭的書籍的作者個數
queryResult=Book.objects .filter(title__startswith="Py").annotate(num_authors=Count('authors'))

# 統計不止一個作者的圖書
queryResult=Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

# 根據一本圖書作者數量的多少對查詢集 QuerySet進行排序
Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

# 查詢各個作者出的書的總價格
# 按author表的所有欄位 group by
    queryResult=Author.objects.annotate(SumPrice=Sum("book__price")).values_list("name","SumPrice")
    
#按authors__name group by
 queryResult2=Book.objects.values("authors__name").annotate(SumPrice=Sum("price")).values_list("authors__name","SumPrice")
View Code

 

五、修改表記錄

修改和以前的一樣還是update比較簡單就不做詳細解釋了直接上實例

def editbook(request,id) :
    # 點擊保存POST提交
    if request.method == "POST" :
        # 提取數據
        id = request.POST.get("id")
        title = request.POST.get("title")
        author_name = request.POST.getlist("author")
        publishDate = request.POST.get("publishDate")
        prince = request.POST.get("prince")
        publish_name = request.POST.get("publish")
        # 出版社object
        publish_obj = models.Publish.objects.get(name=publish_name)
        # 移除舊關聯
        book_obj = models.Book.objects.filter(id=id)[0]
        book_obj.authorlish.clear()
        

        # 更新圖書表
        models.Book.objects.filter(id=id).update(title=title, publishDate=publishDate, prince=prince,publish_id=publish_obj)


        book_obj = models.Book.objects.filter(id=id)[0]
        # 添加新關聯
        for i in author_name :
            print(i)
            obj = models.Author.objects.filter(name=i)[0]
            book_obj.authorlish.add(obj)
        return redirect("/index/")

雖然修改比較簡單就一句update搞定,可是因為現在圖書表和作者表是多對多連接所以在更新圖書表時需要清楚之前的連鍵和建立新的連接,語法在之前都有講解在代碼中也都標記出來了。


 

六、刪除表記錄

實例:

def delbook(request,id) :
    # 刪除圖書
    models.Book.objects.filter(id=id).delete()
    return redirect("/index/")

在 Django 刪除對象時,會模仿 SQL 約束 ON DELETE CASCADE 的行為,換句話說,刪除一個對象時也會刪除與它相關聯的外鍵對象。


 

七、圖書管理小練習(只貼上圖書的增刪改查代碼)

1、model 建表

from django.db import models

# Create your models here.

#圖書表
class Book(models.Model) :
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    prince = models.DecimalField(max_digits=5,decimal_places=2)
    
    publish = models.ForeignKey("Publish")  #一對多
    
    authorlish = models.ManyToManyField("Author")   #多對多

    def __str__(self) :
        return self.title

#出版社
class Publish(models.Model) :
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

    def __str__(self) :
        return  self.name

#作者表
class Author(models.Model) :
    name = models.CharField(max_length=32)
    sex = models.CharField(max_length=32)
    age = models.IntegerField()
    tel = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    
    def __str__(self) :
        return  self.name
View Code

2、template 模版

<!DOCTYPE html>
<!-- saved from url=(0041)http://v3.bootcss.com/examples/dashboard/ -->
<html lang="zh-CN">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3個meta標簽*必須*放在最前面,任何其他內容都*必須*跟隨其後! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="http://v3.bootcss.com/favicon.ico">

    <title>圖書管理系統</title>

    <!-- Bootstrap core CSS -->
    <link href="/static/Dashboard_files/bootstrap.min.css" rel="stylesheet">

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <link href="/static/Dashboard_files/ie10-viewport-bug-workaround.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="/static/Dashboard_files/dashboard.css" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]>
    <script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="/static/Dashboard_files/ie-emulation-modes-warning.js"></script>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
    <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->

    <style>
        /*左側菜單*/
        .head {
            margin: 1px -20px;
            line-height: 40px;
            text-align: center;
        }
        /*添加按鈕*/
        .search {
            padding-bottom: 15px;
        }
    </style>

</head>

<body>
<!--導航欄-->
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
        <div class="navbar-header">
            <a class="navbar-brand" href="/index/">圖書管理系統</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li><a href="/index/">登錄</a></li>
                <li><a href="/index/">註冊</a></li>
                <li><a href="/index/">幫助</a></li>
                <li><a href="/index/">關於</a></li>
            </ul>
        </div>
    </div>
</nav>

<!--左側菜單 和 學生信息-->
<div class="container-fluid">
    <div class="row">
        <!--左側菜單-->
        <div class="col-sm-

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...