Generyczne Widoki

Przegląd generycznych widoków Django i ich możliwości

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'}),
)
Żądanie /foo/ wyświetliłoby szablon foo_index.html, a żądanie /foo/15/ szablon foo_detail.html ze zmienną {{ params.id }} o wartości 15. Argument template jest wymagany i wskazuje szablon.

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/'}),
)
Argument url jest wymagany i wskazuje URL, na który ma nastąpić przekierowanie.

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')
A do urls.py dodajemy:
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'}),)
Przypisujemy listowanie news wraz ze stronicowaniem na stronie głównej (/). Zmienna paginate_by określa ile wpisów wyświetlać na jednej stronie. Do szablonu przekazanie zostanie zmienna object_list zawierająca wpisy. Przykładowy szablon:
{% 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 %}
Zmienna QueryString page określa stronę stronicowania dla tego generycznego widoku. Oprócz tego w szablonie mamy zmienne:
  • 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:
(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'}),
I http://localhost:8080/2/ będzie równoznaczne z http://localhost:8080/?page=2

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ść')
I generyczny widok:
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),
queryset, jak i date_field są wymagane. date_field Określa nazwę pola typu DateField lub DateTimeField modelu. Dodatkowe argumenty: allow_future - Wartość logiczna, określająca czy dołączyć obiekty z "przyszłości", num_latest - liczba ostatnich wpisów jakie mają być przekazane do szablonu, domyślnie 15. Oprócz tego obsługiwane są: allow_empty, context_processors, extra_context, mimetype, template_loader, template_name.
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),
Do szablonu zostanie przekazana zmienna date_list, lista obiektów datetime.date miesięcy, dla których istnieją wpisy.

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),
Wymagany argument month określa dany miesiąc. Domyślny format to trzyliterowy skrót nazwy miesiąca (angielskiej nazwy) np.: "jan", "feb". By stosować zapisu liczbowego należy podać argument opcjonalny month_format o wartości "%m". Do szablonu przekazane zostaną zmienne:
  • 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')
Z regułą URLconf:
(r'^topic/(?P<pagination_id>[0-9]+)/(?P<topic_id>[0-9]+)/$', 'views.post_list'),
Widok post_list zwraca object_list czyli generyczny widok listujący wpisy ze stronicowaniem. Podane są takie argumenty jak "paginate_by" czy extra_context zawierający dodatkowe zmienne przekazywane do szablonu. W porównaniu do wersji z URLconf pierwszy argument to "request", a drugi - nasz queryset - te argumenty są wymagane, w takiej kolejności. Po nich możemy podać pozostałe.
blog comments powered by Disqus

Kategorie

Strony