在 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_related
或 prefetch_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
在 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_related
或 prefetch_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