mrdaliri
mrdaliri

Reputation: 7358

Filter objects by self relation

Here is my Django model:

class Message(models.Model):
    text = models.TextField()
    parent = models.ForeignKey('Message', null=True, on_delete=models.SET_NULL)
    threads = ThreadManager()


class ThreadManager(models.Manager):
    def get_queryset(self):
        return super(ThreadManager, self).get_queryset().filter()

As you see, it uses a custom manager. In ThreadManager I want to filter messages which has a self relation. I mean parent refers to its record. (x.parent = x)

How to write that filter? Is it possible at all?

Upvotes: 1

Views: 2678

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477170

Self reference

You can use a filter where you address the pk column:

from django.db.models import F

Message.objects.filter(parent=F('pk'))

So here we specify that the parent_id should be the same as the primary key, hence in that case the relation is cyclic.

Note that this filter will not detect cycles that have a length longer than 1. For example if we have two messages A and B, and A.parent = B and B.parent = A, then this filter will not detect this. It only detects Messages where A.parent == A.

Furthermore Django does not perform a semantical check: if you later let parent refer to another model, this filter does not makes much sense anymore, but Django will not produce a warning.

Non self-referencing

In case we want to opposite, we can use .exclude(..) or wrap it into a Q object:

# using exclude
Message.objects.exclude(parent=F('pk'))

# using Q
from django.db.models import Q

Message.objects.filter(~Q(parent=F('pk')))

So in that case the Message either has no parent, or a parent that is not the same message.

Upvotes: 5

Related Questions