Saturday, July 28, 2012

Re: Is this possible using the Django ORM without raw SQL?

On 28 heinä, 12:20, sbrandt <s.brandt.ber...@googlemail.com> wrote:
> Hello,
>
> for the first time ever how to compile a query with the Django ORM :/
>
> There are two Models: The second model has a foreign key to the first model
> and a type, where the combination of "first model and second model's type"
> is unique. Let's say type 1 are dogs, type 2 are cats and every model1 can
> only have 0 or 1 dogs and cats. I know that it could have been implemented
> as two seperate tables without types and one-to-ones, but I need to query
> the whole model2 often and it's implemented in this way since years.
>
> This is not a problem for single model1 objects, since it's just a second
> objects.get(...)-call as a lazy property in the model1.
>
> But now I need a query for *all* model1-objects with two additional
> columns: Has this model1 a cat, has this model1 a dog?
>
> Here's some code (Just pseudocode - doesn't work! My actual models would be
> too complicated):
>
> class Model1(Model):
>     name = CharField(...)
>
> m2types = (cats, dogs)
>
> class Model2(Model)
>     m1 = ForeignKey(Model1)
>     type = SmallIntegerField(..., choices=m2types)
>     class Meta:
>         unique_together = ('m1', 'type')
>
> In SQL I would do it with left outer joins:
>
> SELECT m1.id, m1.name, c.id, d.id
> FROM model1 AS m1
> LEFT OUTER JOIN model2 AS c ON c.m1_id = m1.id AND c.type = 1
> LEFT OUTER JOIN model2 AS d ON d.m1_id = m1.id AND d.type = 2;
>
> So, back to my questsion: Is this possible with the Django ORM without
> using raw SQL?
>
> Note: Speed is not important. Since it is a cronjob being done half a year
> and my models are just hundrets, iterating over every model1 and using the
> lazy property cat and dog would be okay, and also using raw SQL would be
> okay since I'm tied to PostgreSQL. I'm explicitly searching for an elegant
> solution with the Django ORM.

Hmmh, so you want to fetch every object, and "annotate" the
information about having a dog or a cat in the original model. I don't
think that can be done, although there might be some trick for this.

What you could do is use the prefetch_related() method, and then do
the annotation in Python code.

Something like this:
objs = Model1.objects.prefetch_related('model2_set')

And in model1 you could have two properties, has_cat and has_dog

class Model1:
...
def _has_dog(self):
return any(obj for obj in self.model2_set if obj.type == DOG)
has_dog = property(_has_dog)
...

While this isn't elegant it gets the work done... If you need to
filter or do something else in the ORM with the has_dog information,
you will not be able to do this using the above idea.

Django's ORM can be somewhat hard to use when working with reverse
foreign key data. There is room to improve the annotation mechanism of
Django. I hope the situation will improve... The prefetch_related
machinery has helped with many situations already.

- Anssi

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to django-users+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

No comments:

Post a Comment