参考
建表 Many to Many
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 association_table = Table('association' , Base.metadata, Column('left_id' , Integer, ForeignKey('left.id' )), Column('right_id' , Integer, ForeignKey('right.id' )) ) class Parent (Base ): __tablename__ = 'left' id = Column(Integer, primary_key=True ) children = relationship( "Child" , secondary=association_table, back_populates="parents" ) class Child (Base ): __tablename__ = 'right' id = Column(Integer, primary_key=True ) parents = relationship( "Parent" , secondary=association_table, back_populates="children" )
多对多需要一个联系表association_table , 这张联系表其实是一张虚拟表 .
secondary: 多对多指定中间表关键字
backref: 在一对多或多对一之间建立双向关系
children = relationship("Child",secondary=association_table,back_populates="parents")
: Parent表的children字段通过association_table联系表 , 链接到Child表的parents字段
这样,Parent表就可以通过Parent.children.id
获取到孩子的id了
同理parents = relationship("Parent",secondary=association_table,back_populates="children")
: Child表的parents字段通过association_table联系表 , 链接到Parent表的children字段. 这样,Child表就可以通过Child.parents.id
获取到父母的id了
结论 : children = relationship("Child",secondary=association_table,back_populates="parents")
的直接作用就是说 : 使得Child表成为Parent表的children字段 , 也就是说,通过Parent表的children字段就可以获取到Child表
当使用backref
参数代替时 relationship.back_populates
,backref将自动secondary
为反向关系使用相同的参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 association_table = Table('association' , Base.metadata, Column('left_id' , Integer, ForeignKey('left.id' )), Column('right_id' , Integer, ForeignKey('right.id' )) ) class Parent (Base ): __tablename__ = 'left' id = Column(Integer, primary_key=True ) children = relationship("Child" , secondary=association_table, backref="parents" ) class Child (Base ): __tablename__ = 'right' id = Column(Integer, primary_key=True )
One to Many 表示一对多的关系时,在子表类中通过 foreign key引用父表类。然后,在父表类中通过 relationship() 方法来引用子表的类:
1 2 3 4 5 6 7 8 9 10 11 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) children = relationship("Child" ) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('parent.id' ))
Parent表通过Parent.children
获取Child表
Child表通过Child.parent_id
获取Parent表的children字段
relationship
和 foreign key
总结:
父表类中通过 relationship() 方法来引用子表的类集合
子表类中通过 foreign key引用父表的参考字段
也就是说 , 外键就只是约束
而已 ,relationship可以获取子表的集合
Many to One 在一对多的关系中建立双向的关系,这样的话在对方看来这就是一个多对一的关系, 在子表类中附加一个relationship()
方法,并且在双方的relationship()
方法中使用relationship.back_populates
方法参数
1 2 3 4 5 6 7 8 9 10 11 12 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) children = relationship("Child" , back_populates="parent" ) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('parent.id' )) parent = relationship("Parent" , back_populates="children" )
这样的话子表将会在多对一的关系中获得父表的属性
或者,可以在单一的relationship()
方法中使用backref
参数来代替back_populates
参数, 推荐使用这种方式,可以少些几句话。
1 2 3 4 5 6 7 8 9 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) children = relationship("Child" , backref="parent" ) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('parent.id' ))
使用backref , 对方就不必再写一个relationship了
One to One 一对一就是多对一和一对多的一个特例,只需在relationship加上一个参数uselist=False替换多的一端就是一对一
从一对多转换到一对一 :
1 2 3 4 5 6 7 8 9 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) child = relationship("Child" , uselist=False , backref="parent" ) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('parent.id' ))
从多对一转换到一对一 :
1 2 3 4 5 6 7 8 9 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) child_id = Column(Integer, ForeignKey('child.id' )) child = relationship("Child" , backref=backref("parent" , uselist=False )) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True )
对象在session中的四种状态
Transient:实例还不在session中,还没有保存到数据库中去,没有数据库身份,想刚创建出来的对象比如User(),仅仅只有mapper()与之关联
Pending:用add()一个transient对象后,就变成了一个pending对象,这时候仍然没有flushed到数据库中去,直到flush发生。
Persistent:实例出现在session中而且在数据库中也有记录了,通常是通过flush一个pending实例变成Persistent或者从数据库中querying一个已经存在的实例。
Detached:一个对象它有记录在数据库中,但是不在任何session中,
关联查询 外键查询
1 session.query(User).join(Address).filter (Address.email=='lzjun@qq.com' ).all ()
指定ON字段:
1 q = session.query(User).join(Address, User.id ==Address.user_id)
多个join :
1 2 q = session.query(User).join("orders" , "items" , "keywords" ) q = session.query(User).join(User.orders).join(Order.items).join(Item.keywords)
子查询JOIN:
1 2 address_subq = session.query(Address).filter (Address.email_address == 'ed@foo.com' ).subquery() q = session.query(User).join(address_subq, User.addresses)
join from:
1 q = session.query(Address).select_from(User).join(User.addresses).filter (User.name == 'ed' )
和下面的SQL等价:
1 2 3 SELECT address.* FROM user JOIN address ON user.id= address.user_id WHERE user.name = :name_1
左外连接,指定isouter=True
,等价于Query.outerjoin()
:
1 q = session.query(Node).join("children" , "children" , aliased=True , isouter=True ).filter (Node.name == 'grandchild 1' )
back_populates参数的作用 back_populates , 顾名思义, 就是向后填充数据的意思
简单来说 : 就是提供双向绑定 , 当一方修改的时候通知另一方进行修改
1 2 3 4 5 6 7 8 9 10 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) children = relationship("Child" ) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('parent.id' )) parent = relationship("Parent" )
如果没有提供back_populates参数 , 在修改Child实例的parent属性后 , 并不会修改Parent实例的child属性
1 2 3 4 5 >>> parent = Parent() >>> child = Child() >>> child.parent = parent >>> print parent.children []
现在看看添加了back_populates参数 :
1 2 3 4 5 6 7 8 9 10 class Parent (Base ): __tablename__ = 'parent' id = Column(Integer, primary_key=True ) children = relationship("Child" , back_populates="parent" ) class Child (Base ): __tablename__ = 'child' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('parent.id' )) parent = relationship("Parent" , back_populates="children" )
1 2 3 4 5 >>> parent = Parent() >>> child = Child() >>> child.parent = parent >>> print parent.children [Child(...)]
也就是说 , back_populates的值一定是另一个Class的属性值
1 2 3 4 5 class Class1 : attr1 = relationship("class2" , back_populates="attr2" ) class Class2 : attr2 = relationship("class1" , back_populates="attr1" )
relationship的参数backref和back_populates的区别
有关backref和back_populates的作用,都是在一对多或多对多查询的时候。设置一个值,这个值用来从一个表格对象指向到另一个对象 ,只是用法稍微有点差别。
backref是新式的方法,back_populates是老式的方法,但是目前都可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from flask_sqlalchemy import SQLAlchemyfrom APP.app import appdb = SQLAlchemy(app) association_table = db.Table('association' , db.Column('id' , db.Integer, primary_key=True , autoincrement=True ), db.Column('customer_id' , db.Integer, db.ForeignKey('customer.id' )), db.Column('product_id' , db.Integer, db.ForeignKey('product.id' )) ) class Customer (db.Model ): __tablename__ = 'customer' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(10 )) work = db.Column(db.String(20 )) customer_to_product = db.relationship('Product' , secondary=association_table, back_populates='product_to_customer' ) def __repr__ (self ): return 'name:{name} work:{work}' .format (name=self.name, work=self.work) class Product (db.Model ): __tablename__ = 'product' id = db.Column(db.Integer, primary_key=True , autoincrement=True ) name = db.Column(db.String(10 )) price = db.Column(db.Float) product_to_customer = db.relationship('Customer' , secondary=association_table, back_populates='customer_to_product' ) def __repr__ (self ): return 'name:{name} price:{price}' .format (name=self.name, price=self.price) key = '司马老板' ct = Customer.query.filter_by(name=key).first() print (ct, type (ct))p = ct.customer_to_product print (p, type (p))for i in p: print (i.name, i.price)
在多对多的关系中,需要在2个表格里面,如果使用back_populates,需要2个类里面同时使用关系指向。
2个类里面的关系指向,字段名称一致,例如:
1 2 3 4 5 6 7 customer_to_product = db.relationship('Product' , secondary=association_table, back_populates='product_to_customer' ) product_to_customer = db.relationship('Customer' , secondary=association_table, back_populates='customer_to_product' )
remote_side : 外键引用的是自身 remote_side
: 表中的外键引用的是自身时,如Node类,如果想表示多对一的关系,那么就可以使用remote_side
1 2 3 4 5 6 class Node (Base ): __tablename__ = 'node' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('node.id' )) data = Column(String(50 )) parent = relationship("Node" , remote_side=[id ])
如果是想建立一种双向的关系,那么还是结合backref:
1 2 3 4 5 6 7 8 9 class Node (Base ): __tablename__ = 'node' id = Column(Integer, primary_key=True ) parent_id = Column(Integer, ForeignKey('node.id' )) data = Column(String(50 )) children = relationship( "Node" , backref=backref('parent' , remote_side=[id ]) )