Django 學習 之ORM多表操做

.建立模型

1.模型關係整理

建立一對一的關係:OneToOne("要綁定關係的表名")html

建立一對多的關係:ForeignKey("要綁定關係的表名")git

 

建立多對多的關係:ManyToMany("要綁定關係的表名")  會自動建立第三張表數據庫

 

書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);django

 一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)app

 

一對1、多對1、多對多 ,用book表和publish表本身來想一想關係,想一想裏面的操做,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上惟一約束。spa

實例:咱們來假定下面這些概念,字段和關係設計

做者模型:一個做者有姓名和年齡。htm

做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one對象

出版商模型:出版商有名稱,所在城市以及emailblog

書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)

下面咱們經過圖書管理系統,來設計出每張表之間的對應關係。

經過上圖關係,來定義一下咱們的模型類。

2.模型關係類的編寫

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()

 

3.注意事項

1、 表的名稱myapp_modelName,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱  

2id 字段是自動添加的

3、對於外鍵字段,Django 會在字段名上添加"_id" 來建立數據庫中的列名

4、這個例子中的CREATE TABLE SQL 語句使用MySQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。

5、定義好模型以後,你須要告訴Django _使用_這些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中設置,在其中添加models.py所在應用的名稱。

6、外鍵字段 ForeignKey 有一個 null=True 的設置(它容許外鍵接受空值 NULL),你能夠賦給它空值 None

4.關於字段選項

每一個字段有一些特有的參數,例如,CharField須要max_length參數來指定VARCHAR數據庫字段的大小。還有一些適用於全部字段的通用參數。 這些參數在文檔中有詳細定義,這裏咱們只簡單介紹一些最經常使用的:

1)null

若是爲TrueDjango 將用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.添加一些簡單的數據

(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);

多表存在關聯的狀況下,插入數據須要先將被關聯的表的數據先插入,在插入關聯的表的數據。

 

2.一對多添加記錄

# 一對多的添加

# 方式一:若是是這樣直接指定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)

 

3.多對多

# ##多對多新增

# 方式一 傳對象的形式

# 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()         #先清空再設置

總結:removeclear的區別

  remove:得吧你要清除的數據篩選出來,而後移除

  clear:不用查,直接就把數據都清空了。

各有應用場景

 

關聯管理器(RelatedManager)

關於關聯管理器大前提:

(1)多對多,雙向都有

(2)一對多時,對應多的一方纔有(出版社的角度)

(3)一對多時,反向才能使用關聯管理器

 

正向按字段名

反向按小寫表名+  _set

4.基於對象的跨表查詢

"""

          正向: 字段名稱

        ---------------->

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()  # 與明教出版社關聯的全部書籍對象集合

 

5.基於雙下劃線的跨表查詢

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)

相關文章
相關標籤/搜索