对于多对多关系,与直通表中外键的字段一起使用的唯一\u

2024-04-20 05:31:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个Django 2.0项目应用程序。它有一个(非工作)型号.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应该包含对软件的引用,因为同一版本字符串对于不同的软件具有不同的含义。你知道吗

代码没有按this Stackoverflow answer中所描述的那样工作。我留下了独特的一行在一起,以说明我正在努力实现。你知道吗

我试图通过重写BundleSoftwareVersion中的save和validate\u unique方法来绕过Django的这个限制(不能同时使用通过unique\u中的外键引用的字段),但效果并不完全好。以下是我尝试过的实现:

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(管理站点在有问题的条目旁边显示一条消息)时,这些检查起作用,但是添加会导致未捕获的异常。你知道吗

有没有更好的方法来加强这种独特性?你知道吗


Tags: nameselfmodelonversionmodelsdefsoftware
1条回答
网友
1楼 · 发布于 2024-04-20 05:31:52

我想出了一个解决办法:

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的错误页,而不是表单中的错误消息。你知道吗

我会很感激关于如何弥补这个缺点的建议,甚至是关于完全不同方法的建议。你知道吗

相关问题 更多 >