gpt4 book ai didi

python - flask SQLAlchemy : How to add a column that depends on another table?

转载 作者:太空宇宙 更新时间:2023-11-03 11:40:11 26 4
gpt4 key购买 nike

我的 SQLAlchemy 数据库中有三个表(使用 Flask SQLAlchemy):产品、产品变体和订单。我想查看订单中包含的产品及其变体。

它适用于关系/外键,但主要问题是:如果我将产品添加到订单中,我仍然可以添加另一个产品的变体(使用 Flask-Admin,或只是 flask shell)。

那么,主要问题:如何在表之间创建连接,以便仅当它们是订单产品的变体时才能添加变体?谢谢:)

另一种解决方案:如何向 Orders 表添加列,以便它根据变体 ID 从 Product 表中获取产品名称?我尝试使用 column_propertyPost.query.get(variation_id)、variation.parent_id、backhref variation.origin_product 但没有任何成功:)

我的模型:

产品(如三星 Galaxy 7)

class Product(db.Model):

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True)
brand = db.Column(db.String(120))
variations = db.relationship('Variation', backref='origin_product', lazy='dynamic')
orders = db.relationship('Order', backref='product_in_order', lazy='dynamic')

产品差异(如 Samsung Galaxy 7 Blue 32GB)

class Variation(db.Model):

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True)
price = db.Column(db.Integer)
product_id = db.Column(db.Integer, db.ForeignKey('product.id'))
orders = db.relationship('Order', backref='variation_in_order', lazy='dynamic')

顺序

class Order(db.Model):

id = db.Column(db.Integer, primary_key=True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
variation_id = db.Column(db.Integer, db.ForeignKey('variation.id'))
product_id = db.Column(db.Integer, db.ForeignKey('product.id'))

P.S. product_id = db.Column(db.Integer, db.ForeignKey('variation.product_id')) 在数据库中工作,我看到正确的产品 ID。 Flask-Admin 等外部工具仍然将 product_id 列视为变体对象,因此没有用。需要一种方法,将产品对象连接到 product_id。例如,连接产品 ForeignKey,但基于 variation_id

最佳答案

防止不相关的产品和变体组合的一种方法是创建一个从订单到产品的外键和一个 overlapping composite foreign key从顺序到变化。为了能够引用 variation.id, variation.product_id 的组合,产品 id 也应该成为主键的一部分,并且 id 必须明确指定自动递增行为:

class Variation(db.Model):

id = db.Column(db.Integer, primary_key=True, autoincrement=True)
product_id = db.Column(db.Integer, db.ForeignKey('product.id'),
primary_key=True)


class Order(db.Model):

product_id = db.Column(db.Integer, nullable=False)
variation_id = db.Column(db.Integer)

__table_args__ = (
db.ForeignKeyConstraint([product_id], ['product.id']),
db.ForeignKeyConstraint([product_id, variation_id],
['variation.product_id', 'variation.id']),
)

由于外键默认为 MATCH SIMPLE,变体的复合外键将允许添加变体 ID 为 NULL 的行,但如果给定变体 ID,则组合必须引用现有行。此设置允许将现有关系 product_in_ordervariation_in_order 分别用于 ProductVariation 而不是更复杂的模型下面,尽管 SQLAlchemy 会(正确地)警告这些关系存在冲突,因为它们都设置了产品 ID。创建订单时只需使用其中之一:

In [24]: o1 = Order(product_in_order=product)

In [25]: o2 = Order(variation_in_order=variation)

或关注documentation about resolving the conflict .在此模型中,产品名称始终可用为

In [31]: o1.product_in_order.name

另一种防止在给定产品时向订单添加不相关变体的方法是在这种情况下完全避免添加变体,反之亦然:

class Order(db.Model):
...
variation_id = db.Column(db.Integer, db.ForeignKey('variation.id'))
product_id = db.Column(db.Integer, db.ForeignKey('product.id'))

__table_args__ = (
# Require either a variation or a product
db.CheckConstraint(
'(variation_id IS NOT NULL AND product_id IS NULL) OR '
'(variation_id IS NULL AND product_id IS NOT NULL)'),
)

建立与 Product 的关系在此模型中有点复杂,需要 using a non primary mapper :

product_variation = db.outerjoin(
Product, db.select([Variation.id,
Variation.product_id]).alias('variation'))

ProductVariation = db.mapper(
Product, product_variation, non_primary=True,
properties={
'id': [product_variation.c.product_id,
product_variation.c.variation_product_id],
'variation_id': product_variation.c.variation_id
})

连接产生的可选对象映射回 Product,但也允许基于 Variation.id 进行选择:

Order.product = db.relationship(
ProductVariation,
primaryjoin=db.or_(Order.product_id == ProductVariation.c.id,
Order.variation_id == ProductVariation.c.variation_id))

通过这种方式,您可以使用 Order 实例访问产品名称

order.product.name

演示:

In [2]: p1 = Product(name='Product 1')

In [3]: v11 = Variation(product=p1)

In [4]: v12 = Variation(product=p1)

In [5]: p2 = Product(name='Product 2')

In [6]: v21 = Variation(product=p2)

In [9]: session.add_all([p1, p2])

In [10]: session.add_all([v11, v12, v21])

In [11]: session.commit()

In [12]: o1 = Order(product_id=p1.id)

In [13]: o2 = Order(variation_id=v12.id)

In [14]: o3 = Order(variation_id=v11.id)

In [15]: o4 = Order(product_id=p2.id)

In [16]: o5 = Order(variation_id=v21.id)

In [17]: session.add_all([o1, o2, o3, o4, o5])

In [18]: session.commit()

In [25]: [o.product.name for o in session.query(Order).all()]
Out[25]: ['Product 1', 'Product 1', 'Product 1', 'Product 2', 'Product 2']

LEFT JOIN 确保没有变体的产品也能正常工作:

In [26]: p3 = Product(name='Product 3')

In [27]: session.add(p3)

In [28]: session.commit()

In [29]: session.add(Order(product_id=p3.id))

In [30]: session.commit()

In [31]: [o.product.name for o in session.query(Order).all()]
Out[31]: ['Product 1', 'Product 1', 'Product 1', 'Product 2', 'Product 2', 'Product 3']

另一方面,除了这种相当复杂的结构,您还可以使用所描述的 CheckConstraint 和一个普通的 property:

class Order(db.Model):
...
@property
def product(self):
if self.product_in_order:
return self.product_in_order

else:
return self.variation_in_order.origin_product

请注意,如果没有预先加载,这将在变化顺序的情况下对数据库触发 2 个单独的 SELECT 查询。

关于python - flask SQLAlchemy : How to add a column that depends on another table?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51089777/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com