tags:

views:

340

answers:

2

Hello,

I want to execute a semi-complex query in Django. For example I want something that is like this:

SELECT 
b.*,
(SELECT count(id) FROM comments c WHERE c.blog_id = b.id) AS number_of_comments
FROM blog b 
WHERE 1

From my PHP background, Code Igniter and Zend Framework has "query builders". Where you can built an SQL-query using the methods in the framework. Is this something like in Django?

What would be the best way to build and execute complex queries in Django? Is there a recommended way / best-practice to do these kinds of queries?

UPDATE:

I got it working with little changes thanks to mherren's code below. Here is the updated version of the code.

In my views.py I have this:

def index(request):
    blog_posts = Blog.objects.all().annotate(Count('comment')).order_by('-pub_date')[:5]

    return render_to_response('blog/index.html', 
    {'blog_posts': blog_posts})

In my template file (index.html) I have this:

Welcome...
{% if blog_posts %}
    <ul>
    {% for post in blog_posts %}
        <li>
            <b>
            <a href="/blog/post/{{ post.id }}">{{ post.title }}</a>
            </b> ({{ post.pub_date }})<br/>
            {{ post.content }}<br/>
            {{ post.comment__count }} comment(s)<br/>
            by: {{ post.author }}<br/><br/>
        </li>
    {% endfor %}
    </ul>
{% else %}
    <p>No posts are available.</p>
{% endif %}

Hope this also helps out the others. Thanks for everything guys!

+4  A: 

You can do this using aggregation described here.

Something the likes of:

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    #additional fields here...

class Comment(models.Model):
    post = models.ForeignKeyField(Post)
    #additional fields here...

...

from django.db.models import Count
from project.application.models import Post, Comment

post_list = Post.objects.annotate(Count('comment_set'))
for p in post_list:
    print p.comment_set__count
Hmmm. I am not sure I get this. I will take a look at the link you gave me. Will the value of COUNT() be included in the fields-list returned by the query? In my example above, I could just print out the number_of_comments in my "loop" when iterating thru each row.
wenbert
Yes. The code given here shows iterating through the posts and printing the comment_set__count for each. +1 This is the right way to do it.
Carl Meyer
This worked for me. I did it like this: blog_posts = Blog.objects.all().annotate(Count('comment')).order_by('-pub_date')[:5] and then in my template I printed: post.comment__count
wenbert
+1  A: 

Don't try to produce SQL queries literally in Django. You want to count of all comments on blog entries. As a simple loop, this is simple.

for b in Blog.objects.all():
    c = b.comment_set.count()

That's the kind of code you use in Django instead of a complex SQL query.

This fetches all blog objects and their associated counts, allowing you to present any relevant fields from the blog objects.

Please read http://docs.djangoproject.com/en/dev/topics/db/queries/#topics-db-queries completely for the way to query data in Django.

S.Lott
This is what I want. So, b.comment_set.count() would contain the blog_id and the result of the count()? Or if you could just tell me how do to var_dump() variables in the template file? So that I can just find out for myself. Thanks!
wenbert
@wenbert: What? This isn't SQL. `b.comment_set.count()` is the count of associated comment objects. `b` is the Blog object itself. `b.id` is the internal ID of the blog. `b.somethingElse` is some other attribute of the Blog object.
S.Lott
-1 There is no reason to do it this way (could result in many SQL queries) when Django's ORM (since 1.1) provides the correct way to do it in a single query (see mherren's answer).
Carl Meyer
-1. Agree with Carl Meyer. Annotation, which will do count(..) or .extra(select{'coment_coubt':'subquery'}) is the way to go here.
uswaretech