【问题标题】:Django ORM Inheritance: You are trying to add a non-nullable field 'dish_ptr' to [...] without a defaultDjango ORM 继承:您正在尝试在没有默认值的情况下将不可为空的字段“dish_ptr”添加到 [...]
【发布时间】:2020-07-12 20:16:09
【问题描述】:

我正在开发一个餐厅应用程序(并且是 Django/Python 的新手)。我希望有一个父类 Dish,它将包含一些计数器或 ID,这些计数器或 ID 会随着 Dish 子类的每个实例而递增。实例是具有不同特征和关系的比萨、意大利面等菜肴。我正在尝试使Dish 具体化,因为在这种情况下,我认为我只需访问Dish 的PK,这将为每个菜单项提供一个Dish-ID。 但是,我不知道如何正确修复遇到的错误:You are trying to add a non-nullable field 'pasta_ptr'

这里是相关代码sn-ps:

class Dish(models.Model):
  pass # should automatically generate PK, right?

class Pasta(Dish):
  name = models.CharField(max_length=64, primary_key=True)
  price = models.DecimalField(max_digits=6, decimal_places=2)

  def __str__(self):
    return f"{self.name}, price: ${self.price}"

class Pizza(Dish):
  sizestyle = models.CharField(max_length=4, choices=SIZESTYLE_CHOICES, default=SMALL_REGULAR)
  topping_count = models.IntegerField(default=0, validators=[MaxValueValidator(5), MinValueValidator(0)])
  price = models.DecimalField(max_digits=6, decimal_places=2)

  def __str__(self):
    return f"Price for {self.sizestyle} pizza with {self.topping_count} toppings: ${self.price}"

class Sub(Dish):
  name = models.CharField(max_length=64, primary_key=True)
  price_category = models.ForeignKey(SubPrice, on_delete=models.DO_NOTHING, related_name="sub_price_category")

  def __str__(self):
    return f"{self.name}, Price Category: {self.price_category}"

class Platter(Dish):
  name = models.CharField(max_length=64, primary_key=True)
  price_large = models.DecimalField(max_digits=6, decimal_places=2, default=0)
  price_small = models.DecimalField(max_digits=6, decimal_places=2, default=0)

  def __str__(self):
    return f"{self.name} price: Large ${self.price_large}, Small ${self.price_small}"

【问题讨论】:

  • 如果您的数据库中已经有 Pastas 或 Pizzas,他们需要知道与哪个具体 Dish 相关联。如果您还没有重要数据,请删除 db 或 python manage.py migrate dish_app zero 并删除并重新进行迁移。
  • 我已经commented on your previous question,你的设计是错误的。
  • @Anna 您的设计问题是您正在将每种菜肴与特定模型进行一对一的映射 - 您正在尝试将用户对数据的视图直接转换为db 模式 - 而不是进行更深入的分析并设计一个可以将各种菜肴(现有和未来)描述为统一模式的模式。这个模式当然不能是用户(最终用户和站点管理员)如何查看数据的简单映射,并且需要一些中间域层来将 db 模式转换为域对象,但大多数非微不足道的应用程序。
  • 例如:您的“拼盘”类有两个价格字段,分别用于小份和大份。这实际上意味着一道菜可以有一个或多个价格,因此您应该有一个带有“限定符”(即“小”、“大”等)的“价格”实体和“菜”实体上的 FK。通过这种方法,当餐厅决定添加“中等”或“特大号”部分时,他们只需在后端进行配置。
  • "Pizza 没有名字" => 将菜名设为可选,或者只将菜名命名为“pizza”。 “披萨的价格是基于浇头数量的计算”=> 那么你需要一个额外的间接级别 - 例如一个知道如何计算菜肴价格的“Pricer”对象(strategy pattern)(我有相同的我当前的应用程序中的案例,这就是我实现解决方案的方式)。请注意,我并不是说您当前的设计根本不起作用,也不是我建议的方法“更简单”(实际上它实际上更复杂);-)

标签: python django django-models orm


【解决方案1】:

如果您想从 Dish() 继承,但在数据库中没有,您可以这样做:Django abstract base class docs

class Dish(models.Model):
    # your model structure

    class Meta:
        abstract = True

class Pizza(Dish):
    # your model structure

当你使用它时,它只会继承 Dish 中的任何内容。您不会将 Dish 视为数据库中的表。

如果您确实想将其用作父类,则需要告诉子类 Dish 是父类:django many to one docs

class Dish(models.Model):
    pass

class Pizza(models.Model):
    dish = models.ForeignKey(Dish, on_delete=models.CASCADE)
    # other model structure

这将在数据库中为 Dish 创建一个 id,当创建 Pizza 时,它将引用 Dish 的 id (pk)。

【讨论】:

  • 谢谢。我尝试了后者,但得到了Reverse query name for 'Pizza.dish' clashes with reverse query name for 'Pizza.dish_ptr'. HINT: Add or change a related_name argument to the definition for 'Pizza.dish' or 'Pizza.dish_ptr' - 所以我添加了一个相关名称。但是当再次运行 makemigrations 时,我又得到了You are trying to add a non-nullable field 'dish' to pasta without a default。如果它引用Dish 中的自动生成的PK,为什么它需要一个默认值?
  • 你得到最后一个错误的原因是因为你是对的 pk 是一个不可为空的字段。这意味着您的数据库中的数据已经由dish_ptr 表示,因此当您运行迁移时,Django 在添加dish = models.ForeignKey... 之前并不知道 Dish 是 Pizza 的主键。我目前知道如何解决这个问题的唯一方法是删除数据库中的数据然后运行迁移。我采用了您的模型,运行迁移,添加数据,然后将模型更新为我的示例,并且必须删除数据才能成功迁移。
  • 您始终可以通过运行 python manage.py dumpdata -o path/to/output/file.json 来挽救数据,但在运行 python manage.py loaddata path/to/file.json 之前,您需要在 json 文件中进行一些编辑,否则您会收到另一个错误,因为 dish_ptr文件中的引用。
猜你喜欢
  • 2017-10-09
  • 2016-01-04
  • 2020-06-03
  • 1970-01-01
  • 2021-09-06
  • 2021-08-25
  • 1970-01-01
  • 2021-06-21
  • 2020-03-28
相关资源
最近更新 更多