【问题标题】:Update a sqlalchemy association proxy target when updating the parent ORM table更新父 ORM 表时更新 sqlalchemy 关联代理目标
【发布时间】:2020-03-04 23:55:58
【问题描述】:

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

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

有没有办法通过仅访问Category ORM 映射器来设置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)

【问题讨论】:

    标签: python flask sqlalchemy flask-sqlalchemy


    【解决方案1】:

    您可以尝试使用Hybrid Attributes 并在.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 模型。当然,在这种情况下,您必须验证默认供应商是现有供应商之一。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-23
      • 2022-08-24
      • 1970-01-01
      • 2020-06-28
      • 1970-01-01
      • 1970-01-01
      • 2021-03-06
      相关资源
      最近更新 更多