[Answered ]-Django referencing a specific object in a many-to-one relationship

1👍

The approach you’ve suggested will work, however in its current form it allows for multiple Articles to be the favorite of one Reporter. With a bit of extra processing you can ensure that only one (at most) Article per Reporter is the favorite.

Making a few modifications to a couple of the answers to the question Unique BooleanField value in Django? we can restrict one True value per Reporter rather than one True value for the entire Article model. The approach is to check for other favorite Articles for the same Reporter and set them to not be favorites when saving an instance (rather than using a validation restriction).

I’d also suggest using a single transaction in the save method so that if saving the instance fails the other instances are not modified.

Here’s an example:

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)

I’ve also changed the approach to use a filter rather than a get just in case.

Then to get the favorite article for a reporter, you can use:

try:
    favorite_article = reporter.articles.get(is_favorite=True)
except Article.DoesNotExist:
    favorite_article = None

which you could wrap into a method/property of the Reporter class.

Leave a comment