【问题标题】:override relationship behaviour in sqlalchemy覆盖sqlalchemy中的关系行为
【发布时间】:2018-10-06 11:13:45
【问题描述】:

假设我有三个声明方式的表,ParentChildPet,这样

  • ParentChildPet 具有多对多关系
  • ChildPet 具有一对多关系

它们的代码是(使用 Flask-SQLAlchemy,尽管我相信解决方案存在于 SQLAlchemy 领域而不是 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

基本上我应该如何覆盖backref.append 方法或relationship.append 方法,以便在调用时(即在python 端)附加来自其他关系的其他对象?我应该如何覆盖remove 方法?

【问题讨论】:

    标签: python python-3.x sqlalchemy flask-sqlalchemy relationships


    【解决方案1】:

    对于parent.pets.all(),我认为您可以将儿童用作secondary join 条件,并将其视为associative entity or junction table

    这取决于您的表格,但看起来像:

    Parent.pets = relationship(
        Pet,
        backref='parent'
        primaryjoin=Pet.child_id == Child.id,
        secondaryjoin=Child.parent_id == Parent.id
    )
    

    如果您愿意,您也可以相当合理地创建一个反向引用 parent - 这样您就可以同时访问 parent_a.petspet_a.parent

    【讨论】:

    • 我正在阅读有关primaryjoin和secondaryjoin的信息。与此同时,“如果你愿意,你也可以相当合理地创建一个 backref 父级”是什么意思。你能举个例子吗?是否有必要也显示我的代码?
    • 添加了 backref 参数以显示它的外观。这有帮助吗?
    【解决方案2】:

    使用来自sqlalchemy mailing list 的相同答案,这可以使用event listeners 来实现,在第一个参数中的对象上调用appendremove 之前调用它们。

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

    【讨论】:

      猜你喜欢
      • 2021-07-26
      • 1970-01-01
      • 2020-02-16
      • 2020-11-22
      • 2014-10-05
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      • 2016-11-23
      相关资源
      最近更新 更多