Main Listing
We’ll start by creating a listing of all albums with a few thumbnails of images. A lot of the code
will be similar to the Blog App. Our url will be /photo/, function will be called main() and
we’ll keep list.html as the template name. Here’s our view:
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import get_object_or_404, render_to_response
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.forms import ModelForm
from settings import MEDIA_URL
from dbe.photo.models import *
def main(request):
"""Main listing."""
albums = Album.objects.all()
if not request.user.is_authenticated():
albums = albums.filter(public=True)
paginator = Paginator(albums, 10)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
albums = paginator.page(page)
except (InvalidPage, EmptyPage):
albums = paginator.page(paginator.num_pages)
for album in albums.object_list:
album.images = album.image_set.all()[:4]
return render_to_response("photo/list.html", dict(albums=albums, user=request.user,
media_url=MEDIA_URL))
We’re only listing albums that are set to public for users who aren’t logged in. I’ll speak in
more detail about security at the end of the tutorial. Here is the urlconf line:
...and list.html (don’t forget to change links in pbase.html):
{% extends "pbase.html" %}
{% block content %}
<div class="main">
<!-- Albums -->
<ul>
{% for album in albums.object_list %}
<div class="title">{{ album.title }} ({{ album.image_set.count }} images)</div>
<ul>
{% for img in album.images %}
<a href="{{ media_url }}{{ img.image.name }}"><img border="0" alt=""
src="{{ media_url }}{{ img.thumbnail2.name }}" /></a>
{% endfor %}
</ul>
{% endfor %}
</ul>
<!-- Next/Prev page links -->
{% if albums.object_list and albums.paginator.num_pages > 1 %}
<div class="pagination">
<span class="step-links">
{% if albums.has_previous %}
<a href= "?page={{ albums.previous_page_number }}">previous << </a>
{% endif %}
<span class="current">
Page {{ albums.number }} of {{ albums.paginator.num_pages }}
</span>
{% if albums.has_next %}
<a href="?page={{ albums.next_page_number }}"> >> next</a>
{% endif %}
</span>
</div>
{% endif %}
</div>
{% endblock %}
Here’s our beautiful, amazing front page (with a bit of styling added):
As you can see, we’re using medium-sized thumbnails. You could also add an option to
switch between the two sizes and add more sizes, as well.
Album View
Let’s add a view for a single album: the url will be: /photo/{id}/, function name:
album(), template: album.html.
We’ll need to add the link to main listing:
<div class="title"><a href="{% url photo.views.album album.pk %}">{{ album.title }}</a>
({{ album.image_set.count }} images)</div>
Here’s our view and the urlconf line:
def album(request, pk):
"""Album listing."""
album = Album.objects.get(pk=pk)
if not album.public and not request.user.is_authenticated():
return HttpResponse("Error: you need to be logged in to view this album.")
images = album.image_set.all()
paginator = Paginator(images, 30)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
images = paginator.page(page)
except (InvalidPage, EmptyPage):
images = paginator.page(paginator.num_pages)
return render_to_response("photo/album.html", dict(album=album, images=images, user=request.user,
media_url=MEDIA_URL))
I will only show the main loop since the template is almost the same:
<!-- Images -->
<ul>
<div class="title">{{ album.title }}</div>
<ul>
{% for img in images.object_list %}
<a href="{{ media_url }}{{ img.image.name }}"><img border="0" alt=""
src="{{ media_url }}{{ img.thumbnail2.name }}" /></a>
{% endfor %}
</ul>
</ul>
In exactly the same way, the next thing to do is to add a page for a single image: url, function
and template will all be called image:
(r"^image/(\d+)/$", "image"),
def image(request, pk):
"""Image page."""
img = Image.objects.get(pk=pk)
return render_to_response("photo/image.html", dict(image=img, user=request.user,
backurl=request.META["HTTP_REFERER"], media_url=MEDIA_URL))
<a href="{{ backurl }}"><< back</a>
<!-- Image -->
<ul>
{% if image.title %}
<div class="title">{{ image.title }}</div>
{% endif %}
<ul>
<img border="0" alt="" src="{{ media_url }}{{ image.image.name }}" width="900" />
</ul>
</ul>
Slideshow
The next logical thing to do is to have some sort of slideshow functionality. I’m not going to add
a javascript slideshow since I want to concentrate on Django, but that’s something you could
easily do yourself. I will just have a page with full-size images stacked vertically.
First we’ll change the view function to allow full or thumbnail view and add a urlconf line:
def album(request, pk, view="thumbnails"):
"""Album listing."""
num_images = 30
if view == "full": num_images = 10
album = Album.objects.get(pk=pk)
images = album.image_set.all()
paginator = Paginator(images, num_images)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
images = paginator.page(page)
except (InvalidPage, EmptyPage):
images = paginator.page(paginator.num_pages)
return render_to_response("photo/album.html", dict(album=album, images=images,
user=request.user, view=view, media_url=MEDIA_URL))
(r"^(\d+)/(full|thumbnails)/$", "album"),
Since it’s common to have images that are larger than full screen, we’ll limit width to 900 pixels
in album.html to make full view manageable:
<!-- Images -->
<ul>
<div class="title">{{ album.title }}</div>
<div class="right">
View:
<a href="{% url photo.views.album album.pk 'thumbnails' %}">thumbnails</a>
<a href="{% url photo.views.album album.pk 'full' %}">full</a>
</div>
{% for img in images.object_list %}
{% if view == "full" %}
<a href="{% url photo.views.image img.pk %}"><img border="0" alt=""
src="{{ media_url }}{{ img.image.name }}"
{% if img.width > 900 %}width="900"{% endif %} /></a>
{% else %}
<a href="{% url photo.views.image img.pk %}"><img border="0" alt=""
src="{{ media_url }}{{ img.thumbnail2.name }}" /></a>
{% endif %}
{% endfor %}
</ul>
And here’s our full album view:
On to part III!