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

 结果以下,看代码就明白其意义了。

相关文章
相关标签/搜索