覆盖sqlalchemy中的关系行为 [英] override relationship behaviour in sqlalchemy
问题描述
假设我以声明方式提供了三个表Parent
,Child
和Pet
,
Say I have three tables in a declarative fashion, Parent
, Child
, and Pet
, in such way that
-
Parent
与Child
和Pet
都具有多对多关系
-
Child
与Pet
有一对多关系
Parent
has a many-to-many relationship with bothChild
andPet
Child
has a one-to-many relationship withPet
它们的代码是(使用Flask-SQLAlchemy,尽管我认为解决方案存在于SQLAlchemy领域中,而不是在Flask中).
The code for them is (using Flask-SQLAlchemy, although I believe the solution lives in the realm of SQLAlchemy rather than in Flask).
class Parent(db.Model):
__tablename__ = 'parents'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
# many to many relationship between parent and children
# my case allows for a children to have many parents. Don't ask.
children = db.relationship('Child',
secondary=parents_children_relationship,
backref=db.backref('parents', lazy='dynamic'),
lazy='dynamic')
# many to many relationship between parents and pets
pets = db.relationship('Pet',
secondary=users_pets_relationship,
backref=db.backref('parents', lazy='dynamic'), #
lazy='dynamic')
# many to many relationship between parents and children
parents_children_relationship = db.Table('parents_children_relationship',
db.Column('parent_id', db.Integer, db.ForeignKey('parents.id')),
db.Column('child_id', db.Integer, db.ForeignKey('children.id')),
UniqueConstraint('parent_id', 'child_id'))
# many to many relationship between User and Pet
users_pets_relationship = db.Table('users_pets_relationship',
db.Column('parent_id', db.Integer, db.ForeignKey('parents.id')),
db.Column('pet_id', db.Integer, db.ForeignKey('pets.id')),
UniqueConstraint('parent_id', 'pet_id'))
class Child(db.Model):
__tablename__ = 'children'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
# parents = <backref relationship with User model>
# one to many relationship with pets
pets = db.relationship('Pet', backref='child', lazy='dynamic')
class Pet(db.Model):
__tablename__ = 'pets'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
# child = backref relationship with cities
child_id = db.Column(db.Integer, db.ForeignKey('children.id'), nullable=True)
# parents = <relationship backref from User>
我想做这样的事情
parent_a = Parent()
child_a = Child()
pet_a = Pet()
然后我就可以这样做
parent_a.children.append(child_a)
# commit/persist data
parent_a.children.all() # [child_a]
我想实现这样的目标
child_a.pets.append(pet_a)
parent_a.children.append(child_a)
# commit/persist data
parent_a.children.all() # [child_a]
parent_a.pets.all() # [pet_a], because pet_a gets
# automatically added to parent using some sorcery
# like for child in parent_a.children.all():
# parent.pets.append(child.pets.all())
# or something like that.
我可以使用Parent
对象中的方法(例如add_child_and_its_pets()
)来实现此目的,但是我想覆盖关系的工作方式,因此不需要覆盖可能从此行为中受益的其他模块,例如例如Flask-Admin
.
I can achieve this with a method in the Parent
object like add_child_and_its_pets()
, but I would like to override the way relationship works, so I don't need to override other modules that may benefit from this behaviour, like Flask-Admin
for instance.
基本上我应该如何覆盖backref.append
方法或relationship.append
方法,以便在调用时(即在python端)附加来自其他关系的其他对象?我还应该如何覆盖remove
方法?
Basically how should I override the backref.append
method or the relationship.append
method to also append other objects from other relationships at call time i.e. on the python side? How should I override the remove
methods as well?
推荐答案
使用来实现事件监听器,该监听器在第一个参数中的对象上调用append
或remove
之前被调用.
Using the same answer from the sqlalchemy mailing list, this can be achieved using event listeners, which are called before an append
or remove
is called on the object in the first parameter.
@db.event.listens_for(Parent.children, 'append')
def _append_children(parent, child, initiator):
# appends also the pets bound to the child that the
# is being appended to the Parent
parent.pets.extend(child.pets.all())
# at the end of this call, executes
# parent.children.append(child)
这篇关于覆盖sqlalchemy中的关系行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!