views:

98

answers:

3

Given the Model:

class Profile(models.Model):
    user = models.ForeignKey(User, unique=True)

class Thingie(models.Model):
    children = models.ManyToManyField('self', blank=True, symmetrical=False) 

class Relation(models.Model):
    profile = models.ForeignKey(Profile)
    thingie = models.ForeignKey(Thingie)

How would one return a QuerySet containing all Profile instances related to a given Thingie? That is, every Profile that has a foreign key pointing to it from a Relation and to the given thingie.

I know all about select_related(), and how I could use it to do this by iterating but i find iterating irritating (badoop bah!). Also, values_list() has been looked at, but it doesn't quite do the right thing.

Please help! Thanks!

A: 

Did you read the Django doc on Making queries? It has a simple example of achieving what you want, more specifically Lookups that span relationships. Make sure you refer the code snippets in the latter link to the model code at the beginning of the page.

Thierry Lam
Yes I have read the documentation end-to-end quite a few times. However, I seem only to be able to get comparison information by spanning relationships(IE: find me all Thingies where thingie.profile.user = X), not actual instances themselves (IE: Find me all relation.profiles where relation.thingie = y).
neuman
A: 

Without defining a direct relationship between Profile and Thingie, you can't.

The best way would be to add a ManyToMany to Thingie that points to Profile and use the through argument (django docs) on the ManyToMany to specify your Relation model/table.

That way you can do a direct filter operation on Thingie to get your profiles and still store your intermediate Relation data.

Gabriel Hurley
I may end up using this because it is more in line with Django best practices, but I felt the second part of the above answer was more accurate for this question. Thanks!
neuman
+2  A: 

Do you definitely need it to be a queryset? If you only need it to be an iterable, a simple expression for your purposes is:

profiles = [r.profile for r in thingie.relation_set.all()]

I'm not sure if a list comprehension counts as irritating iterating, but to me this is a perfectly intuitive, pythonic approach. Of course if you need it to be a queryset, you're going to do something messier with two queries, eg:

relation_values = thingie.relation_set.all().values_list('pk', flat=True)
profiles = Profile.objects.filter(relation__in=relation_values)

See the "in" documentation for more. I prefer the first approach, if you don't need a queryset. Oh and if you only want distinct profiles you can just take set(profiles) in the first approach or use the distinct() queryset method in the second approach.

ozan
List comprehension definitely works well, but the OP was complaining about iterating (which is basically what a list comprehension does)...
Gabriel Hurley