sqlalchemy - day2

             Relationship Configuration

1、one to many

直接上代碼mysql

 1 from sqlalchemy import create_engine
 2 
 3 engine = create_engine("mysql+pymysql://root:max123@127.0.0.1/test?charset=utf8", echo=False)
 4 
 5 from sqlalchemy import *
 6 from sqlalchemy.ext.declarative import declarative_base
 7 from datetime import datetime, date
 8 
 9 Base = declarative_base()
10 
11 
12 class Parent(Base):
13     __tablename__ = 'perent'
14 
15     id = Column(Integer(), primary_key=True)
16     name = Column(String(50))
17     children = relationship('Child')  
18 
19 
20 class Child(Base):
21     __tablename__ = 'child'
22 
23     id = Column(Integer(), primary_key=True)
24     name = Column(String(50))
25     parent_id = Column(Integer(), ForeignKey('parent.id'))

 如今Parent類就定義了one to many 的relationship, Child類沒有定義任何relationship. backref通常用於單邊的relationship,下圖是官方文檔的說明。sql

 

1 child_one = Child(name='purk')
2 parent = Parent(name='Wu', children=[child_one])
3 db.add(parent)
4 db.commit()

 結果以下,在add parent時,關聯的child 也 add 到數據庫了。數據庫

 

如今分開操做,先add child,在add parentsession

 

1 child_one =  Child(name='purk')
2 db.add(child_one)
3 db.commit()

 結果以下,parent_id是null值app

 

在執行以下代碼spa

1 parent = Parent(name='Wu', children=[child_one])
2 db.add(parent)
3 db.commit()

 結果以下,打開echo,能夠看到SQL是先insert parent,而後update child.3d

 

2、many to one

直接上代碼,如今Child對Parent就是many to one的關係code

 1 class Parent(Base):
 2     __tablename__ = 'parent'
 3 
 4     id = Column(Integer(), primary_key=True)
 5     name = Column(String(50))
 6     children = relationship('Child', back_populates='parent')
 7 
 8 
 9 class Child(Base):
10     __tablename__ = 'child'
11 
12     id = Column(Integer(), primary_key=True)
13     name = Column(String(50))
14     parent_id = Column(Integer(), ForeignKey('parent.id'), nullable=True)
15     parent = relationship('Parent', back_populates='children')

 

 經過relationship處理的類,能夠直接用屬性的方式訪問到關聯的父級或子級orm

 1 from sqlalchemy.orm import sessionmaker
 2 
 3 Session = sessionmaker(bind=engine)
 4 
 5 db = Session()
 6 
 7 child_one = Child(name='purk')
 8 parent = Parent(name='Wu', children=[child_one])
 9 db.add(parent)
10 db.commit()
11 
12 child = db.query(Child).get(1)
13 print(child.parent.name)

  結果以下對象

3、one to one

 只需給relationship的uselist賦值爲False, parent 對child就成了one to one的關係,只要保證many 的那一方加上uselist=False就能夠了。

 1 class Parent(Base):
 2     __tablename__ = 'parent'
 3 
 4     id = Column(Integer(), primary_key=True)
 5     name = Column(String(50))
 6     child = relationship('Child', back_populates='parent', uselist=False)
 7 
 8 
 9 class Child(Base):
10     __tablename__ = 'child'
11 
12     id = Column(Integer(), primary_key=True)
13     name = Column(String(50))
14     parent_id = Column(Integer(), ForeignKey('parent.id'), nullable=True)
15     parent = relationship('Parent', back_populates='child')

 

 以前的方式把child繼續當成一個list就會報錯了

不當成list,

4、many to many

 方式一: 能夠看出many to many 會有一箇中間表,經過secondary指定,這個中間表是不須要維護的,作增刪改時自動維護了,可是此中間表就沒有有價值的字段,若是中間表須要增長一些除了外鍵的字段,就是下面的方                式二

 1 from sqlalchemy import create_engine
 2 
 3 engine = create_engine("mysql+pymysql://root:max123@127.0.0.1/test?charset=utf8", echo=False)
 4 
 5 from sqlalchemy import *
 6 from sqlalchemy.orm import relationship
 7 from sqlalchemy.ext.declarative import declarative_base
 8 from datetime import datetime, date
 9 
