SQLAlchemy具有多个多对多关系

时间:2018-03-28 18:01:50

标签: sql sqlalchemy flask-sqlalchemy

我想用SQLAlchemy为食谱创建一个数据库,但是我不确定我的方法是否正确。如何将数据插入recipe_ingredient表?

我的方法:

食谱有一个名称,可以有多种成分。一种成分包括量,单位和名称(成分表),例如500毫升水。

表食谱 - id,主键 - 名称 (一个食谱可以有多种成分,一种成分可以有多种食谱)

table recipe_ingredient

  • 外键(recipe.id)
  • 外键(amounts.id)
  • 外键(units.id)
  • 外键(ingredients.id)

表金额

  • id,主键
  • 金额(例如500)

表单位

  • id,主键
  • 单位(例如ml)

表成分

  • id,主键
  • 成分(例如水)

代码:

recipe_ingredient = db.Table('recipe_ingredient',
                        db.Column('idrecipe', db.Integer, db.ForeignKey('recipe.id')),
                        db.Column('idingredient', db.Integer, db.ForeignKey('ingredients.id')),
                        db.Column('idunit', db.Integer, db.ForeignKey('units.id')),
                        db.Column('idamount', db.Integer, db.ForeignKey('amounts.id'))
)

class Recipe(db.Model):
    id= db.Column(db.Integer, primary_key=True, autoincrement=True)
    name= db.Column(db.VARCHAR(70), index=True)
    ingredient= db.relationship("Ingredients", secondary=recipe_ingredient, backref='recipe')
    amounts = db.relationship("Amounts", secondary=recipe_ingredient, backref='recipe')
    units= db.relationship("Units", secondary=recipe_ingredient , backref='recipe')

