Reputation: 14255
In Django, what is the simplest way to determine if there are any objects in our database that refer to a given object?
Consider this minimal example from Django's Related objects reference:
from django.db import models
class Reporter(models.Model):
pass
class Article(models.Model):
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
How can we determine if there are any objects, so not only Article
objects, that point to a given Reporter
object, either through a OneToOneField
, a ForeignKey
, or a ManyToManyField
?
In other words, we want to determine if there are any reverse relations to the given object.
For Article
it is easy, we can just get e.g. reporter.article_set.count()
, but other models may be added later which also point to Reporter
, and these will also have to be taken into account.
An example use case is where we want to prevent modification as soon as an object is being referenced by any other object. Or we could use it to enforce a kind of behavior similar to the on_delete=models.PROTECT
mechanism.
Upvotes: 2
Views: 1715
Reputation: 14255
Here's a working solution, using the Model._meta
API, but I am not sure if this is the best way to do it. Hoping for better answers.
Basically, given an object, we get a list of its reverse relations, then, for each of those, we check if there are any objects in the relation.
# just any given reporter object
obj = Reporter.objects.first()
# assume no references to obj
obj_has_reverse = False
# skip for new objects (i.e. those not yet saved to database)
if obj.id is not None:
# reverse relation "fields" on the Reporter model are auto-created and
# not concrete
for reverse in [f for f in obj._meta.get_fields()
if f.auto_created and not f.concrete]:
# in case the related name has been customized
name = reverse.get_accessor_name()
# one-to-one requires a special approach
has_reverse_one_to_one = reverse.one_to_one and hasattr(obj, name)
has_reverse_other = not reverse.one_to_one and getattr(obj, name).count()
if has_reverse_one_to_one or has_reverse_other:
obj_has_reverse = True
Note that a reverse relation for ForeignKey
and ManyToManyField
returns a RelatedManager, so we can check e.g. the count()
. However, a reverse relation for OneToOneField
does not return a RelatedManager
. It raises a DoesNotExist
exception if there is no related object, as described in the docs.
Also see the source for reverse_related
.
Upvotes: 1