覆盖sqlalchemy中的关系行为 [英] override relationship behaviour in sqlalchemy

查看:154
本文介绍了覆盖sqlalchemy中的关系行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我以声明方式提供了三个表ParentChildPet

Say I have three tables in a declarative fashion, Parent, Child, and Pet, in such way that

  • ParentChildPet
  • 都具有多对多关系
  • ChildPet
  • 有一对多关系
  • Parent has a many-to-many relationship with both Child and Pet
  • Child has a one-to-many relationship with Pet

它们的代码是(使用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?

推荐答案

使用来实现事件监听器,该监听器在第一个参数中的对象上调用appendremove之前被调用.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