我正在开发一个Django 2.0项目应用程序。它有一个(非工作)models.py文件,看起来像这样:
from django.db import models
from django.utils import timezone
class Computer(models.Model):
name = models.CharField(max_length=25)
def __str__(self):
return "Computer {}".format(self.name)
class Software(models.Model):
name = models.CharField(max_length=25)
description = models.CharField(max_length=1024, blank=True)
def __str__(self):
return self.name
class SoftwareVersion(models.Model):
software = models.ForeignKey(Software, on_delete=models.CASCADE, related_name="versions")
version = models.CharField(max_length=100)
released_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return "{} {}".format(self.software, self.version)
class ComputerSoftwareBundle(models.Model):
computer = models.ForeignKey(Computer, on_delete=models.CASCADE, related_name="bundles")
installed_at = models.DateTimeField(default=timezone.now)
versions = models.ManyToManyField(SoftwareVersion, through="BundleSoftwareVersion", related_name="bundles")
class BundleSoftwareVersion(models.Model):
bundle = models.ForeignKey(ComputerSoftwareBundle, on_delete=models.CASCADE)
version = models.ForeignKey(SoftwareVersion, on_delete=models.CASCADE)
class Meta:
unique_together = (("bundle", "version__software"),)
该应用程序会跟踪当前或以前安装在计算机上的软件包。这里的事情是捆绑包不应包含同一软件的多个版本。此外,SoftwareVersion应包含对Software的引用,因为相同的版本字符串对于不同的软件具有不同的含义。
代码无效,如this Stackoverflow answer中所述。我离开了unique_together线来说明我想要实现的目标。
我试图通过覆盖BundleSoftwareVersion中的save和validate_unique方法来解决Django的这种限制(无法使用通过unique_together中的外键引用的字段),但这并没有完全解决。这是我尝试过的实现:
class BundleSoftwareVersion(models.Model):
bundle = models.ForeignKey(ComputerSoftwareBundle, on_delete=models.CASCADE)
version = models.ForeignKey(SoftwareVersion, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
self.validate_unique()
super().save(*args, **kwargs)
def validate_unique(self, exclude=None):
super().validate_unique(exclude)
bundle_versions = BundleSoftwareVersion.objects.filter(bundle=self.bundle,
version__software=self.version.software)
count = len(bundle_versions)
if not self.pk:
# if this instance is not stored in the database,
# we need to increment the count to take this instance
# into account
count += 1
if count > 1:
raise ValidationError("There already is an instance of software '{}' in this bundle.".format(self.version.software))
到目前为止,我已通过管理网站试用了这些模型。更改现有ComputerSoftwareBundle时,检查工作(管理站点在违规条目旁边显示消息),但在未捕获的异常中添加结果。
有没有更好的方法来强制实现这种独特性?
答案 0 :(得分:0)
我想出了一个解决方法:
class BundleSoftwareVersion(models.Model):
bundle = models.ForeignKey(ComputerSoftwareBundle, on_delete=models.CASCADE)
version = models.ForeignKey(SoftwareVersion, on_delete=models.CASCADE)
_software = models.ForeignKey(Software, on_delete=models.CASCADE, null=True, editable=False)
class Meta:
unique_together = (("bundle", "_software"),)
def save(self, *args, **kwargs):
self._software = self.version.software
super().save(*args, **kwargs)
正如您所看到的,我现在有一个帮助字段_software
,该字段在unique_together
中使用,每次保存时都会存储self.version.software
。
到目前为止,我遇到了这种方法的一个缺点:尝试保存包含重复软件实例的ComputerSoftwareBundle
会导致显示IntegrityError
的错误页面,而不是表单中的错误消息。
我很欣赏有关如何解决这个缺点的建议,甚至是对完全采用不同方法的建议。