views:

338

answers:

3

Hey,

So here's the problem. Imagine 2 models: Photographer and Photo. Strict rule is that there can be only 1 photographer for image, so in Photo there's a ForeignKey link to Photographer.


class Photographer(models.Model):
    name = models.CharField(max_length = 40)

class Photo(models.Model):
    name = models.CharField(max_length = 40)
    photographer = models.ForeignKey(Photographer)

Now I'd like to show most famous photographers on a main page with 3 most popular his photos. So it should be something like:


# views.py
def main(request):
    photographers = ...
    return render_to_response("main_page.html", {
                "photographers": photographers
           })

#main_page.html
{% for photo in photographers.photo_set.all()[:3] %}
{{ photo.name }}
{% endfor %}

But Django templating system doesn't allow all() and [:3]. So what is the best way to do that? The most obvious solution would be to create objects on the fly (create array from photographers and photos and pass it to template), but I just don't see this a good way.

Please don't just refer me to Django manual, I read it thoroughly, but couldn't find an answer...

+1  A: 

You got it, you just need to move the logic to the view, and just pass the template the objects once you've got them.

# views.py
def main(request):
    photographers = photographers.photo_set.all()[:3]
    return render_to_response("main_page.html", {
                "photographers": photographers
           })

#main_page.html
{% for photo in photographers %}
{{ photo.name }}
{% endfor %}

Make sure your views contain the logic and the template just shows the data.

Yuval A
Yeah, but I also need to show Photographer's name on a page... So it will be smth like "Photographer John Rain, photos: photo1, photo2, photo3".
Vitaly
+2  A: 

Try using the slice filter:

{% for photo in photographers.photo_set.all|slice:":3" %}

Note that the .all doesn't need to be explicitly called.

Jeff Bradberry
That works, great! I actually found a more generic way myself: add a method to Photographer get_top3photos(self) which just returns self. photo_set.all()[:3] and add a property top_3_photos = property(get_top3photos)So in the template I can refer to this new property {% for photo in photographer.top_3_photos %}
Vitaly
Yes, moving selection out of the template is your best bet. Put it into the view or a template tag.
hughdbrown
+1  A: 

I think Yuval A's almost got it, but not quite - the photos and photographers seem to be mixed up. I think the best approach would be to provide a top_photos attribute on the Photographer model which returns the top 3 (or however many) photos. Then:

{% for photographer in photographers %}
Photographer: {% photographer.name %}
Top Photos:
{% for photo in photographer.top_photos %}
{% photo.name}
{% endfor %}
{% endfor %}

I've left out any HTML formatting in the above, but hopefully the intent is clear.

Vinay Sajip