繁体   English   中英

更新父 ORM 表时更新 sqlalchemy 关联代理目标

[英]Update a sqlalchemy association proxy target when updating the parent ORM table

我有一个 Flask SQLAlchemy 项目,其中包含 3 个 ORM 表、Category、Vendor 和 CategoryVendorMap,我正在使用 Flask-Marshmallow 为 api 定义模式。

关系:通过 CategoryVendorMap,Category 有很多 Vendor。 CategoryVendorMap 也有一个is_default标志,指示类别的默认供应商。 is_default字段也已非规范化到 Category 表中。

有没有办法通过只访问Category ORM Mapper 来设置CategoryVendorMap对象的is_default值? 基本上我想设置Category.default_vendor_id字段并让这个字段的设置器更新适当的CategoryVendorMap关联。我知道我可以直接更新CategoryVendorMap对象,但是最终我的模型是通过 api 公开的,我想避免暴露这个对象,并创建自定义控制器逻辑来处理这个用例。

class CategoryVendorMap(db.Model):
    __tablename__ = "CategoryVendorMap"
    __table_args__ = (
      db.PrimaryKeyConstraint('category_id', 'vendor_id'),
    )
    category_id = db.Column(db.Integer, db.ForeignKey('Category.category_id'))
    vendor_id = db.Column(db.Integer, db.ForeignKey('Vendor.vendor_id'))
    is_default = db.Column(db.Boolean, default=False)

    category = db.relationship('Category', backref=db.backref("category_vendors", cascade="all, delete-orphan"))
    vendor = db.relationship('Vendor')


class Category(db.Model):
    __tablename__ = 'Category'

    @property    
    def default_vendor_id(self):
        if self.category_vendors:
            default_vendor_id = [cat_vendor_map for cat_vendor_map in self.category_vendors if cat_vendor_map.is_default]
            return default_vendor_id[0].vendor_id if default_vendor_id else None
        return

    @default_vendor_id.setter
    def default_vendor_id(self, vendor_id):
      return int(vendor_id)

    category_id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), unique=False, nullable=False)    

    vendors = association_proxy("category_vendors", "vendor", 
      creator=lambda vendor: CategoryVendorMap(vendor=vendor))

class Vendor(db.Model):
    __tablename__ = 'Vendor'

    vendor_id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, nullable=False)

您可以尝试使用混合属性并在.setter使用自定义逻辑:

@hybrid_property
def default_vendor_id(self):
    res = [_cv for _cv in self.category_vendors if _cv.is_default]
    return res and res[0].vendor_id or None

@default_vendor_id.setter
def default_vendor_id(self, value):
    # @note: need thorough testing in case a new vendor is added and set to be a default
    # also new objects will not have the `vendor_id` set yet
    for _cv in self.category_vendors:
        _cv.is_default = bool(_cv.vendor_id == value)

但是,需要很好地测试 setter 以涵盖极端情况,其中一些在评论中提到。

您还可以考虑在CategoryVendorMap表上创建检查约束(或唯一过滤索引),以确保每个类别最多只有一个is_default真值。


另一个要考虑的选择是将is_default标志从关系表中直接移动到Category模型。 在这种情况下,当然,您必须验证默认供应商是现有供应商之一。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM