Earlier quoted context omitted.
I want to preface this by saying that Django isn't bad. It's really good. However, there are some maddening things. For example: In Rails, you can say Article.find(:all, :include => :comments) and that will get you the articles with their comments. In Django, you can say for a in Article.objects.all(): a.comments #hits the database again! argh! Before you say, "Django has select_related() which does the same thing as…
Your first issue is incorrect. You can easily accomplish the query with one sql hit. The extra() command would work if your foreignkey was defined in the comments model.
Article.objects.all().extra(select={'comment_count': 'SELECT count(*) FROM blog_comment WHERE blog_comment.article_id = blog_article.id'})
But I can't populate a list of comments objects onto it.I've talked to Malcom about it (who wrote the QuerySet Refactor branch - or at least a substantial portion) and it's a known deficiency. Basically, the issue is two-fold. First, no one has stepped up to write the code that would make select_related() work in that fashion. Second, people want the implementation to disallow certain bad situations.
The first part is self-explanatory: select_related() should be enhanced to support that, but someone needs to write the code. The second part isn't as much, but it's more interesting.
Let's say you execute this query:
SELECT * FROM articles LEFT OUTER JOIN comments ON comments.article_id = articles.id
How many rows will you get from that? You'll get somewhere in the vicinity of the number of comments (adding a row for any article without comments). Suffice it to say, the results set grows linearly in proportion to the number of articles and comments.Now let's say we have this:
SELECT * FROM articles LEFT OUTER JOIN comments ON comments.article_id = articles.id LEFT OUTER JOIN votes ON votes.article_id = articles.id
So, we have our article with comments, but also votes now. So, let's say we want to get just one article that has 100 comments and 200 votes. How many lines will that return? In the original SQL query, we would have seen 100 lines (one for each comment) which would have been manageable. Here, we get 100 * 200 lines back. That's 20,000 rows to parse to build a single Article object!So, the Django folk aren't into letting you just hang yourself out to dry like that. There have been proposals to limit it in ways that wouldn't let you do that, but it's really just something genuinely missing and there isn't a way to do it other than running looped queries or doing something ugly.
extra() doesn't do the same thing as select_related(). I'm not saying that doing ORM over multi-valued relationships is easy or that you can't execute queries that are really bad doing them, but it still means that there isn't support for a pretty basic function.