【问题标题】:Django: storing model property on a field vs. on a different modelDjango:将模型属性存储在字段上与不同模型上
【发布时间】:2015-04-19 00:48:47
【问题描述】:

我对 Django 甚至数据库设计都比较陌生,我有一些想法想由其他人来运行。这不是一个具体的问题。我只是想看看其他人是如何看待这些事情的。

假设我们有一个应用程序到某个服务的模型。它包含您可能想象的应用程序包含的所有普通内容:

class Application(models.Model):
  first_name = CharField(max_length=255)
  last_name = CharField(max_length=255)
  date_of_birth = DateField()
  married = BooleanField()
  # ...other stuff

好的,这一切都很好。但是现在,想象一下您正在编写的 web 应用程序具有您可以部分完成应用程序、保存并稍后再返回的功能。一种方法是在上面的模型中添加另一个属性:

complete = BooleanField()

它有效,使用起来非常简单,但我不太喜欢它,因为它混淆了应用程序的语义;它添加了与应用程序没有内在联系的信息。另一种方法是创建另一个模型来跟踪完整的应用程序:

class CompleteApplication(models.Model):
  application = ForeignKey(Application)

我更喜欢这个,因为它使Application 保持干净。但是,它确实有弄乱查询的缺点。以下是查询系统中所有完整应用程序的两种方式:

方法一:

completed_applications = Application.objects.filter(complete=True)

方法二:

pks = CompleteApplication.objects.all().values_list("application__pk")
complete_applications = Application.object.filter(pk__in=pks)

方法 2 是两行代码,而不是一个和两个查询,而之前一个就足够了,因此数据库性能会受到影响。

还有第三种方法:我们可以创建一个元数据模型来存储我们可能想要附加到Application 模型的任何元数据,而不是创建一个跟踪完整应用程序的模型。出于我们的目的,该模型可以包含一个跟踪完整性的字段。但是,这种方法还具有允许任意数量的元数据字段与每个应用程序相关联的好处,而无需为每个应用程序创建一个新的 DB 表(如上述方法 2 的情况)。

class ApplicationMeta(models.Model):
  application = ForeignKey(Application)
  complete = BooleanField()

并且,为了完整性(双关语),要查询所有完整的应用程序,我们将使用以下语句:

completed_applications = Application.objects.all(applicationmeta__complete=True)

很好,很简单,就像方法1一样,但是查询对于数据库来说肯定是更多的工作。对于某些应用,这种方法还有另一个缺点。例如,假设我们想要跟踪有关应用程序的一些附加信息:它们可以被确认或被拒绝。但是,如果申请未得到确认,它确实必然意味着它被拒绝:它可能正在等待审核。此外,假设我们要跟踪确认日期和拒绝日期(当然,如果两者都适用)。那么,我们的元数据模型就变成了:

class ApplicationMeta(models.Model):
  complete = BooleanField()
  confirmed = BooleanField()
  rejected = BooleanField()
  date_confirmed = DateField()
  date_rejected = DateField()

好的...这可行,但它开始变得一团糟。首先,我们现在已经向潜在的错误打开了我们的系统:如果 ApplicationMeta 实例以某种方式被拒绝和确认设置为 True 怎么办?如果发生有趣的事情,我们可以对我们的类做一些花哨的步法(也许重写 setattr)以抛出异常,这样我们就可以防止持久化到数据库,但这是增加的复杂性,我希望没有必要。此外,任何模型最多只能设置 date_confirmed 或 date_rejected 之一。那是问题吗?在这里,我实际上并不确定。我的猜测是这可能会浪费空间,但我实际上并不知道。这个例子很简单,如果更复杂的例子向我们展示了大量不一定会填充的字段怎么办?似乎是糟糕的设计。

我很想听听关于这些想法的一些想法。

谢谢!

【问题讨论】:

    标签: django database-design django-models django-orm


    【解决方案1】:

    如果您有大量可能的元数据,出于性能原因,第三种方法可能有意义。对于一些布尔值和日期列,我不会这样做。如果您担心模型本身的可读性,您可以将任何元数据分解为抽象基础模型。您甚至可以将抽象模型重用于需要相同元数据的其他模型。该信息仍将存在于您的Application 模型中。

    如果您确实采用第二种或第三种方法,我会使用OneToOneField 而不是ForeignKey。它确保单个Application 没有2 个可能的ApplicationMeta 模型,并具有UNIQUE 数据库索引的额外好处。

    至于应用程序的状态,NullBooleanField 正是为此而设计的。它以None(数据库中的NULL)开头,意思是“没有价值”。然后可以将其设置为True(接受)或False(拒绝)。

    【讨论】:

      猜你喜欢
      • 2013-04-13
      • 1970-01-01
      • 2012-12-24
      • 1970-01-01
      • 2010-09-28
      • 2010-10-12
      • 2014-02-13
      • 2019-03-18
      • 2019-12-17
      相关资源
      最近更新 更多