在 Django 中,如何保持多对多关系同步?

In Django, how to keep many-to-many relations in sync?

在 Django 中,设置并保持最新的多对多字段的最佳方法是什么(由于缺少更好的术语)多对多字段的组合来自其他型号?

举个具体的例子,我有一个代表简历的模型。

class Resume(models.Model):
    owner = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="resume",
    )
    description = models.TextField(default="Resume description")
    skills = models.ManyToManyField(Skill, related_name="resume")

Resume 对象被另一个名为 WorkExperience 的模型引用:

class WorkExperience(models.Model):
    ...
    skills = models.ManyToManyField(Skill, related_name="work")
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=False,
        default=1,
        related_name="work_experience",
    )
    resume = models.ForeignKey(
        Resume,
        on_delete=models.CASCADE,
        null=False,
        default=1,
        related_name="work_experience",
    )

请注意,这里有相当多的冗余,Resume 和 WorkExperience 都指向同一个所有者等。也就是说,这两个模型(Resume 和 WorkExperience)都有一个名为 Skills 的字段。那引用了另一个名为 Skills 的模型。

我想要的是让 Resume 技能指向与 WorkExperience 中的技能相同的技能。关于如何执行此操作的任何建议?我还有一个名为 EducationExperience 的模型,它也引用 Skills 并且与 Resume 有外键关系。有什么方法可以使 Resume 中的技能与 WorkExperience 和 EducationExperience 中的技能保持同步?

一个直接的选择是实现一个名为 set_resume_skills 的方法,该方法会在调用时将他们拥有的技能添加到 Resume 拥有的技能列表中

Class WorkExperience(models.Model):
    # Same model as above
    def set_resume_skills(self):
        self.resume.skills.add(self.skills)

我对这种方法的问题是它只会增加技能,并不能真正使它们保持同步。因此,如果我从 WorkExperience 中删除一项技能,它不会从 Resume 中删除。另一个边界条件是,如果多个 WorkExperience 对象正在引用一项技能,然后我从一个对象中删除该技能,我将如何确保 Resume 中的引用仍然完好无损?即,两个工作经验对象引用技能“Javascript”。我从一个 WorkExperience 对象中删除了 Skill。 Resume 仍应引用技能“Javascript”,因为一个 WorkExperience 对象仍引用它。

编辑:我想这样做的原因是为了减少在前端完成的查询量。如果过滤技能的唯一方法是通过“子模型”(WorkExperience、EducationExperience),我需要在前端执行两个查询而不是一个。虽然现在想想,做两个查询也不错。

我是从错误的角度来解决问题的。当我只对 Skill 对象进行反向查询时,我不必担心检查 Skills。目前我正在过滤我视图中的技能查询集,如下所示:

class SkillViewSet(viewsets.ModelViewSet):
    serializer_class = SkillSerializer

    def get_queryset(self):
        queryset = Skill.objects.all()
        email = self.request.query_params.get("email", None)
        if email:
            User = get_user_model()
            user = User.objects.get(email=email)
            query_work_experience = Q(work__owner=user)
            query_education_experience = Q(education__owner=user)
            queryset = queryset.filter(
                query_work_experience | query_education_experience
            )

        return queryset

这消除了保持技能与简历模型同步的需要。

根据您的设计,Skills 似乎实际上属于所有者,即 AUTH_USER。通过在 Resume 和其他模型中使用 M2M 关系肯定会导致冗余。为什么不直接在用户模型中创建 M2M 技能?

关于减少查询的观点,还有其他方法可以做到这一点。通过使用 select_relatedprefetch_related

class User(models.Model):
skills = models.ManyToManyField(Skill, related_name="resume")
# ... Other fields here

class Resume(models.Model):
owner = models.OneToOneField(
    settings.AUTH_USER_MODEL,
    on_delete=models.CASCADE,
    related_name="resume",
)
description = models.TextField(default="Resume description")
# ... Other fields here