views:

136

answers:

2

I have these two simple models, A and B:

from django.db import models

class A(models.Model):
  name = models.CharField(max_length=10)

class B(A):
  age = models.IntegerField()

Now, how can I query for all instances of A which do not have an instance of B?

The only way I found requires an explicitly unique field on each subclass, which is NOT NULL, so that I can do A.objects.filter(b__this_is_a_b=None), for example, to get instances that are not also B instances. I'm looking for a way to do this without adding an explicit silly flag like that.

I also don't want to query for all of the objects and then filter them in Python. I want to get the DB to do it for me, which is basically something like SELECT * FROM A WHERE A.id in (SELECT id from B)

A: 

I don't work with django, but it looks like you want the isinstance(obj, type) built-in python method.

Edit:
Would A.objects.exclude(id__exact=B__id) work?

tgray
Unfortunately, no. I'm trying to give the django ORM the right instructions to generate the SQL to do this for me, rather than filter them on the client side.
ironfroggy
+1  A: 

I'm not sure it's possible to do this purely in the DB with Django's ORM, in a single query. Here's the best I've been able to do:

A.objects.exclude(id__in=[r[0] for r in B.objects.values_list("a_ptr_id")])

This is 2 DB queries, and works best with a simplistic inheritance graph - each subclass of A would require a new database query.


Okay, it took a lot of trial and error, but I have a solution. It's ugly as all hell, and the SQL is probably worse than just going with two queries, but you can do something like so:

A.objects.exclude(b__age__isnull=True).exclude(b__age_isnull=False)

There's no way to get Django to do the join without referencing a field on b. But with these successive .exclude()s, you make any A with a B subclass match one or the other of the excludes. All you're left with are A's without a B subclass.

Anyway, this is an interesting use case, you should bring it up on django-dev...

AdamKG
I would accept this answer, but it doesn't seem like any answer is really "correct" in a good sense. I'll bring it up with django-dev as you suggest.
ironfroggy