當前表
my_db=# select * from users;
id | name | fullname | password
----+--------+----------------+----------
1 | ed | Ed Jones | 0000
2 | wendy | Wendy Williams | foobar
3 | mary | Mary Contrary | xxf12
4 | jack | Jack Bean | gjfdd
5 | rickyy | ricky yu | 1
my_db=# select * from addresses;
id | email_address | user_id
----+---------------+---------
1 | jack@qq.com | 4
2 | j12@gamil.com | 4
3 | 1@qq.com | 5
4 | 2@qq.com | 5
(4 行記錄)
刪除
讓我們刪除rickyy用戶,看看怎么回事,我們在會話中將該對象標記為已刪除,然后我們發(fā)出查詢,看看有沒有剩余的行;
>>> session.delete('rickyy')
>>> session.query(User).filter_by(name='rickyy').count()
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.id = %(param_1)s
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine {'param_1': 5}
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM addresses
WHERE %(param_1)s = addresses.user_id ORDER BY addresses.id
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine {'param_1': 5}
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine UPDATE addresses SET user_id=%(user_id)s WHERE addresses.id = %(addresses_id)s
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine ({'addresses_id': 3, 'user_id': None}, {'addresses_id': 4, 'user_id': None})
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine DELETE FROM users WHERE users.id = %(id)s
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine {'id': 5}
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = %(name_1)s) AS anon_1
2017-03-11 09:17:32,045 INFO sqlalchemy.engine.base.Engine {'name_1': 'rickyy'}
0L
我們可以看到,用戶rickyy已經(jīng)被刪除了,我們再來看看Address表中的記錄是否還在
>>> session.query(Address).filter(Address.email_address.in_(['1@qq.com', '2@qq.com'])).count()
2017-03-11 09:21:54,078 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.email_address IN (%(email_address_1)s, %(email_address_2)s)) AS anon_1
2017-03-11 09:21:54,078 INFO sqlalchemy.engine.base.Engine {'email_address_2': '2@qq.com', 'email_address_1': '1@qq.com'}
2L
通過查詢,發(fā)現(xiàn)Address表中和用戶rikcyy關(guān)聯(lián)的地址仍然存在,我們提交改動試試。
>>> session.commit()
2017-03-11 09:22:39,924 INFO sqlalchemy.engine.base.Engine COMMIT
>>>
提交后查看一下我們的數(shù)據(jù)庫
my_db=# select * from addresses;
id | email_address | user_id
----+---------------+---------
1 | jack@qq.com | 4
2 | j12@gamil.com | 4
3 | 1@qq.com |
4 | 2@qq.com |
(4 行記錄)
我們可以看出,user_id列被置為NULL,但是行沒有被刪除,這是因為SQLAlchemy不會級聯(lián)刪除,我們必須告訴它這樣做。
配置刪除/級聯(lián)刪除
我們將在User.addresses關(guān)系上配置級聯(lián)選項以更改行為。雖然SQLAlchemy允許我們在任何時間點添加新的屬性和關(guān)系到映射,在這種情況下需要刪除現(xiàn)有關(guān)系,因此我們需要完全拆除映射并重新開始 - 我們將關(guān)閉會話;
>>> session.close()
使用declarative_base()新建一個基類
>>> Base = declarative_base()
下面我們將重新定義User類,并添加addresses關(guān)系,包括級聯(lián)配置。
>>> class User(Base):
... __tablename__ = 'users'
... id=Column(Integer, primary_key=True)
... name=Column(String)
... fullname=Column(String)
... password=Column(String)
... addresses = relationship('Address', back_populates="user",cascade='all,delete,delete-orphan')
... def __repr__(self):
... return "<User(name=%s, fullname=%s, password=%s)>" % (self.name, self.fullname, self.password)
...
重新定義Address類
>>> class Address(Base):
... __tablename__ = 'addresses'
... id = Column(Integer, primary_key=True)
... email_address = Column(String, nullable=False)
... user_id = Column(Integer, ForeignKey('users.id'))
... user = relationship("User", back_populates="addresses")
... def __repr__(self):
... return "<Address(email_address=%s)>" % (self.email_address,)
...
接下來刪除用戶jack,可以從上面的表內(nèi)容看出我們的用戶jack有兩個地址。
# 通過主鍵查詢jack
>>> jack=session.query(User).get(4)
2017-03-11 09:42:16,760 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.id = %(param_1)s
2017-03-11 09:42:16,762 INFO sqlalchemy.engine.base.Engine {'param_1': 4}
>>> jack
<User(name=jack, fullname=Jack Bean, password=gjfdd)>
# 刪除jack的一個地址
>>> del jack.addresses[1]
# 查詢刪除后還有幾個地址
>>> session.query(Address).filter(Address.email_address.in_(['jack@qq.com','j12@gmail.com'])).count()
2017-03-11 09:43:46,957 INFO sqlalchemy.engine.base.Engine DELETE FROM addresses WHERE addresses.id = %(id)s
2017-03-11 09:43:46,957 INFO sqlalchemy.engine.base.Engine {'id': 2}
2017-03-11 09:43:47,000 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.email_address IN (%(email_address_1)s, %(email_address_2)s)) AS anon_1
2017-03-11 09:43:47,003 INFO sqlalchemy.engine.base.Engine {'email_address_2': 'j12@gmail.com', 'email_address_1': 'jack@qq.com'}
1L
我們刪除jack,和他關(guān)聯(lián)的所有地址將會被刪除:
>>> session.delete(jack)
>>> session.query(User).filter_by(name='jack').count()
2017-03-11 09:44:21,401 INFO sqlalchemy.engine.base.Engine DELETE FROM addresses WHERE addresses.id = %(id)s
2017-03-11 09:44:21,404 INFO sqlalchemy.engine.base.Engine {'id': 1}
2017-03-11 09:44:21,407 INFO sqlalchemy.engine.base.Engine DELETE FROM users WHERE users.id = %(id)s
2017-03-11 09:44:21,408 INFO sqlalchemy.engine.base.Engine {'id': 4}
2017-03-11 09:44:21,413 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name = %(name_1)s) AS anon_1
2017-03-11 09:44:21,414 INFO sqlalchemy.engine.base.Engine {'name_1': 'jack'}
0L
>>> session.query(Address).filter(Address.email_address.in_(['jack@qq.com','j12@gmail.com'])).count()
2017-03-11 09:44:25,979 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user_id AS addresses_user_id
FROM addresses
WHERE addresses.email_address IN (%(email_address_1)s, %(email_address_2)s)) AS anon_1
2017-03-11 09:44:25,980 INFO sqlalchemy.engine.base.Engine {'email_address_2': 'j12@gmail.com', 'email_address_1': 'jack@qq.com'}
0L
最后我們提交到數(shù)據(jù)庫。
>>> session.commit()
再來看看當前的表內(nèi)容:
my_db=# select * from addresses;
id | email_address | user_id
----+---------------+---------
3 | 1@qq.com |
4 | 2@qq.com |
(2 行記錄)
my_db=# select * from addresses;
id | email_address | user_id
----+---------------+---------
3 | 1@qq.com |
4 | 2@qq.com |
(2 行記錄)
建立多對多的關(guān)系
在數(shù)據(jù)庫中建立n:m的關(guān)系,需要新增加一個關(guān)系表。
我們再定義一個BlogPost類,多對多關(guān)系我們要創(chuàng)建一個沒有映射的表作為關(guān)聯(lián)表:
>>> from sqlalchemy import Table, Text
>>> post_keywords = Table('post_keywords', Base.metadata,
... Column('post_id', ForeignKey('posts.id'), primary_key=True),
... Column('keyword_id', ForeignKey('keywords.id'), primary_key=True)
... )
我們上面創(chuàng)建了一個表,只有兩個字段分別參照兩個不同的表,posts keywords表。
接下來我們定義這兩個類
>>> class BlogPost(Base):
... __tablename__ = 'posts'
...
... id = Column(Integer, primary_key=True)
... user_id = Column(Integer, ForeignKey('users.id'))
... headline = Column(String(255), nullable=False)
... body = Column(Text)
...
... # many to many BlogPost<->Keyword
... keywords = relationship('Keyword',
... secondary=post_keywords,
... back_populates='posts')
...
... def __init__(self, headline, body, author):
... self.author = author
... self.headline = headline
... self.body = body
...
... def __repr__(self):
... return "BlogPost(%r, %r, %r)" % (self.headline, self.body, self.author)
>>> class Keyword(Base):
... __tablename__ = 'keywords'
...
... id = Column(Integer, primary_key=True)
... keyword = Column(String(50), nullable=False, unique=True)
... posts = relationship('BlogPost',
... secondary=post_keywords,
... back_populates='keywords')
...
... def __init__(self, keyword):
... self.keyword = keyword
因為發(fā)布文章表和關(guān)鍵詞表時n:m的關(guān)系,所以需要使用relationship()建立關(guān)系,這里使用了secondary來定義多對多的關(guān)系。
我們也希望我們的BlogPost類有一個作者字段。我們將它添加為另一個雙向關(guān)系,除了一個問題,我們將有一個單一的用戶可能有很多博客文章。當我們訪問User.posts時,我們希望能夠進一步過濾結(jié)果,以便不加載整個集合。為此,我們使用一個被relationship()稱為lazy ='dynamic'的設(shè)置,該屬性在屬性上配置一個備用加載器策略。
>>> BlogPost.author = relationship(User, back_populates="posts")
>>> User.posts = relationship(BlogPost, back_populates="author", lazy="dynamic")
創(chuàng)建一個新表
>>> Base.metadata.create_all(engine)
現(xiàn)在我們發(fā)布博客
>>> wendy = session.query(User).\
... filter_by(name='wendy').\
... one()
>>> post = BlogPost("Wendy's Blog Post", "This is a test", wendy)
>>> session.add(post)
我們將關(guān)鍵字存儲在數(shù)據(jù)庫中,但是現(xiàn)在還沒有,所以我們可以創(chuàng)建它們:
>>> post.keywords.append(Keyword('wendy'))
>>> post.keywords.append(Keyword('firstpost')
我們現(xiàn)在可以使用關(guān)鍵字firstpost查找所有博文。我們將使用any運算符來定位“任何關(guān)鍵字具有關(guān)鍵字字符串firstpost的博客帖子
>>> session.query(BlogPost).filter(BlogPost.keywords.any(keyword='firstpost')).all()
2017-03-11 10:23:44,809 INFO sqlalchemy.engine.base.Engine INSERT INTO keywords (keyword) VALUES (%(keyword)s) RETURNING keywords.id
2017-03-11 10:23:44,809 INFO sqlalchemy.engine.base.Engine {'keyword': 'wendy'}
2017-03-11 10:23:44,809 INFO sqlalchemy.engine.base.Engine INSERT INTO keywords (keyword) VALUES (%(keyword)s) RETURNING keywords.id
2017-03-11 10:23:44,809 INFO sqlalchemy.engine.base.Engine {'keyword': 'firstpost'}
2017-03-11 10:23:44,809 INFO sqlalchemy.engine.base.Engine INSERT INTO posts (user_id, headline, body) VALUES (%(user_id)s, %(headline)s, %(body)s) RETURNING posts.id
2017-03-11 10:23:44,825 INFO sqlalchemy.engine.base.Engine {'body': 'This is a test', 'headline': "Wendy's Blog Post", 'user_id': 2}
2017-03-11 10:23:44,857 INFO sqlalchemy.engine.base.Engine INSERT INTO post_keywords (post_id, keyword_id) VALUES (%(post_id)s, %(keyword_id)s)
2017-03-11 10:23:44,857 INFO sqlalchemy.engine.base.Engine ({'keyword_id': 2, 'post_id': 1}, {'keyword_id': 1, 'post_id': 1})
2017-03-11 10:23:44,872 INFO sqlalchemy.engine.base.Engine SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.body AS posts_body
FROM posts
WHERE EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword = %(keyword_1)s)
2017-03-11 10:23:44,872 INFO sqlalchemy.engine.base.Engine {'keyword_1': 'firstpost'}
[BlogPost("Wendy's Blog Post", 'This is a test', <User(name=wendy, fullname=Wendy Williams, password=foobar)>)]
補充上面關(guān)系E-R圖

參考官方文檔:http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#building-a-relationship