前面幾篇隨筆的資料庫增刪改查操作都是在單表的操作上的,然而現實中不可能都是單表操作,更多的是多表操作,一對一,一對多,多對多的表結構才是我們經常需要處理的,本篇將帶我們瞭解多表操作的一些相關操作。也會帶著大家做一個簡單的圖書管理的小練習。 ...
前面幾篇隨筆的資料庫增刪改查操作都是在單表的操作上的,然而現實中不可能都是單表操作,更多的是多表操作,一對一,一對多,多對多的表結構才是我們經常需要處理的,本篇將帶我們瞭解多表操作的一些相關操作。也會帶著大家做一個簡單的圖書管理的小練習。
本篇導航:
本篇繼續以圖書管理的例子。
模型:書籍有書名、作者、出版日期、價格,出版社,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關係就是多對多的關聯關係(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是用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.nameView 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-