【发布时间】:2016-06-17 11:35:02
【问题描述】:
我有一个包含成分列表的数据库。我想避免此表中的重复条目。我不想使用unique 关键字有两个原因:
- 我的唯一性约束比单纯的
=更复杂一点 - 我不想在创建预先存在的成分模型时引发异常,而是只想返回该模型,以便我可以写
Ingredient(ingredient_name='tomato')并继续我的一天,而不是封装所有在try子句中。这将使我能够轻松地在我的食谱表中快速添加配料。
一个解决方案是简单地拥有一个像create_ingredient 这样的包装器函数,但我不觉得它特别优雅,更具体地说,它对于其他一些开发人员来说并不健壮,只是忘记使用包装器。因此,我正在玩弄 pre_init 和 post_init 信号。
这是我目前所拥有的:
class Ingredient(models.Model):
ingredient_name = models.CharField(max_length=200)
recipes = models.ManyToManyField(Recipe,related_name='ingredients')
def __str__(self):
return self.ingredient_name
class Name(models.Model):
main_name = models.CharField(max_length=200, default=None)
equivalent_name = models.CharField(max_length=200, primary_key=True, default=None)
def _add_ingredient(sender, args, **kwargs):
if 'ingredient_name' not in kwargs['kwargs'] :
return
kwargs['kwargs']['ingredient_name'] = kwargs['kwargs']['ingredient_name'].lower()
# check if equivalent name exists, make this one the main one otherwise
try:
kwargs['kwargs']['ingredient_name'] = Name.objects.filter(
equivalent_name=kwargs['kwargs']['ingredient_name']
)[0].main_name
except IndexError:
name = Name(main_name=kwargs['kwargs']['ingredient_name'],
equivalent_name=kwargs['kwargs']['ingredient_name'])
name.save()
pre_init.connect(_add_ingredient, Ingredient)
到目前为止一切顺利。这实际上有效,并将在模型初始化之前在需要时替换 ingredient_name。现在我想要检查有问题的成分是否已经存在,如果存在则让初始化程序返回它。我想我需要使用post_init 来执行此操作,但我不知道如何修改正在创建的特定实例。这就是我的意思:
def _finalize_ingredient(sender, instance, **kwargs):
try:
# doesn't work because of python's "pass arguments in python's super unique way of doing things" thing
instance = Ingredient.objects.filter(ingredient_name=instance.ingredient_name)[0]
except IndexError:
pass
post_init.connect(_finalize_ingredient, Ingredient)
正如我所评论的,我不希望这会起作用,因为 instance = ... 实际上并没有修改 instance,它只是重新分配变量名(顺便说一下,如果你尝试运行它,会发生各种可怕的事情我不想理解,因为我知道这是完全错误的)。那么我该怎么做呢?我真的希望包装函数不是这里最干净的选择。我是 OOP 的忠实拥护者,天哪,我想要一个 OOP 解决方案来解决这个问题(正如我所说,我认为从长远来看,它会比包装器更健壮、更安全)。
我当然意识到我可以向 Recipe 添加一个 add_ingredient 方法,它会为我完成所有这些,但我真的很喜欢将所有这些包含在我的 Ingredient 类中的想法,因为它可以保证正确任何情况下的数据库行为。我也很想知道post_init 方法是否/如何用于在给定情况下完全覆盖创建的对象。
顺便说一句,你们中的一些人可能想知道为什么我的Name 类中没有ForeignKey 条目可以将Name 表连接到Ingredient 表。毕竟,这不是我的检查在我的_add_ingredient 方法中基本上完成的吗?原因之一是,如果我这样做,那么我最终会遇到我试图在这里解决的相同问题:如果我想动态创建一种成分以将其添加到我的食谱中,我可以简单地创建一个 @987654340 @object 创建Ingredient 对象时,但如果它对应于已在使用中的main_name(而不是简单地返回我需要的对象),则会引发异常。
【问题讨论】:
标签: django parameter-passing unique-index