10 Base = declarative_base()
11 
12 
13 class ParentChild(Base):
14     __tablename__ = 'parent_child'
15 
16     id = Column(Integer(), primary_key=True)
17     child_id = Column(Integer(), ForeignKey('child.id'), nullable=False)
18     parent_id = Column(Integer(), ForeignKey('parent.id'), nullable=False)
19 
20 
21 class Parent(Base):
22     __tablename__ = 'parent'
23 
24     id = Column(Integer(), primary_key=True)
25     name = Column(String(50))
26     children = relationship('Child', back_populates='parents', secondary=ParentChild.__table__)
27 
28 
29 class Child(Base):
30     __tablename__ = 'child'
31 
32     id = Column(Integer(), primary_key=True)
33     name = Column(String(50))
34     parents = relationship('Parent', back_populates='children', secondary=ParentChild.__table__)
35 
36 Base.metadata.drop_all(engine)
37 Base.metadata.create_all(engine)
38 
39 
40 from sqlalchemy.orm import sessionmaker
41 
42 Session = sessionmaker(bind=engine)
43 
44 db = Session()
45 
46 child_one = Child(name='purk1')
47 child_two = Child(name='purk2')
48 child_three = Child(name='purk3')
49 child_four = Child(name='purk4')
50 parent_one = Parent(name='Wu1')
51 parent_two = Parent(name='Wu2')
52 parent_one.children.extend([child_one, child_two])
53 parent_two.children.extend([child_two, child_three])
54 child_four.parents.extend([parent_one, parent_two])
55 db.add_all([parent_one, parent_two, child_four])
56 db.commit()

 

結果以下

加上以下的delete操做代碼

1 parent_one.children.remove(child_one)
2 db.merge(parent_one)
3 db.commit()
4 db.delete(child_three)
5 db.commit()

 

 結果以下

方式二:many to one 而後 one to many,這樣就能夠把中間表利用起來了

 

 1 from sqlalchemy import create_engine
 2 
 3 engine = create_engine("mysql+pymysql://root:max123@127.0.0.1/test?charset=utf8", echo=False)
 4 
 5 from sqlalchemy import *
 6 from sqlalchemy.orm import relationship
 7 from sqlalchemy.ext.declarative import declarative_base
 8 from datetime import datetime, date
 9 
10 Base = declarative_base()
11 
12 
13 class Parent(Base):
14     __tablename__ = 'parent'
15 
16     id = Column(Integer(), primary_key=True)
17     name = Column(String(50))
18     children = relationship('ParentChild', back_populates='parent')
19 
20 
21 class Child(Base):
22     __tablename__ = 'child'
23 
24     id = Column(Integer(), primary_key=True)
25     name = Column(String(50))
26     parents = relationship('ParentChild', back_populates='child')
27 
28 
29 class ParentChild(Base):
30     __tablename__ = 'parent_child'
31 
32     id = Column(Integer(), primary_key=True)
33     child_id = Column(Integer(), ForeignKey('child.id'), nullable=False)
34     parent_id = Column(Integer(), ForeignKey('parent.id'), nullable=False)
35     description = Column(String(100))
36 
37     parent = relationship('Parent',back_populates='children')
38     child = relationship('Child',back_populates='parents')
39 
40 
41 Base.metadata.drop_all(engine)
42 Base.metadata.create_all(engine)
43 
44 
45 from sqlalchemy.orm import sessionmaker
46 
47 Session = sessionmaker(bind=engine)
48 
49 db = Session()
50 
51 child_one = Child(name='purk1')
52 child_two = Child(name='purk2')
53 child_three = Child(name='purk3')
54 child_four = Child(name='purk4')
55 parent_one = Parent(name='Wu1')
56 parent_two = Parent(name='Wu2')
57 parent_child_one = ParentChild(description='association one')
58 parent_child_two = ParentChild(description='association two')
59 parent_child_one.child = child_one
60 parent_child_two.child = child_two
61 parent_one.children.extend([parent_child_one, parent_child_two])
62 
63 db.add_all([parent_one, parent_two, child_four])
64 db.commit()

 

 結果以下

加入以下代碼

1 parent_one.children.pop(0)
2 # parent_one.children[1].append(child_three)
3 db.merge(parent_one)
4 db.commit()

 報錯了以下,固然前面定義中間表的時候定義了nullable= False, 但是SQL要強行把起更新爲Null,確定報錯咯。雖然去掉nullable=False的限制,可是這可能也不是我想要的,我想要的是直接刪掉這條記錄的。

 

 想到就去作,給relationship加上cascade='all,delete-orphan', cascade官方解釋以下

