Django 在多对一关系中引用特定对象
Posted
技术标签:
【中文标题】Django 在多对一关系中引用特定对象【英文标题】:Django referencing a specific object in a many-to-one relationship 【发布时间】:2021-07-10 17:41:12 【问题描述】:假设我有以下模型:
from django.db import models
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, related_name="articles", on_delete=models.CASCADE)
我想向我的Reporter
模型添加一个favorite_article
字段,该字段将引用来自reporter.articles
的特定Article
。
一种选择是将信息放入Article
模型中:
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, related_name="articles", on_delete=models.CASCADE)
is_favorite = models.BooleanField()
但这似乎不是一个非常干净的解决方案。有没有更好的方法来做到这一点?
【问题讨论】:
你有没有尝试过,或者你对如何去做有任何想法? @HenryWoody 我添加了一个我考虑过的可能方法。我希望有一个更清洁/更好的解决方案。 【参考方案1】:您建议的方法可行,但在目前的形式下,它允许多篇文章成为一名记者的最爱。通过一些额外的处理,您可以确保每个记者只有一篇(最多)一篇文章是最喜欢的。
对问题Unique BooleanField value in Django? 的几个答案进行一些修改,我们可以限制每个记者一个True
值,而不是整个文章模型的一个True
值。方法是检查同一 Reporter 的其他收藏文章,并在保存实例时将它们设置为不收藏(而不是使用验证限制)。
我还建议在 save
方法中使用单个事务,这样如果保存实例失败,则不会修改其他实例。
这是一个例子:
from django.db import transaction
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, related_name="articles", on_delete=models.CASCADE)
is_favorite = models.BooleanField(default=False)
def save(self, *args, **kwargs):
with transaction.atomic():
if self.is_favorite:
reporter_id = self.reporter.id if self.reporter is not None else self.reporter_id
other_favorites = Article.objects.filter(is_favorite=True, reporter_id=reporter_id)
if self.pk is not None: # is None when creating a new instance
other_favorites.exclude(pk=self.pk)
other_favorites.update(is_favorite=False)
return super().save(*args, **kwargs)
为了以防万一,我还更改了使用filter
而不是get
的方法。
然后要获得喜欢的文章为reporter
,您可以使用:
try:
favorite_article = reporter.articles.get(is_favorite=True)
except Article.DoesNotExist:
favorite_article = None
您可以将其包装到 Reporter 类的方法/属性中。
【讨论】:
感谢@Henry Woody,这行得通,但我希望有一些东西可以将favorite_article
字段与记者联系起来,以便可以直接引用它,而不必搜索(可能许多) 文章以找到标记为收藏的文章。
@USS1994 您可以使用reporter.articles.get(is_favorite=True)
(或只是filter
)检索收藏的文章,因此您无需搜索。此外,如果记者没有收藏的文章,您应该将其包装在 try
/except
中。这种方法对你有用吗?以上是关于Django 在多对一关系中引用特定对象的主要内容,如果未能解决你的问题,请参考以下文章
Django models 多对一关系中 on_delete 参数的设置