【问题标题】:If a child object has an attribute=True then update all others to attribute=False如果子对象的属性=True,则将所有其他对象更新为属性=False
【发布时间】:2021-02-03 12:08:38
【问题描述】:

我有以下型号:

class AddressOfUser(AddressMixin, BaseModel):

    __tablename__ = 'adrusr_addressofuser'

    user = db.relationship('User', backref='addresses')
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    default = db.Column('adrusr_default', db.Boolean, default=False)
    first_name = db.Column('adrusr_first_name', db.String)
    last_name = db.Column('adrusr_last_name', db.String)
    phone = db.Column('adrusr_phone', db.String)

因此,当用户创建地址时,他们可以使用用于创建地址的表单中的复选框将default 属性设置为 True,从而将该地址设置为默认值。

问题是用户可以将多个地址设置为默认地址,这是一个问题,因为我们无法知道将哪个地址用作默认地址,所以如果用户注册的最新地址,我该怎么做设置为默认值,因此其余地址将不再是默认值?换句话说,如果 的最新元素具有属性default = True,那么之前的地址应该具有default = False

【问题讨论】:

    标签: flask sqlalchemy flask-sqlalchemy


    【解决方案1】:

    您可以使用“after_insert”和“after_update”事件更新兄弟对象。在以下示例中,如果使用is_default=True 插入或更新地址,则事件处理程序将强制该特定用户的所有其他地址条目为is_default=False

    # https://stackoverflow.com/q/66027263/2144390
    import sqlalchemy as db
    from sqlalchemy import event
    from sqlalchemy.orm import declarative_base, relationship
    
    connection_uri = "sqlite://"
    engine = db.create_engine(
        connection_uri,
        future=True,
        echo=True,
    )
    
    Base = declarative_base()
    
    
    class Address(Base):
        __tablename__ = "address"
        id = db.Column(db.Integer, primary_key=True)
        user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
        address = db.Column(db.String, nullable=False)
        is_default = db.Column(db.Boolean, nullable=False, default=False)
        user = relationship("User", back_populates="addresses")
    
        def __repr__(self):
            return (
                f"<Address(user={self.user}, address='{self.address}'"
                f", is_default={self.is_default})>"
            )
    
    
    class User(Base):
        __tablename__ = "user"
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        addresses = relationship("Address", back_populates="user")
    
        def __repr__(self):
            return f"<User(id={self.id}, name='{self.name}')>"
    
    
    Base.metadata.create_all(engine)
    
    
    def _remove_other_default_addrs(mapper, connection, target):
        if target.is_default:
            session = db.inspect(target).session
            session.query(Address).filter(
                Address.user_id == target.user_id
            ).filter(Address.id != target.id).filter(
                Address.is_default == True
            ).update(
                values={"is_default": False}, synchronize_session="evaluate"
            )
    
    
    @event.listens_for(Address, "after_insert")
    def receive_after_insert(mapper, connection, target):
        _remove_other_default_addrs(mapper, connection, target)
    
    
    @event.listens_for(Address, "after_update")
    def receive_after_update(mapper, connection, target):
        _remove_other_default_addrs(mapper, connection, target)
    
    
    with db.orm.Session(engine, future=True) as session:
        gord = User(name="Gord")
        gord_old_addr = Address(user=gord, address="123 Old Ave", is_default=True)
        session.add_all([gord, gord_old_addr])
        session.commit()
        print(f">>> gord_old_addr.is_default is {gord_old_addr.is_default}")
        # >>> gord_old_addr.is_default is True
    
        gord_new_addr = Address(user=gord, address="567 New Blvd", is_default=True)
        session.add(gord_new_addr)
        session.flush()
        print(">>> session flushed")
        print(f">>> gord_old_addr.is_default is {gord_old_addr.is_default}")
        # >>> gord_old_addr.is_default is False
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-01
      • 1970-01-01
      • 2018-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多