class Ingredients(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ingredient = db.Column(db.VARCHAR(200))

class Units(db.Model):
    id= db.Column(db.Integer, primary_key=True, autoincrement=True)
    unit= db.Column(db.VARCHAR(45), nullable=False)

class Amounts(db.Model):
    id= db.Column(db.Integer, primary_key=True, autoincrement=True)
    amount= db.Column(db.VARCHAR(45), nullable=False)


更新

class RecipeIngredient(db.Model):
    __tablename__ = 'recipe_ingredient'
    recipe_id = db.Column(db.Integer, db.ForeignKey('recipe.id'), primary_key=True)
    ingredient_id = db.Column(db.Integer, db.ForeignKey('ingredient.id'), primary_key=True)
    amount = db.Column(db.Integer, db.ForeignKey('amount.id'))
    unit = db.Column(db.Integer, db.ForeignKey('unit.id'))
    recipes = relationship("Recipe", back_populates="ingredients")
    ingredients = relationship("Ingredient", back_populates="recipes")


class Recipe(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.VARCHAR(70), index=True)
    ingredients = relationship("RecipeIngredient", back_populates="recipes")


class Ingredient(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.VARCHAR(200))
    recipes = relationship("RecipeIngredient", back_populates="ingredients")


class Unit(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.VARCHAR(45), nullable=False)


class Amount(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    number = db.Column(db.VARCHAR(45), nullable=False)

我添加了以下对象

pizza = Recipe(name='Pizza')
flour = Ingredient(name='Flour')
water = Ingredient(name='Water')
g = Unit(name='g')
ml = Unit(name='ml')
a500 = Amount(number='500')
a100 = Amount(number='100')

r1= RecipeIngredient(recipe_id=pizza.id, ingredient_id=flour.id, amount=a500.id, unit=g.id)
r2= RecipeIngredient(recipe_id=pizza.id, ingredient_id=water.id, amount=a100.id, unit=ml.id)

pizza.ingredients的结果:

[<RecipeIngredient 1, 1>, <RecipeIngredient 1, 3>]

我需要添加到该模型中以获取成分的名称: pizza.ingredients[0].name

1 个答案:

答案 0 :(得分:2)

因此,您的关系定义和多对多recipe_ingredient表格很好。您可以使用您拥有的代码执行您想要的操作。您有一些风格问题会使您的代码难以阅读。

工作原理

让我们先来看看你的功能:

Recipe个对象将具有ingredient属性,其作用类似于列表。您可以向其附加Ingredients个对象,当您调用它时,您将拥有Ingredients的常规Python列表:

# Make a cake, 
cake = Recipe(name='Cake')
flour = Ingredients(ingredient='Flour')
eggs = Ingredients(ingredient='Eggs')
cake.ingredient.append(flour)
cake.ingredient.append(eggs)
for i in cake.ingredient:
    print(i.ingredient)

因为您已经定义了Recipe.amount的{​​{1}}关系,secondary,所以SQLAlchemy拥有管理多对多关系所需的所有信息。

secondary=recipe_ingredient个对象将具有Ingredients属性,其作用类似于列表,并引用相同的关系:

recipe

您甚至可以将配方添加到配料中,而不是将配料添加到配方中,它的工作方式也是如此:

# Find recipes using flour
for r in flour.recipe:
    print(r.name)

如何读取

你可能已经注意到,你命名的方式使它看起来有点笨拙。当任何属性引用a-to-many关系时,使用复数时更清楚。例如,# Make cookies cookies = Recipe(name='Cookies') eggs.recipe.append(cookies) for i in cookies.ingredient: print(i.ingredient) 中的成分关系如果实际上被称为Recipe而不是ingredients,则读起来会更好。这将让我们遍历ingredient。反方向也是如此:调用backref cake.ingredients而不是配方将使recipes引用多个链接的食谱更加清晰,其中flour.recipes可能有点误导。< / p>

关于对象是复数还是单数,也存在不一致。 flour.recipe是单数,但Recipe是复数。老实说,哪些是正确的风格的意见并不普遍 - 我更喜欢对我的所有模型使用单数,IngredientsRecipeIngredientAmount - 但这是只有我。选择一种风格并坚持下去,而不是在它们之间切换。

最后,您的属性名称有点多余。 Unit有点多了。 Ingredients.ingredient更有意义,而且更清晰。

还有一件事

这里还有一件事 - 它让我觉得你想要存储关于你的食谱/配料关系的更多信息,这是一种成分的数量和单位。您可能需要2个鸡蛋或500克面粉,我猜这是您的Ingredient.nameAmounts表的用途。在这种情况下,这些附加信息是您关系的关键部分,而不是尝试在单独的表中进行匹配,您可以直接在关联表中对其进行故事记录。这需要更多的工作 - SQLAlchemy文档更深入地了解如何使用Association object来管理这些额外的数据。我只想说使用这种模式可以使你的代码更清洁,更容易长期管理。

更新

@ user7055220在我看来,您不需要单独的UnitsAmount表,因为这些值仅作为Unit关系的一部分具有意义。我会删除这些表格,并将RecipeIngredient中的amountunit属性更改为直接RecipeIngredientString值,直接存储单位/金额:

Integer

在任何一种情况下,要回答关于如何访问成分值的问题 - 在您的示例中class RecipeIngredient(db.Model): __tablename__ = 'recipe_ingredient' recipe_id = db.Column(db.Integer, db.ForeignKey('recipe.id'), primary_key=True) ingredient_id = db.Column(db.Integer, db.ForeignKey('ingredient.id'), primary_key=True) amount = db.Column(db.Integer) unit = db.Column(db.VARCHAR(45), nullable=False) recipes = relationship("Recipe", back_populates="ingredients") ingredients = relationship("Ingredient", back_populates="recipes") 现在包含一组关联对象。该成分是该关联对象的子成员,可以通过其pizza.ingredients属性进行访问。如果您进行上述建议的更改,则可以直接访问ingredientsunit值。访问该数据将如下所示:

amount

或者,如果您只想使用您询问的语法:

for i in pizza.ingredients:
    print(i.amount) # This only works if you're storing the amount directly in the association proxy table as per my example above
    print(i.unit) # Same goes for this
    print(i.ingredients.name) # here, the ingredient is accessed via the "ingredients" attribute of the association object

关于命名的另一件事:请注意,当关联对象中的backref对象只映射到单个成分时,它被称为print(pizza.ingredients[0].ingredients.name) ,因此它应该是单数 - 那么上面的示例将是{{ 1}},听起来好一点