建立一對一的關係:OneToOne("要綁定關係的表名")html
建立一對多的關係:ForeignKey("要綁定關係的表名")git
建立多對多的關係:ManyToMany("要綁定關係的表名") 會自動建立第三張表數據庫
書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);django
一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)。app
一對1、多對1、多對多 ,用book表和publish表本身來想一想關係,想一想裏面的操做,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上惟一約束。spa
實例:咱們來假定下面這些概念,字段和關係設計
做者模型:一個做者有姓名和年齡。htm
做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one)對象
出版商模型:出版商有名稱,所在城市以及email。blog
書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)。
下面咱們經過圖書管理系統,來設計出每張表之間的對應關係。
經過上圖關係,來定義一下咱們的模型類。
from django.db import models
class Book(models.Model):
# nid = models.AutoField(primary_key=True) # 自增id(能夠不寫,默認會有自增id)
title = models.CharField(max_length=32) # 書名
price = models.DecimalField(max_digits=5, decimal_places=2)
# 價格 一共5位,保留兩位小數
pub_date = models.DateField() #出版日期
# 一個出版社有多本書,關聯字段要寫在多的一方
# 不用命名爲publish_id,由於django爲咱們自動就加上了_id
# foreignkey(表名)創建的一對多關係
publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
authors = models.ManyToManyField("Author") # 創建的多對多的關係
class Publish(models.Model):
# 不寫id的時候數據庫會自動給你增長自增id
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
email = models.EmailField()
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)
class AuthorDetail(models.Model):
gender_choices = (
(0, "女"),
(1, "男"),
(2, "保密"),
)
gender = models.SmallIntegerField(choices=gender_choices)
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()
1、 表的名稱myapp_modelName,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱
2、id 字段是自動添加的
3、對於外鍵字段,Django 會在字段名上添加"_id" 來建立數據庫中的列名
4、這個例子中的CREATE TABLE SQL 語句使用MySQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。
5、定義好模型以後,你須要告訴Django _使用_這些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中設置,在其中添加models.py所在應用的名稱。
6、外鍵字段 ForeignKey 有一個 null=True 的設置(它容許外鍵接受空值 NULL),你能夠賦給它空值 None 。
每一個字段有一些特有的參數,例如,CharField須要max_length參數來指定VARCHAR數據庫字段的大小。還有一些適用於全部字段的通用參數。 這些參數在文檔中有詳細定義,這裏咱們只簡單介紹一些最經常使用的:
1)null
若是爲True,Django 將用NULL 來在數據庫中存儲空值。 默認值是 False.
(1)blank
若是爲True,該字段容許不填。默認爲False。
要注意,這與 null 不一樣。null純粹是數據庫範疇的,而 blank 是數據驗證範疇的。
若是一個字段的blank=True,表單的驗證將容許該字段是空值。若是字段的blank=False,該字段就是必填的。
(2)default
字段的默認值。能夠是一個值或者可調用對象。若是可調用 ,每有新對象被建立它都會被調用。
(3)primary_key
若是爲True,那麼這個字段就是模型的主鍵。若是你沒有指定任何一個字段的primary_key=True,
Django 就會自動添加一個IntegerField字段作爲主鍵,因此除非你想覆蓋默認的主鍵行爲,
不然不必設置任何一個字段的primary_key=True。
(4)unique
若是該值設置爲 True, 這個數據字段的值在整張表中必須是惟一的
(5)choices
由二元組組成的一個可迭代對象(例如,列表或元組),用來給字段提供選擇項。 若是設置了choices ,
默認的表單將是一個選擇框而不是標準的文本框,並且這個選擇框的選項就是choices 中的選項。
這是一個關於 choices 列表的例子:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每一個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是在管理界面或 ModelChoiceField 中用做顯示的內容。
在一個給定的 model 類的實例中,想獲得某個 choices 字段的顯示值,就調用 get_FOO_display 方法(這裏的 FOO 就是 choices 字段的名稱 )。例如:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large
一旦你創建好數據模型以後,django會自動生成一套數據庫抽象的API,可讓你執行關於表記錄的增刪改查的操做。
(1)publish表
insert into app01_publish(name,city,email) values("華山出版社", "華山", "hs@163.com"), ("明教出版社", "黑木崖", "mj@163.com");
(2)authordatail表
insert into app01_authordetail(gender,tel,addr,birthday) value (1,13432335433,"華山","1994-5-23"),(1,13943454554,"黑木崖","1961-8-13"),(0,13878934322,"黑木崖","1996-5-20");
(3)author表
insert into app01_author(name,age,au_detail_id) value ("令狐沖",25,1),("任我行",58,2),("任盈盈",23,3);
多表存在關聯的狀況下,插入數據須要先將被關聯的表的數據先插入,在插入關聯的表的數據。
# 一對多的添加
# 方式一:若是是這樣直接指定publish_id字段去添加值,前提是你的主表裏面必須有數據
# 主表:沒有被關聯的(由於book表是要依賴於publish這個表的)也就是publish表
# 子表:關聯的表
# ##一對多新增
# 方式一 傳對象的形式
pub_obj = models.Publish.objects.get(pk=1)
book = models.Book.objects.create(title="獨孤九劍", price=180, pub_date="2018-10-23", publish=pub_obj)
# 方式二 傳對象的id(用的更多)
# new_book = models.Book.objects.create(title="衝靈劍法", price=120, pub_date="2018-08-23", publish_id=pub_obj.pk)
# print(new_book)
# ##多對多新增
# 方式一 傳對象的形式
# book_obj = models.Book.objects.get(pk=1)
# print(book_obj)
# ling = models.Author.objects.get(pk=1)
# print(ling)
# ying = models.Author.objects.get(pk=3)
# print(ying)
# book_obj.authors.add(ling, ying)
# 傳入的是對象,經過模型會自動轉爲相應的id,爲book_authors新增信息
#
# # 方式二 傳對象id
book_obj = models.Book.objects.get(pk=2)
ling = models.Author.objects.get(pk=1)
# print(ling.pk)
book_obj.authors.add(ling.pk)
book_obj.authors.remove() # 將某個特定的對象從被關聯對象集合中去除。 ====== book_obj.authors.remove(*[])
book_obj.authors.clear() #清空被關聯對象集合
book_obj.authors.set() #先清空再設置
總結:remove和clear的區別
remove:得吧你要清除的數據篩選出來,而後移除
clear:不用查,直接就把數據都清空了。
各有應用場景
關於關聯管理器大前提:
(1)多對多,雙向都有
(2)一對多時,對應多的一方纔有(出版社的角度)
(3)一對多時,反向才能使用關聯管理器
正向按字段名
反向按小寫表名+ _set
"""
正向: 字段名稱
---------------->
Book Publish
<----------------
反向: 小寫表名+_set
"""
(1)一對多查詢(Publish 與 Book)
# 正向例子
# 查詢主鍵爲1的書籍的出版社所在的城市
book_obj = models.Book.objects.get(pk=1)
print(book_obj.publish.city)
# 反向例子
# 查詢華山出版社出版的書籍名
pub_obj = models.Publish.objects.filter(name="華山出版社").first()
# 反向獲取到存在這個pub_obj對象的書,在遍歷出來
for book in pub_obj.book_set.all():
print(book.title)
(2)一對一查詢(Author 和 AuthorDetail)
正向查詢(按字段:au_detail)
# 查詢令狐沖的電話
author_obj = models.Author.objects.filter(name="令狐沖").first()
print(author_obj.au_detail.tel)
# 反向查詢(按表名:author):
# 查詢全部住址在黑木崖的做者的姓名
au_detail_obj = models.AuthorDetail.objects.filter(addr="黑木崖")
for au in au_detail_obj:
print(au.author.name)
(3)多對多查詢 (Author 與 Book)
# 獨孤九劍全部做者的名字以及手機號
book_obj = models.Book.objects.filter(title="獨孤九劍").first()
for au in book_obj.authors.all():
print(au.name, au.au_detail.tel)
# 反向查詢(按表名:book_set):
# 查詢令狐沖出過的全部書籍的名字
au_obj = models.Author.objects.filter(name="令狐沖").first()
for book in au_obj.book_set.all():
print(book.title)
注意:
你能夠經過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,若是 Book model 中作一下更改:
publish = models.ForeignKey("Publish", on_delete=models.CASCADE, related_name="book_list")
那麼接下來就會如咱們看到這般:
# 查詢 明教出版社出版過的全部書籍
publish=Publish.objects.get(name="明教出版社")
book_list=publish.book_list.all() # 與明教出版社關聯的全部書籍對象集合
Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯繫。要作跨關係查詢,就使用兩個下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的model 爲止。
正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表
"""
正向: 字段名稱__跨表的字段名稱
----------------------------->
Book Publish
<-----------------------------
反向: 小寫表名__跨表的字段名稱
"""
(1)一對多查詢
# 練習: 查詢華山出版社出版過的全部書籍的名字與價格(一對多)
# 正向查詢 按字段:publish
book_title_price1 = models.Book.objects.filter(publish__name="華山出版社").values_list("title", "price")
print(book_title_price1)
# 反向查詢 按表名:book
book_title_price2 = models.Publish.objects.filter(name="華山出版社").values_list("book__title", "book__price")
print(book_title_price2)
(2)多對多查詢
# 練習: 查詢令狐沖出過的全部書籍的名字(多對多)
# 正向查詢 按字段:authors:
book_name = models.Book.objects.filter(authors__name="令狐沖").values("title")
print(book_name)
# 反向查詢 按表名:book
book_name = models.Author.objects.filter(name="令狐沖").values("book__title")
print(book_name)
(3)一對一查詢
# 查詢令狐沖的手機號
au_tel =models.Author.objects.filter(name ="令狐沖").values("au_detail__tel")
print(au_tel)
# 反向查詢
au_tel = models.AuthorDetail.objects.filter(author__name="令狐沖").values("tel")
print(au_tel)
(4)進階練習(連續跨表)
# # 正向查詢
bname_aname = models.Book.objects.filter(publish__name="華山出版社").values("title","authors__name")
print(bname_aname)
# 反向查詢
bname_aname = models.Publish.objects.filter(name="華山出版社").values("book__title","book__authors__name")
print(bname_aname)
# 練習2: 手機號以132開頭的做者出版過的全部書籍名稱以及出版社名稱
# 正向查詢
bname_pname = models.Book.objects.filter(authors__au_detail__tel__startswith="134").values("title", "publish__name")
print(bname_pname)
# 反向查詢1
bname_pname = models.Author.objects.filter(au_detail__tel__startswith="134").values("book__title", "book__publish__name")
print(bname_pname)
# 反向查詢2
bname_pname = models.AuthorDetail.objects.filter(tel__startswith="134").values("author__book__title", "author__book__publish__name")
print(bname_pname)