2.5.3 Cascades
Mappers support the concept of configurable cascade behavior on relationship() constructs. This refers to how
operations performed on a 「parent」 object relative to a particular Session should be propagated to items referred to
by that relationship (e.g. 「child」 objects), and is affected by the relationship.cascade option.
The default behavior of cascade is limited to cascades of the so-called save-update and merge settings. The typical
「alternative」 setting for cascade is to add thedeleteand delete-orphanoptions; these settings are appropriate for related
objects which only exist as long as they are attached to their parent, and are otherwise deleted.
Cascade behavior is configured using the by changing the cascade option on relationship():

1 class Order(Base):
2 __tablename__ = 'order'
3 items = relationship("Item", cascade="all, delete-orphan")
4 customer = relationship("User", cascade="save-update")

 


To set cascades on a backref, the same flag can be used with the backref() function, which ultimately feeds its
arguments back into relationship():
1class Item(Base):

2 __tablename__ = 'item'
3 order = relationship("Order",
4 backref=backref("items", cascade="all, delete-orphan")
5 )

 

 加上cascade之後,在刪除children時,就會把parent_child表裏面的記錄刪掉了。

1 class Parent(Base):
2     __tablename__ = 'parent'
3 
4     id = Column(Integer(), primary_key=True)
5     name = Column(String(50))
6     children = relationship('ParentChild', back_populates='parent',cascade='all,delete-orphan')

 

 結果以下

 backref與relationship同功能的屬性有 uselist和cascade。

lazy ='select' :

     1) select: lazy加載模式,只在真正調用這個屬性時纔會去訪問數據庫

 

1 class Parent(Base):
2     __tablename__ = 'parent'
3 
4     id = Column(Integer(), primary_key=True)
5     name = Column(String(50))
6     children = relationship('ParentChild', back_populates='parent', cascade='all,delete-orphan', lazy='select')
7 
8 parent = db.query(Parent).get(1)
9 db.close()

 結果是沒有去請求parent_child的數據

1 parent = db.query(Parent).get(1)
2 parent.children
3 db.close()

 結果以下,對比這兩個結果,能夠看出,parent.children在執行的時候纔去請求的數據。

    2) immediate: 直接加載,隨父級以子查詢的方式一同訪問數據庫。

 

1 parent = db.query(Parent).get(1)
2 # parent.children
3 db.close()

 結果是,在parent加載的時候就已經加載數據了。

    3) joined: 直接加載,隨父級以join的方式一同訪問數據庫,具體是 inner join仍是outer join(left join)就看relationship的innerjoin屬性時True仍是False了。

 

class Parent(Base):
    __tablename__ = 'parent'

    id = Column(Integer(), primary_key=True)
    name = Column(String(50))
    children = relationship('ParentChild', back_populates='parent', cascade='all,delete-orphan', lazy='joined')

parent = db.query(Parent).get(1)
# parent.children
db.close()

 

 

 

  結果能夠看出parent加載數據的時候就join出了children的數據

     

 1 class Parent(Base):
 2     __tablename__ = 'parent'
 3 
 4     id = Column(Integer(), primary_key=True)
 5     name = Column(String(50))
 6     children = relationship('ParentChild', back_populates='parent', cascade='all,delete-orphan', lazy='joined', innerjoin=True)
 7 
 8 parent = db.query(Parent).get(1)
 9 # parent.children
10 db.close()

 

innerjoin參數的做用

  4) subquery: 結果集鏈接查詢的方式跟隨parent一塊兒加載出數據

1 children = relationship('ParentChild', back_populates='parent', cascade='all,delete-orphan', lazy='subquery')
2 
3 parent = db.query(Parent).get(1)
4 # parent.children
5 db.close()

 

  5) noload:no loading should occur at any time. This is to support 「write-only」 attributes,

or attributes which are populated in some manner specific to the application. 官方文檔解釋,感受沒有什麼實際意思。

 6) dynamic: 返回query對象,可一直使用filter等條件過濾。

1 children = relationship('ParentChild', back_populates='parent', cascade='all,delete-orphan', lazy='dynamic')
2 
3 parent = db.query(Parent).get(1)
4 parent.children.all()
5 db.close()

 結果以下,看代碼就明白其意義了。

相關文章
相關標籤/搜索