Teraz zajmiemy się rozbudową naszej przykładowej aplikacji. Na początek chcielibyśmy wyświetlać listę wiadomości ze stronicowaniem wyników. Django udostępnia bardzo proste rozwiązanie tego problemu. Zamiast
render_to_response wystarczy użyć
object_list(request, QUERYSET, paginate_by=INT, extra_context={}, template_name='SZABLON.HTML'). Oto kod widoku głównego, oraz widoku wyświetlającego wiadomości z podanej kategorii:
# -*- coding: utf-8 -*-
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.views.generic.list_detail import object_list
from news.models import *
def index(request):
news = News.objects.all().order_by('-id')
return object_list(
request,
news,
paginate_by = 10,
extra_context = {},
template_name = 'index.html')
def news_by_category(request, slug):
c = Category.objects.get(slug=slug)
#news = News.objects.filter(category=c).order_by('-id')
news = c.news_set.all()
return object_list(
request,
news,
paginate_by = 10,
extra_context = {'c':c},
template_name = 'news_by_category.html')
Szablon
index.html też ulega pewnym modyfikacjom:
{% for n in object_list %}
<h3><a href="/news/{{ n.slug }}/">{{ n.title }}</a></h3>
{{ n.text|safe }}<br />
<b>Dodane</b>: {{ n.date|date:"Y.m.d H:i" }}, <b>Kategorie</b>:
{% for i in n.category.all %}
<a href="/{{ i.slug }}/">{{ i.name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
{% endfor %}
{% if has_previous %}
<div style="text-align:center;"><a href="/?page={{ previous }}"><b>Nowsze Wiadomości</b></a></div>
{% endif %}
{% if has_next %}
<div style="text-align:center;"><a href="/?page={{ next }}"><b>Starsze Wiadomości</b></a></div>
{% endif %}
Lista stronicowanych elementów dostępna jest pod zmienną
object_list. Dodatkowo dostępne są zmienne takie jak "has_previous" i "previous", czy "has_next" i "next", gdzie drugie zmienne z tych par zawierają numer strony stronicowania odpowiednio poprzedniej jak i następnej. Także dodaliśmy wyświetlanie listy kategorii każdej wiadomości przemiatając w pętli
n.category.all. Tytuł wiadomości jest też odnośnikiem do strony, która będzie miała za zadanie wyświetlenie jego treści, a także umożliwienie komentowania. Na razie prosty widok:
def show_news(request, slug):
news = News.objects.get(slug=slug)
return render_to_response('show_news.html', {'news': news}, context_instance=RequestContext(request))
urls.py dla widoków wygląda następująco:
from django.conf.urls.defaults import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^news/(?P<slug>[\w\-_]+)/$', 'news.views.show_news'),
(r'^(?P<slug>[\w\-_]+)/$', 'news.views.news_by_category'),
(r'^/?$', 'news.views.index'),
)
W efekcie mamy praktycznie gotową aplikację wiadomości:
Teraz na chwilę zajmiemy się wyglądem. Stosujemy kilka szablonów bez stylowania itp. By strona wyglądała porządnie wykorzystamy darmowy szablon HTML
HigherGround. Rozpakuj paczkę ZIP i katalog "images" przenieś do katalogu "site_media", a plik "index.html" przenieś do katalogu "templates" jako "
base.html", w którym zmieniamy odnośnik do pliku CSS na:
<link rel="stylesheet" href="/site_media/images/HigherGround.css" type="text/css" />
Oraz zastępujemy całą zawartość DIVa o id "main" definicją bloka:
<div id="main">
{% block main %}{% endblock %}
</div>
W
urls.py dodajemy regułę do obsługi plików statycznych (tylko na serwerze deweloperskim!):
import os.path
...
(r'^site_media/(.*)$', 'django.views.static.serve', {'document_root': os.path.join(os.path.dirname(__file__), 'site_media')}),
Po zmodyfikowaniu
index.html do postaci:
{% extends "base.html" %}
{% block main %}
{% for n in object_list %}
<h3><a href="/news/{{ n.slug }}/">{{ n.title }}</a></h3>
{{ n.text|safe }}<br />
<b>Dodane</b>: {{ n.date|date:"Y.m.d H:i" }}, <b>Kategorie</b>:
{% for i in n.category.all %}
<a href="/{{ i.slug }}/">{{ i.name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
{% endfor %}
{% if has_previous %}
<div style="text-align:center;"><a href="/?page={{ previous }}"><b>Nowsze Wiadomości</b></a></div>
{% endif %}
{% if has_next %}
<div style="text-align:center;"><a href="/?page={{ next }}"><b>Starsze Wiadomości</b></a></div>
{% endif %}
{% endblock %}
Powinniśmy uzyskać listę wiadomości ładnie umieszczoną w docelowym szablonie:
Zastosowaliśmy tutaj dziedziczenie szablonów. Stworzyliśmy główny "szkieletowy" szablon i zdefiniowaliśmy w nim blok w miejscu, gdzie powinna wyświetlać się treść. W szablonach używanych przez widoki bezpośrednio dodaliśmy informację o dziedziczeniu szablonu -
{% extends "base.html" %}, a istniejący kod umieściliśmy w bloku o zdefiniowanej wcześniej nazwie. Podobnie edytujemy pozostałe szablony. Można też zdefiniować dodatkowe bloki, np. w tagu TITLE do dynamicznego wstawiania tytułu news do tytułu strony.
Jak wyświetlić listę kategorii w menu po prawej stronie? Potrzebować będziemy listy kategorii dostępnej globalnie w każdym szablonie (a dokładniej base.html). W tym przypadku stworzymy własny procesor szablonów (TEMPLATE_CONTEXT_PROCESSOR), który doda taką listę. W katalogu projektu stwórz plik "
globs.py" o kodzie:
#!/usr/bin/python
# TEMPLATE_CONTEXT_PROCESSOR
from news.models import Category
def globs(request):
cats = Category.objects.all()
return {'cats': cats}
Do
settings.py dodaj:
TEMPLATE_CONTEXT_PROCESSORS = ("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"globs.globs",)
I gotowe. Wpis procesora szablonów na liście "TEMPLATE_CONTEXT_PROCESSORS" ma postać "
nazwa_pliku.nazwa_funkcji". Teraz w base.html możemy dodać listowanie kategorii:
<h3>Kategorie</h3>
<ul class="sidemenu">
{% for i in cats %}
<li>{% if i.icon %}<img src="/site_media/{{ i.icon }}" alt="" /> {% else %}<img src="/site_media/default.png" alt="" /> {% endif %}<a href="/{{ i.slug }}/">{{ i.name }}</a></li>
{% endfor %}
</ul>
- Dodane: 08.09.2008 przez riklaunim