Alex Gaynor writes:
> On Mon, Feb 23, 2009 at 12:40 PM, sphogan <sph...@gmail.com> wrote:
>
>> [...]
>>
>> However, select_related() only works on the object the foreign
>> key is declared on
>> (http://docs.djangoproject.com/en/dev/ref/models/querysets/ #id4
>> <http://docs.djangoproject.com/en/dev/ref/models/querysets/%0A#id4>;
>> specifically, "You can only refer to ForeignKey relations in the
>> list of fields passed to select_related.") Presumably this is
>> because it does an INNER JOIN rather than a LEFT OUTER JOIN and
>> would therefore miss articles with no comments.
>>
>> What I would like to be able to do is:
>>
>> Article.objects.select_related('comment_set').all()
>>
>> This seems like a somewhat simple case and there seems to be no
>> mention of it in the documentation anywhere.
>
> That's not possible,
By principle, or just because Django can't do it (yet)? The related
feature request can be found at
<http://code.djangoproject.com/ticket/2238>. It it, I refer to
http://rubynugs.blogspot.com/2008/07/couple-of-stupid-things-about-djano.html
where a Rails fan claims that Rails can do it.
I don't know much about SQL so I really would like to know whether
this could be realised.
Tschö,
Torsten.
--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
Errors should never pass silently.
Unless explicitly silenced.
~Zen of Python
Well, I guess I have a project for this week!
Sean.
[...]
> >> What I would like to be able to do is:
> >>
> >> Article.objects.select_related('comment_set').all()
> >>
> >> This seems like a somewhat simple case and there seems to be no
> >> mention of it in the documentation anywhere.
> >
> > That's not possible,
>
> By principle, or just because Django can't do it (yet)? The related
> feature request can be found at
> <http://code.djangoproject.com/ticket/2238>.
Actually, this is really just a disguised version of #5768, which is
talking about values() -- since that contains this problem plus the
presentation issue subtlety. Read the comments there for information
(I've repeated the basic hard part of this, below).
> It it, I refer to
> http://rubynugs.blogspot.com/2008/07/couple-of-stupid-things-about-djano.html
> where a Rails fan claims that Rails can do it.
>
> I don't know much about SQL so I really would like to know whether
> this could be realised.
It's possible, but it's not at all trivial. Firstly, there's a
representation issue for values(): there are multiple *_set rows
returned for any individual result in the original query. So it requires
a bunch more munging of results.
Secondly, there's the "don't shoot yourself in the foot" issue, in that
it's *very* easy to create a horribly inefficient query. The problem is
when you start querying for both foo_set and bar_set. If they return X
and Y results, respectively, for a given row, we need to make sure we
only get back O(X + Y) results from the database, not O(X * Y) results.
the simplest ways to construct the query for these multi-valued returns
leads to the second case, so extra care has to be put into the situation
with multiple multi-valued return results.
We've always said we'll put this in once there's an excellent patch that
avoids the second problem and has a decent return format for values()
Hopefully that should put to rest all the debate in this thread about
what's going on. We'll do, but somebody has to write the patch and it's
not trivial. (That somebody might be me one day, but I also about a few
hundred other things to work on in Django, so it's prioritised
accordingly).
Regards,
Malcolm
Yes, that's the idea.
>
> That would seem to allow for decent iteration over something to
> propagate the appropriate *_set list attributes.
>
> To illustrate using the blog example (extended with an authors m2m
> relationship so that an article can have many authors):
>
> SELECT * FROM articles /*fill nulls for other columns, cut for
> readability*/
> UNION
> SELECT * FROM articles /*could also possibly just fill nulls for
> articles to make the iteration easier*/
> INNER JOIN comments ON comments.article_id=articles.id
> UNION
> SELECT * FROM articles
> INNER JOIN authors_m2m_table ON
> authors_m2m_table.article_id=articles.id
> INNER JOIN authors ON authors_m2m_table.author_id=authors.id
This is almost correct, except that you will have different numbers of
fields in each union sub-query (or possibly the same number). So, in
order to be able to differentiate which rows are for which results,
you'd also select a constant value (1, 2, 3, ...) to indicate which
sub-query it's coming from.
>
>
> The problem with that is that any filters would have to be repeated
> multiple times - like if you just wanted articles written by Malcom,
> you'd have to add the explicit joins on each clause to the union.
Yes, they're effectively all separate queries that have the results
jammed together. It's still slightly (but only slightly, in most cases)
more efficient than doing the same thing in Python. Whether all the
added complexity is worth it, though, or whether we just restrict it to
one field and say "do the rest in Python" remains to be seen from how
complicated the patch ends up looking.
You've certainly understand the details of the solution. You can see
that it gets a little complex. Not technically difficult, just fiddly to
implement. Which is kind of why it hasn't been done yet (at least by
me). The payback on effort required is lower than for a bunch of other
things that are biting people.
Regards,
Malcolm