Django:在一个字段上存储模型属性而在另一个模型上存储

我对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)

我更喜欢这个,因为它保持了应用程序的清洁.但是,它确实具有弄乱查询的缺点.以下是查询系统中所有完整应用程序的两种方法:

方法1:

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

方法2:

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)如果发生了一些有趣的事情就抛出一个异常,所以我们可以防止持久化到DB,但是这增加了我希望没有必要的复杂性.此外,任何模型最多只能有一个date_confirmed或date_rejected集合.那是问题吗?在这里,我实际上并不确定.我猜这可能是浪费空间,但我实际上并不知道.这个例子很简单,如果更复杂的例子给我们提供了大量不必填充的字段,该怎么办?好像很糟糕的设计.

我很想听听有关这些想法的一些想法.

谢谢!

最佳答案 如果您有大量可能的元数据,那么第三种方法可能因性能原因而有意义.我不会为一些布尔和日期列做这件事.如果您担心模型本身的可读性,可以将任何元数据分解为抽象基础模型.您甚至可以将抽象模型重用于需要相同元数据的其他模型.该信息仍将存在于您的应用程序模型中.

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

至于应用程序的状态,NullBooleanField就是为此而设计的.它以None(NULL中的NULL)开头,表示“无值”.然后可以将其设置为True(已接受)或False(已拒绝).

点赞