日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Python?SQLAlchemy建立模型基礎關系模式過程詳解_python

作者:皮皮要HAPPY ? 更新時間: 2023-01-08 編程語言

使用SQLAlchemy建立模型之間的基礎關系模式

1.一對多

class Author(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(70), unique=True) 
    phone = db.Column(db.String(20)) 
class Article(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    title = db.Column(db.String(50), index=True) 
    body = db.Column(db.Text)

1.1 定義外鍵

定義外鍵:定義關系的第一步是創建外鍵。外鍵是(foreign key)用來在 A 表存儲 B 表的主鍵值以便和 B 表建立聯系的關系字段。

因為外鍵只能存儲單一數據(標量),所以外鍵總是在 “多” 這一側定義,多篇文章屬于同 一個作者,所以我們需要為每篇文章添加外鍵存儲作者的主鍵值以指向 對應的作者。在 Article 模型中,我們定義一個 author_id 字段作為外鍵:

class Article(db.Model): 
	... 
	author_id = db.Column(db.Integer, db.ForeignKey('author.id'))

這個字段使用 db.ForeignKey 類定義為外鍵,傳入關系另一側的表名 和主鍵字段名,即 author.id。實際的效果是將 article 表的 author_id 的值限制為 author 表的 id 列的值。它將用來存儲 author 表中記錄的主鍵值。

1.2 定義關系屬性

定義關系的第二步是使用關系函數定義關系屬性。關系屬性在關系 的出發側定義,即一對多關系的 “一” 這一側。一個作者擁有多篇文章, 在 Author 模型中,我們定義了一個 articles 屬性來表示對應的多篇文章:

class Author(db.Model): 
	... 
	articles = db.relationship('Article')

這個屬性并沒有使用 Column 類聲明為列,而是使用了 db.relationship() 關系函數定義為關系屬性,因為這個關系屬性返回多個記錄,我們稱之為集合關系屬性。

relationship() 函數的第一個參數 為關系另一側的模型名稱,它會告訴 SQLAlchemy 將 Author 類與 Article 類建立關系。當這個關系屬性被調用時,SQLAlchemy 會找到關系另一側 (即 article 表)的外鍵字段(即 author_id),然后反向查詢 article 表中所有 author_id 值為當前表主鍵值(即 author.id)的記錄,返回包含這些記錄的列表,也就是返回某個作者對應的多篇文章記錄。

1.3 建立關系

foo = Author(name='Foo') 
spam = Article(title='Spam') 
ham = Article(title='Ham')

建立關系有兩種方式,第一種方式是為外鍵字段賦值:

spam.author_id = 1
ham.author_id = 1

調用后,結果如下:

foo.articles 
# [<Article u'Spam'>, <Article u'Ham'>]

另一種方式是通過操作關系屬性,將關系屬性賦給實際的對象即可建立關系。

foo.articles.append(spam) 
foo.articles.append(ham)

1.4 建立雙向關系

我們在 Author 類中定義了集合關系屬性 articles,用來獲取某個作者擁有的多篇文章記錄。在某些情況下,你也許希望能在 Article 類中定義 一個類似的 author 關系屬性,當被調用時返回對應的作者記錄,這類返回單個值的關系屬性被稱為 標量關系屬性。而這種兩側都添加關系屬性獲取對方記錄的關系我們稱之為 雙向關系(bidirectional relationship)。

雙向關系并不是必須的,但在某些情況下會非常方便。雙向關系的建立很簡單,通過在關系的另一側也創建一個 relationship() 函數,我們就可以在兩個表之間建立雙向關系。我們使用作家(Writer)和書 (Book)的一對多關系來進行演示:

class Writer(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(70), unique=True) 
	books = db.relationship('Book', back_populates='writer') 
class Book(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	title = db.Column(db.String(50), index=True) 
	writer_id = db.Column(db.Integer, db.ForeignKey('writer.id')) 
	writer = db.relationship('Writer', back_populates='books')

需要注意的是,我們只需要在關系的一側操作關系。當為 Book 對象的 writer 屬性賦值后,對應 Writer 對象的 books 屬性的返回值也會自動包含這個 Book 對象。反之,當某個 Writer 對象被刪除時,對應的 Book 對象的 writer 屬性被調用時的返回值也會被置為空(即 NULL,會返回 None)。

其他關系模式建立雙向關系的方式完全相同,在下面介紹不同的關系模式時我們會簡單說明。

1.5 使用 backref 簡化關系定義

在介紹關系函數的參數時,我們曾提到過,使用關系函數中的 backref 參數可以簡化雙向關系的定義。以一對多關系為例,backref 參數用來自動為關系另一側添加關系屬性,作為反向引用(back reference),賦予的值會作為關系另一側的關系屬性名稱。

class Singer(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(70), unique=True) 
	songs = db.relationship('Song', backref='singer') 
class Song(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(50), index=True) 
	singer_id = db.Column(db.Integer, db.ForeignKey('singer.id'))

盡管使用 backref 非常方便,但通常來說 “顯式好過隱式”,所以我們應該盡量使用 back_populates 定義雙向關系。

2.多對一

一對多關系反過來就是多對一關系,這兩種關系模式分別從不同的視角出發。

我們在前面介紹過,關系屬性在關系模式的出發側定義。當出發點在 “多” 這一側時,我們希望在 Citizen 類中添加一個關系屬性 city 來獲取對應的城市對象,因為這個關系屬性返回單個值,我們稱之為標量關系屬性。在定義關系時,外鍵總是在 “多” 這一側定義,所以在多對一關系中外鍵和關系屬性都定義在 “多” 這一側,即 City 類中。

class Citizen(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(70), unique=True) 
	city_id = db.Column(db.Integer, db.ForeignKey('city.id')) 
	city = db.relationship('City') 
class City(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(30), unique=True)

這時定義的 city 關系屬性是一個標量屬性(返回單一數據)。當 Citizen.city 被調用時,SQLAlchemy 會根據外鍵字段 city_id 存儲的值查找對應的 City 對象并返回,即居民記錄對應的城市記錄。

當建立雙向關系時,如果不使用 backref,那么一對多和多對一關系 模式在定義上完全相同,這時可以將一對多和多對一視為同一種關系模式。在后面我們通常都會為一對多或多對一建立雙向關系,這時將弱化這兩種關系的區別,一律稱為一對多關系。

3.一對一

一對一關系實際上是通過建立雙向關系的一對多關系的基礎上轉化而來。我們要確保關系兩側的關系屬性都是標量屬性,都只返回單個值,所以要在定義集合屬性的關系函數中將 uselist 參數設為 False,這時一對多關系將被轉換為一對一關系。

class Country(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(30), unique=True) 
	capital = db.relationship('Capital', uselist=False) 
class Capital(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(30), unique=True) 
	country_id = db.Column(db.Integer, db.ForeignKey('country.id')) 
	country = db.relationship('Country')

4.多對多

我們將使用學生和老師來演示多對多關系:每個學生有多個老師, 而每個老師有多個學生。

在一對多關系中,我們可以在 “多” 這一側添加外鍵指向 “一” 這一 側,外鍵只能存儲一個記錄,但是在多對多關系中,每一個記錄都可以與關系另一側的多個記錄建立關系,關系兩側的模型都需要存儲一組外鍵。

SQLAlchemy 中,要想表示多對多關系,除了關系兩側的模型外,我們還需要創建一個關聯表(association table)。關聯表不存儲數據,只用來存儲關系兩側模型的外鍵對應關系。

association_table = db.Table(
	'association',
	db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
	db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id')) 
	) 
class Student(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(70), unique=True) 
	grade = db.Column(db.String(20)) 
	teachers = db.relationship('Teacher', secondary=association_table, back_populates='students') 
class Teacher(db.Model): 
	id = db.Column(db.Integer, primary_key=True) 
	name = db.Column(db.String(70), unique=True) 
	office = db.Column(db.String(20))

當我們需要查詢某個學生記錄的多個老師時,我們先通過學生和關聯表的一對多關系查找所有包含該學生的關聯表記錄,然后就可以從這 些記錄中再進一步獲取每個關聯表記錄包含的老師記錄。

我們在 Student 類中定義一個 teachers 關系屬性用來獲取老師集合。 在多對多關系中定義關系函數,除了第一個參數是關系另一側的模型名稱外,我們還需要添加一個 secondary 參數,把這個值設為關聯表的名稱。

為了便于實現真正的多對多關系,我們需要建立雙向關系。建立雙向關系后,多對多關系會變得更加直觀。在 Student 類上的 teachers 集合 屬性會返回所有關聯的老師記錄,而在 Teacher 類上的 students 集合屬性 會返回所有相關的學生記錄。

class Student(db.Model): 
	... 
	teachers = db.relationship('Teacher', secondary=association_table, back_populates='students') 
class Teacher(db.Model): 
	... 
	students = db.relationship('Student', secondary=association_table, back_populates='teachers')

關聯表由 SQLAlchemy 接管,它會幫我們管理這個表:我們只需要像往常一樣通過操作關系屬性來建立或解除關系,SQLAlchemy 會自動在關聯表中創建或刪除對應的關聯表記錄,而不用手動操作關聯表。

原文鏈接:https://blog.csdn.net/be_racle/article/details/127134603

欄目分類
最近更新