Zapraszam do kolejnego posta na temat widoków klasowych w Django. W poprzednim poście opisałem:
ListViewCreateViewFormViewDeleteView
Dziś omówię widoki oparte na przetwarzaniu dat, to jest:
ArchiveIndexViewYearArchiveViewMonthArchiveViewWeekArchiveViewDayArchiveViewTodayArchiveViewDateDetailView
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 wcontextdostę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 lubNonejeśli nie ma stronicowaniapage_obj: obiekt strony, lubNone
allow_empty: True/False. Interesujący parametr. Jeśli ustawiony naFalsei 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_dayiprevious_day, bo mamy widok dzienny. Dla pozostałych widoków trzeba odpowiednio zmienić, np.next_year. - dodatkowo są dostępne metody z
ArchiveIndexViewopisanego 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.
TodayArchiveViewdziała jakDayArchiveViewz 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!