Generyczne Widoki
14 July 2008
Comments
Począwszy od Django 1.3 te generyczne widoki zostały oznaczone jako przestarzałe. Należy stosować widoków opartych o klasy
Generic Views - "Ogólne" widoki Django wykonują z góry określoną czynność i są bardzo poręczne przy tworzeniu widoków o "popularnej" funkcjonalności. Generyczne widoki nie zostały stworzone jako zastępstwo własnych widoków. Jeżeli potrzebujemy coś więcej, niż to, co oferuje generyczny widok to musimy napisać własny widok. Oto lista generycznych widoków:
Proste widoki
- django.views.generic.simple.direct_to_template
- django.views.generic.simple.redirect_to
Widoki bazujące na dacie
- django.views.generic.date_based.archive_index
- django.views.generic.date_based.archive_year
- django.views.generic.date_based.archive_month
- django.views.generic.date_based.archive_week
- django.views.generic.date_based.archive_day
- django.views.generic.date_based.archive_today
- django.views.generic.date_based.object_detail
Widoki Listuj/Pokaż
- django.views.generic.list_detail.object_list
- django.views.generic.list_detail.object_detail
Widoki stwórz/edytuj/kasuj
- django.views.generic.create_update.create_object
- django.views.generic.create_update.update_object
- django.views.generic.create_update.delete_object
Proste widoki
Funkcja django.views.generic.simple.direct_to_template parsuje podany szablon przekazując do szablonu słownik {{ params }} zawierający parametry złapane z URLa. Przykład:urlpatterns = patterns('django.views.generic.simple',
(r'^foo/$', 'direct_to_template', {'template': 'foo_index.html'}),
(r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template': 'foo_detail.html'}),
)
Funkcja django.views.generic.simple.redirect_to przekierowuje na podany URL. Podany URL może zawierać formatowanie łańcuchów podobne do słownikowego, które będzie interpolowane względem złapanych z URLa zmiennych. Jeżeli za URL podane będzie None Django zwróci HTTP 410 (Gone). Poniższy przykład przekierowuje z /foo/(id)/ na /bar/(id)/:
urlpatterns = patterns('django.views.generic.simple',
('^foo/(?p<id>\d+)/$', 'redirect_to', {'url': '/bar/%(id)s/'}),
)
Złożone widoki
Kolejne, bardziej złożone widoki przyjmują sporą liczbę dodatkowych argumentów, takie jak:- allow_empty - Wartość logiczna, określająca czy wyświetlać stronę w przypadku braku obiektów do wyświetlania. W przypadku ich braku i wartości False widok zwróci błąd 404
- context_processors - Lista parserów template-context, jakie mają być nałożone na szablon widoku.
- extra_context - Słownik z dodatkowymi zmiennymi, jakie mają być przekazane do szablonu
- mimetype - Typ MIME dla wyniku. Domyślnie przyjmuje wartość DEFAULT_MIME_TYPE.
- template_loader - Określa nazwę modułu ładującego szablon. Domyślnie jest to django.template.loader i nie wymaga modyfikacji
- template_name - Nazwa szablonu do wykorzystania.
django.views.generic.list_detail.object_list listuje wpisy danego modelu z możliwością listowania. Wymagany argument to queryset - czyli "zapytanie" ORMa Django zwracające określony wynik. Przykładowo, mamy prosty model:
class News(models.Model):
title = models.CharField(maxlength=255, verbose_name='Tytuł')
text = models.TextField(verbose_name='Treść')
date = models.DateTimeField(auto_now_add = True, blank=True, verbose_name='Data dodania')
from django.conf.urls.defaults import *
from projekt.aplikacja.models import *
urlpatterns = patterns('',
(r'^/?$', 'django.views.generic.list_detail.object_list', {'queryset':News.objects.all().order_by('-id'), 'paginate_by':10, 'allow_empty':True, 'template_name':'content/news_list.html'}),)
{% for new in object_list %}
<h1>{{ new.title }}</h1><div class="htimeauth">{{ new.date|truncatewords:"1" }}</div>
<p>{{ new.text }}</p>
{% 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 %}
- is_paginated - Wartość logiczna, True jeżeli lista jest stronicowana (i jest wystarczająca ilość wpisów)
- has_next - Wartość logiczna, czy jest kolejna strona stronicowania
- has_previous - Wartość logiczna, czy jest wcześniejsza strona stronicowania
- page - Numer obecnej strony stronicowania
- next, previous - Numer następnej, poprzedniej strony stronicowania
- pages - Liczba wszystkich stron stronicowania
Zmienną stronicowania zamiast w QueryString można wpisać w URL podając go w wyrażeniu:
I http://localhost:8080/2/ będzie równoznaczne z http://localhost:8080/?page=2
(r'^/?$', 'django.views.generic.list_detail.object_list', {'queryset':News.objects.all().order_by('-id'), 'paginate_by':10, 'allow_empty':True, 'template_name':'content/news_list.html'}),
(r'^(?P<page>[0-9]+)/?$', 'django.views.generic.list_detail.object_list', {'queryset':News.objects.all().order_by('-id'), 'paginate_by':10, 'allow_empty':True, 'template_name':'content/news_list.html'}),
django.views.generic.list_detail.object_detail wyświetla określony wpis identyfikowany po ID lub polu typu slugField. Do szablonu przekazana zostanie zmienna {{ object }} zawierająca wszystkie dane wybranego wpisu. Przykładowo prosty model z polem SlugField:
class Page(models.Model):
title = models.CharField(maxlength=255, verbose_name='Tytuł')
slug = models.SlugField(maxlength=255, unique=True, verbose_name='Odnośnik', prepopulate_from=['title'])
description = models.CharField(maxlength=255, verbose_name='Opis')
text = models.TextField(verbose_name='Treść')
from projekt.aplikacja.models import *
....
(r'^p/(?P<slug>[\w\-_]+)/', 'django.views.generic.list_detail.object_detail', {'queryset':Page.objects.all() , 'slug_field':'slug', 'template_name':'content/page_show.html'}),
- slug_field - Określa wartość pola typu SlugField, po której odnaleziony ma być wpis (nazwa zmiennej z URLa jak i nazwa pola typu SlugField modelu)
- slug - Ustawiona wartość pola typu SlugField w modelu
- object_id - Określa wartość pola "id" modelu, po której odnaleziony ma być wpis (nazwa zmiennej z URLa)
Widoki bazujące na dacie - wpisy archiwalne
Widoki generyczne zebrane w django.views.generic.date_based pozwalają wyświetlać wpisy na podstawie dany - sposób na prezencję archiwalnej zawartości.django.views.generic.date_based.archive_index tworzy "stronę główną" dla archiwum - generuję listę lat, dla których istnieją wpisy oraz wyświetla listę najnowszych wpisów.
book_info = {
"queryset" : Book.objects.all(),
"date_field" : "publication_date"
}
......
(r'^books/$', date_based.archive_index, book_info),
Jeżeli template_name nie będzie określone przyjęta zostanie domyślna wartość [etykieta_aplikacji]/[nazwa_modelu]_archive.html
Do szablonu przekazane zostaną dwie zmienne: date_list - listę obiektów datetime.date dla poszczególnych lat, dla których istnieją wpisy. latest - lista ostatnich wpisów.django.views.generic.date_based.archive_year umożliwia tworzenie archiwów dla poszczególnych lat pokazując miesiące, dla których istnieją wpisy. Dochodzi trzeci wymagany argument year określający rok (czterocyfrowy zapis). Argument opcjonalny make_object_list przyjmuje wartość logiczną i w przypadku True do szablonu przekazana zostanie pełna lista obiektów pod zmienną {{ object_list }}. Przykładowy URLconf:
(r'^books/(?P<year>\d{4})/?$', date_based.archive_year, book_info),
django.views.generic.date_based.archive_month umożliwia tworzenie archiwów dla poszczególnych dni miesiąca. Przykładowy URLconf:
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', date_based.archive_month, book_info),
- month - obiekt datetime.date reprezentujący bieżący miesiąc
- next_month - to samo dla kolejnego miesiąca. Jeżeli wypada w przyszłości zmienna przyjmie wartość None.
- previous_month - to samo dla wcześniejszego miesiąca
- object_list - lista obiektów dostępna dla danego miesiąca. Nazwa zmiennej zależy od wartości template_object_name (domyślnie "object"). W przypadku przypisania innej wartości do template_object_name otrzymamy WARTOŚĆ_list
Używanie Generycznych widoków we własnych widokach
Generyczny widok django.views.generic.list_detail.object_list jest bardzo poręczny. Ten jak i inne widoki generyczne można wykorzystać w zwykłych widokach (gdy chcemy jego funkcjonalności ale musimy także wykonać inny dodatkowy kod uniemożliwiający bezpośrednie zastosowanie generycznego widoku). Oto przykład widoku listującego posty danego tematu:def post_list(request, topic_id, pagination_id):
# importujemy generyczny widok
from django.views.generic.list_detail import object_list
# czy temat istnieje
try:
topic = Topic.objects.get(id=topic_id)
except Topic.DoesNotExist:
return HttpResponseRedirect('/forum/')
return object_list(request, topic.post_set.all().order_by('post_date'), paginate_by = 10, page = pagination_id, extra_context = {}, template_name = 'myghtyboard/post_list.html')
(r'^topic/(?P<pagination_id>[0-9]+)/(?P<topic_id>[0-9]+)/$', 'views.post_list'),
RkBlog
Comment article