Zapraszam do kolejnego posta na temat widoków klasowych w Django. W poprzednim poście opisałem:
ListView
CreateView
FormView
DeleteView
Dziś omówię widoki oparte na przetwarzaniu dat, to jest:
ArchiveIndexView
YearArchiveView
MonthArchiveView
WeekArchiveView
DayArchiveView
TodayArchiveView
DateDetailView
Cała seria postów jest rozwinięciem webinaru, w którym pokazałem jak szybko zbudować stronę internetową z wykorzystaniem CBV: Django w godzinę: Tworzenie aplikacji z Class Based Views.
Podstawowe informacje
Widoki oparte na datach są dostępne w django.views.generic.dates
. Wykorzystuje się je do wyświetlania stron szczegółowych, dla danych opartych na datach. Przykładem może być blog, archiwum dowolnych danych, wiadomości medialne i tym podobne.
Podobnie jak w poprzednim poście, do przykładowego kodu wykorzystam model Note
(notatki).
# models.py
from django.db import models
# Create your models here.
class Note(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self) -> str:
return self.title
źródło: Własne: https://github.com/jacoor/django-w-godzine-webinar/
ArchiveIndexView
ArchiveIndexView
tworzy stronę wyświetlającą najnowsze obiekty "na górze" - czyli posortowane po dacie, od najnowszych. Coś jak feed Facebooka. Nie pokazuje obiektów z datą z przyszłości, chyba że ustawi mu się parametr future
na True.
# views.py
from django.views.generic.dates import (
ArchiveIndexView
)
from django.urls import reverse_lazy
from .models import Note
class NoteArchiveIndeksView(ArchiveIndexView):
model = Note
date_field = "created_at"
template_name = "notes_archive.html"
{# notes_archive.html #}
<h1>Notatki - sortowane po dacie</h1>
<ul>
{% for object in object_list %}
<li><a href="{% url 'note_detail' object.pk %}">{{object}}</a></li>
{% endfor %}
</ul>
<h1>Notatki - Lata z obiektami</h1>
<ul>
{% for object in date_list %}
<li>{{object}}</li>
{% endfor %}
</ul>
żródło: Własne
Powyższy kod wyświetli na stronie wszystkie obiekty klasy Note
. Obiekty będą dostępne w szablonie pod standardową zmienną object_list
. Tę nazwę można zmienić poprzez atrybut context_object_name
. Dokumentacja twierdzi, że obiekty powinny być dostępne pod zmienną latest
- w praktyce latest = object_list
. W końcu ArchiveIndexView
dziedziczy po django.views.generic.list.MultipleObjectMixin
który wykorzystuje objects_list
.
Najważniejsze parametry i metody dostępne w ArchiveIndexView
, są właściwie takie same jak dla ListView
:
date_field
- pole z datą, która będzie wykorzystywana do generowania widoku- szablon zawiera dodatkowo obiekt
date_list
, który domyślnie zawiera wszystkie lata, które mają dostępne obiekty, w kolejności malejącej. Ten okres "grupowania" można zmienić korzystając z parametrudate_list_period
. get_context_data
- metoda pozwalająca na dodanie własnych danych do szablonu: https://akademiait.com.pl/class-based-views-w-django-wygodny-sposob-tworzenia-widokow-czesc-1/#get_context_datamodel
: nazwa modelu do pobrania z bazy. Domyślnie pobiera wszystkie obiekty, czylimodel.objects.all()
- metoda
get_queryset
: umożliwia zdefiniowanie własnego zapytania do bazy. Domyślnie, Django prosi o wszystkie obiekty modelu:model.objects.all()
. Użycie własnego queryset pozwala na przykład na sortowanie po cenie, nazwisku, tytule, lub wyszukiwanie. Co ciekawe, nie musi zwracać Queryset, wystarczy iterable, czyli obiekt iterowany w postaci listy. To z kolei umożliwia zwracanie danych, na przykład różnych modeli: książek i czasopism. template_name
: nazwa szablonu, który będzie wykorzystany do wyświetlenia danychpaginate_by
: numer, określa ilość elementów na stronie jeśli chcemy korzystać ze stronicowania. Więcej informacji zawiera dokumentacja: https://docs.djangoproject.com/en/4.2/ref/class-based-views/mixins-multiple-object/#django.views.generic.list.MultipleObjectMixin.paginate_by. Warto spojrzeć na parametrypaginate_orphans
,page_kwarg
. Przy wykorzystaniu stronicowania wcontext
dostępne są dodatkowe obiekty, które umożliwiają pełną obsługę listy stron i przechodzenia między nimi:is_paginated
: True jeśli jest wykorzystane stronicowaniepaginator
: instancja paginatora lubNone
jeśli nie ma stronicowaniapage_obj
: obiekt strony, lubNone
allow_empty
: True/False. Interesujący parametr. Jeśli ustawiony naFalse
i w bazie nie ma obiektów do wyświetlenia, Django zwróci błąd404 Not Found
.
Pozostałe widoki oparte na dacie są również bardzo zbliżone do ich "bezdatowych" odpowiedników.
YearArchiveView
, MonthArchiveView
, WeekArchiveView
, DayArchiveView
, TodayArchiveView
Wszystkie powyższe są prawie takie same. Wyświetlają widoki zawierające posty z danego okresu czasu:
YearArchiveView
- Roczna strona archiwum pokazująca wszystkie dostępne miesiące w danym roku. Obiekty z datą przyszłą nie są wyświetlane, chyba że ustawisz parametr allow_future na True.MonthArchiveView
- Miesięczna strona archiwum pokazująca wszystkie obiekty w danym miesiącu. Obiekty z datą przyszłą nie są wyświetlane, chyba że ustawisz parametr allow_future na True.WeekArchiveView
- Tygodniowa strona archiwum pokazująca wszystkie obiekty w danym tygodniu. Obiekty z datą przyszłą nie są wyświetlane, chyba że ustawisz parametr allow_future na True.DayArchiveView
- Strona archiwum dnia, pokazująca wszystkie obiekty w danym dniu. Dni w przyszłości zgłaszają błąd 404, niezależnie od tego, czy istnieją obiekty dla przyszłych dni, chyba że ustawisz allow_future na True.TodayArchiveView
- Strona archiwum dziennego, pokazująca wszystkie obiekty na dzisiaj. Jest to dokładnie to samo, co django.views.generic.dates.DayArchiveView, z tą różnicą, że zamiast argumentów rok/miesiąc/dzień używana jest dzisiejsza data.
Z uwagi na ich podobieństwo, ograniczę się do przykładu z DayArchiveView
.
# views.py
from django.views.generic.dates import (
DayArchiveView
)
class NoteDayArchiveView(DayArchiveView):
model = Note
date_field = "created_at"
template_name = "notes_archive.html"
{# notes_archive.html #}
<h1>Notes</h1>
<h2> {{day}}</h2>
{% if previous_day %}
<a href="{% url 'note_day_archive' previous_day.year previous_day.month previous_day.day%}">{{previous_day}}</a>
{% endif %}
{% if next_day %}
<a href="{% url 'note_day_archive' next_day.year next_day.month next_day.day%}">{{next_day}}</a>
{% endif %}
<ul>
{% for note in object_list %}
<li><a href="{% url 'note_details' note.pk %}">{{ note.title }}</a> {{note.created_at}} <a href="{% url 'note_delete' note.pk %}">usun</a></li>
{% empty %}
<li>No notes yet.</li>
{% endfor %}
</ul>
# urls.py
from django.urls import path
from .views import NoteDayArchiveView
urlpatterns = [
path('archiwum-dzienne/<int:year>/<str:month>/<int:day>/', NoteDayArchiveView.as_view(), name='note_day_archive'),
]
żródło: Własne
Najważniejsze parametry i metody:
- każdy z tych widoków zawiera bieżący obiekt, pod nazwą
day
,week
,month
,year
, zależnie od widoku, który wykorzystujemy - każdy z widoków zawiera link do następnego/poprzedniego obiektu, który można wykorzystać w paginacji. W przykładzie jest to
next_day
iprevious_day
, bo mamy widok dzienny. Dla pozostałych widoków trzeba odpowiednio zmienić, np.next_year
. - dodatkowo są dostępne metody z
ArchiveIndexView
opisanego powyżej. - Warto zajrzeć do dokumentacji Django: https://docs.djangoproject.com/en/4.2/ref/class-based-views/generic-date-based/#yeararchiveview - zawiera kilka dodatkowych informacji.
TodayArchiveView
działa jakDayArchiveView
z tym, że data jest ustawiona na dzisiaj.
DateDetailView
Reprezentuje pojedynczy obiekt. Jeśli obiekt ma wartość daty w przyszłości, widok domyślnie zgłosi błąd 404, chyba że ustawisz allow_future na True. Zawiera dokładnie te same informacje co DetailView
opisywany w Class-Based Views w Django: Wygodny sposób tworzenia widoków, część 1, czyli {{object}}
w szablonie zawierający dane do wyświetlenia.
# views.py
from django.views.generic.dates import (
DateDetailView
)
class NoteDayDetailView(DateDetailView):
model = Note
template_name = 'note_detail.html'
context_object_name = 'note'
date_field = "created_at"
{# note_detail.html #}
<h1>{{note.title}}</h1>
<p>{{note.content}}</p>
<p><a href="{% url 'note_edit' note.pk %}">edytuj</a></p>
<p><a href="{% url 'note_delete' note.pk %}">usun</a></p>
<p><a href="{% url 'note_list' %}">Back</a></p>
# urls.py
from .views import
urlpatterns = [
path("<int:year>/<str:month>/<int:day>/<int:pk>/",
NoteDayDetailView.as_view(),
name="archive_date_detail")
]
żródło: Własne
Podsumowanie
Przyznam szczerze, że nigdy nie użyłem widoków opartych na dacie w moich projektach. Żałuję. Zapewne oszczędziłbym sobie sporo stresu. Widać, że są to potężne widoki i dobrze użyte, mogą oszczędzić masę pracy - na blogach, serwisach z wiadomościami i innych. Jak w każdym frameworku, trzeba znać narzędzie, z którym się pracuje i umieć dostosować je do swoich potrzeb.
To już ostatni post z całej serii. Mam nadzieję, że posty i powiązany webinar będą pomocne dla wszystkich, którzy chcą zgłębić temat Class-Based Views w Django i wykorzystać je w swoich projektach.
Webinar można zobaczyć na Youtube Django w godzinę: Tworzenie aplikacji z Class Based Views, a pozostałe posty na akademiait.com.pl
Zapraszam do zadawania pytań przez formularz kontaktowy. Oczywiście, proponuję też samemu poeksperymentować z kodem. Jeśli natomiast potrzebujesz indywidualnych konsultacji, zapraszam do mentoringu
Spodobał Ci się post?
Podziel się nim!
Masz uwagi do posta, chcesz porozmawiać, szukasz pomocy z Pythonem i Django? Napisz do mnie!