django ManyToManyField自对称唯一共关系

hi3rlvi2  于 2023-03-24  发布在  Go
关注(0)|答案(1)|浏览(98)

我在试图通过Django应用程序获取一些manytomanyrelated数据时遇到了一些麻烦
下面是我的模型:

SoftwareVersion(models.Model):
    id = models.AutoField(
        primary_key=True,
        db_index=True
    )

    ... Some other fields ...

    incompatibilities= models.ManyToManyField(
        "self",
        symmetrical=True,
        blank=True,
        default=None,
        through="Incompatibilities"
    )

Incompatibilities(models.Model):
    id = models.AutoField(
        primary_key=True,
        db_index=True
    )

    softwareversion_a = models.ForeignKey(
        "SoftwareVersion",
        models.CASCADE,
        db_index=True,
        db_column='softwareversion_a ',
        related_name='softwareversion_a',
        verbose_name="software version a",
    )

    softwareversion_b = models.ForeignKey(
        "SoftwareVersion",
        models.CASCADE,
        db_index=True,
        db_column='softwareversion_b',
        related_name='softwareversion_b',
        verbose_name="softwareversion_b",
    )

    status = models.BooleanField(
        verbose_name='Status',
        default=False
    )

    class Meta:
        unique_together = (('softwareversion_a', 'softwareversion_b'),)

为此,我在SoftwareVersion的保存方法中添加了一个逻辑来为每个新软件版本创建相关的不兼容性。我已经尝试了几种方法来做到这一点(使用循环或使用bulk_create)这里是我使用的批量创建函数:

# Inside SoftwareVersion Model class
def save(self, force_insert=False, force_update=False, using=None, update_fields=None) -> None:
    save = super().save(force_insert, force_update, using, update_fields)
    
    Incompatibilities.objects.bulk_create(
       (Incompatibilities(
       softwareversion_a=self,
       softwareversion_b=software_version,
       status=False
            ) for software_version in SoftwareVersion.objects.exclude(self)),
            ignore_conflicts=True,
            batch_size=1000
        )

    return save

我遇到的第一个问题是这个方法忽略了unique_together约束,它创建了重复项。之前我在每个SoftwareVersion上使用循环来创建对象,但它太长了,所以我想使用bulk_create,但它似乎不像我预期的那样工作。是否有其他优化的方法来完成它或传递一个参数,以便遵守unique_together约束。
其次,查询“给予此软件版本的所有不兼容性”的最优化方法是什么?目前我正在做的是:

Incompatibilities.objects.filter(softwareversion_a=?)

但我实际上错过了在字段“softwareversion_b”中引用软件版本的不兼容性。我尝试使用以下逻辑:

VersionLogiciel.objects.get(pk=?).incompatibilities.all()

但是django输出了相同的SQL请求,因此,结果相同。

Incompatibilities.objects.filter(softwareversion_a=?)

是否有一个参数可以放在某个地方,以便两个字段(这里是'softwarevision_a',' softwarevision_b')可以互换。或者我必须以特定的方式查询以访问不兼容对象。我已经尝试查询:

Incompatibilities.objects.filter(Q(softwareversion_a=?) or Q(softwareversion_b=?))

但是,由于我必须在softwareversion_a相关字段和softwareversion_b相关字段上订购结果,因此我无法让它像我想要的那样工作。
PS:对不起,我的英语不好,请注意,我把模型翻译成英语,所以我可能犯了一些错误,但在我这边,模型编译和迁移正在按预期工作。
提前感谢您的帮助。

5sxhfpxr

5sxhfpxr1#

我最终选择了第二种解决方案:https://charlesleifer.com/blog/self-referencing-many-many-through/
我没有在我的django模型中添加方法,而是使用django-pgtrigger创建postgres触发器。我的触发器允许复制更新,插入和删除。为了避免递归,我添加了一个“if exist”验证,以避免更新已经更新的数据(插入和删除也是如此)。

相关问题