7.5. Czat (cz. 2)¶
Dodawanie, edycja, usuwanie czy przeglądanie danych zgromadzonych w bazie są typowymi czynnościami w aplikacjach internetowych. Utworzony w scenariuszu Czat (cz. 1) kod ilustruje “ręczną” obsługę żądań GET i POST, w tym tworzenie formularzy, walidację danych itp. Django zawiera jednak gotowe mechanizmy, których użycie skraca i ulepsza programistyczną pracę eliminując potencjalne błędy.
Będziemy rozwijać kod uzyskany po zrealizowaniu punktów 5.4.1 – 5.4.4 scenariusza Czat (cz. 1).
Pobierz więc archiwum
z potrzebnymi plikami
i rozpakuj w katalogu domowym użytkownika. Utworzony zostanie katalog czatpro2
,
w którym będziemy pracować.
Na początku zajmiemy się obsługą użytkowników. Umożliwimy im samodzielne zakładanie kont w serwisie, logowanie i wylogowywanie się. Później zajmiemy się dodawaniem, edycją i usuwaniem wiadomości. Inaczej niż w cz. 1 zadania te zrealizujemy za pomocą tzw. widoków wbudowanych opartych na klasach (ang. class-based generic views ).
7.5.1. Rejestrowanie¶
Na początku pliku czatpro2/czat/urls.py
aplikacji czat importujemy formularz tworzenia użytkownika
(UserCreationForm
) oraz wbudowany widok przenaczony do dodawania danych (CreateView
):
6 7 | from django.contrib.auth.forms import UserCreationForm
from django.views.generic.edit import CreateView
|
Następnie do listy paterns
dopisujemy:
17 18 19 20 | url(r'^rejestruj/', CreateView.as_view(
template_name='czat/rejestruj.html',
form_class=UserCreationForm,
success_url='/'), name='rejestruj'),
|
Powyższy kod wiąże adres URL /rejestruj z wywołaniem widoku wbudowanego jako funkcji
CreateView.as_view()
. Przekazujemy jej trzy parametry:
template_name
– szablon, który zostanie użyty do zwrócenia odpowiedzi;form_class
– formularz, który zostanie przekazany do szablonu;success_url
– adres, na który nastąpi przekierowanie w przypadku braku błędów (np. po udanej rejestracji).
Teraz tworzymy szablon formularza rejestracji, który zapisać należy w pliku czatpro2/czat/templates/czat/rejestruj.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!-- czatpro2/czat/templates/czat/rejestruj.html -->
<html>
<body>
<h1>Rejestracja użytkownika</h1>
{% if user.is_authenticated %}
<p>Jesteś już zarejestrowany jako {{ user.username }}.
<br /><a href="/">Strona główna</a></p>
{% else %}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Zarejestruj</button>
</form>
{% endif %}
</body>
</html>
|
Na koniec wstawimy link na stronie głównej, a więc uzupełniamy plik index.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- czatpro2/czat/templates/czat/index.html -->
<html>
<head></head>
<body>
<h1>Witaj w aplikacji Czat!</h1>
{% if user.is_authenticated %}
<p>Jesteś zalogowany jako {{ user.username }}.</p>
{% else %}
<p><a href="{% url 'czat:rejestruj' %}">Zarejestruj się</a></p>
{% endif %}
</body>
</html>
|
Zwróć uwagę na sposób tworzenia linków w szablonie: {% url 'czat:rejestruj' %}
.
czat
to nazwa przestrzeni nazw zdefiniowanej w pliku adresów projektu
czatpro2/czatpro/urls.py
(namespace='czat'
). Link rejestruj
definiowany jest w parametrze name
w pliku czatpro2/czat/urls.py
aplikacji.
Ćwiczenie: dodaj link do strony głównej w szablonie rejestruj.html
.
Uruchom aplikację (python manage.py runserver
) i przetestuj dodawanie użytkowników:
spróbuj wysłać niepełne dane, np. bez hasła; spróbuj dodać dwa razy tego samego użytkownika.
7.5.2. Wy(logowanie)¶
Na początku pliku urls.py
aplikacji dopisujemy wymagany import:
8 9 | from django.core.urlresolvers import reverse_lazy
from django.contrib.auth import views as auth_views
|
– a następnie:
21 22 23 24 25 26 | url(r'^loguj/', auth_views.login,
{'template_name': 'czat/loguj.html'},
name='loguj'),
url(r'^wyloguj/', auth_views.logout,
{'next_page': reverse_lazy('czat:index')},
name='wyloguj'),
|
Widać, że z adresami /loguj i /wyloguj wiążemy wbudowane w Django widoki login
i logout
importowane z modułu django.contrib.auth.views
. Jedynym nowym
parametrem jest next_page
, za pomocą którego wskazujemy stronę
wyświetlaną po wylogowaniu (reverse_lazy('czat:index')
).
Logowanie wymaga szablonu loguj.html
, który tworzymy i zapisujemy w podkatalogu templates/czat
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!-- czatpro2/czat/templates/czat/loguj.html -->
<html>
<body>
<h1>Logowanie użytkownika</h1>
{% if user.is_authenticated %}
<p>Jesteś już zalogowany jako {{ user.username }}.
<br /><a href="/">Strona główna</a></p>
{% else %}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Zaloguj</button>
</form>
{% endif %}
</body>
</html>
|
Musimy jeszcze określić stronę, na którą powinien zostać przekierowany
użytkownik po udanym zalogowaniu. W tym wypadku na końcu pliku czatpro/czatpro/settings.py
definiujemy wartość zmiennej LOGIN_REDIRECT_URL
:
# czatpro2/czatpro/settings.py
from django.core.urlresolvers import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('czat:index')
Ćwiczenie: Uzupełnij plik index.html
o linki służące do logowania i wylogowania.
7.5.3. Lista wiadomości¶
Chcemy, by zalogowani użytkownicy mogli przeglądać wiadomości wszystkich użytkowników, zmieniać, usuwać i dodawać własne. Najprostszy sposób to skorzystanie z wspomnianych widoków wbudowanych.
Note
Django oferuje wbudowane widoki przeznaczone do typowych operacji:
- DetailView i ListView – (ang. generic display view) widoki przeznaczone do prezentowania szczegółów i listy danych;
- FormView, CreateView, UpdateView i DeleteView – (ang. generic editing views) widoki przeznaczone do wyświetlania formularzy ogólnych, w szczególności służących dodawaniu, uaktualnianiu, usuwaniu obiektów (danych).
Do wyświetlania listy wiadomości użyjemy klasy ListView
.
Do pliku urls.py
dopisujemy importy:
10 11 12 | from django.contrib.auth.decorators import login_required
from django.views.generic import ListView
from czat.models import Wiadomosc
|
– i wiążemy adres /wiadomosci z wywołaniem widoku:
27 28 29 30 31 32 33 | url(r'^wiadomosci/', login_required(
ListView.as_view(
model=Wiadomosc,
context_object_name='wiadomosci',
paginate_by=10),
login_url='/loguj'),
name='wiadomosci'),
|
Zakładamy, że wiadomości mogą oglądać tylko użytkownicy zalogowani. Dlatego
całe wywołanie widoku umieszczamy w funkcji login_required()
.
W wywołaniu ListView.as_view()
wykorzystujemy kolejne parametry
modyfikujące działanie widoków:
model
– podajemy model, którego dane zostaną pobrane z bazy;context_object_name
– pozwala zmienić domyślną nazwę (object_list) listy obiektów przekazanych do szablonu;paginate_by
– pozwala ustawić ilość obiektów wyświetlanych na stronie.
Parametr login_url
określa adres, na który przekierowany zostanie
niezalogowany użytkownik.
Potrzebujemy szablonu, którego Django szuka pod domyślną nazwą
<nazwa modelu>_list.html, czyli w naszym przypadku tworzymy plik
~/czatpro/czat/templates/czat/wiadomosc_list.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <!-- czatpro2/czat/templates/czat/wiadomosc_list.html -->
<html>
<body>
<h1>Wiadomości</h1>
<h2>Lista wiadomości:</h2>
<ol>
{% for wiadomosc in wiadomosci %}
<li>
<strong>{{ wiadomosc.autor.username }}</strong> ({{ wiadomosc.data_pub }}):
<br /> {{ wiadomosc.tekst }}
</li>
{% endfor %}
</ol>
<p><a href="{% url 'czat:index' %}">Strona główna</a></p>
</body>
</html>
|
Kolejne wiadomości odczytujemy i wyświetlamy w pętli przy użyciu tagu {% for %}
.
Dostęp do właściwości obiektów umożliwia operator kropki, np.: {{ wiadomosc.autor.username }}
.
Ćwiczenie: Dodaj link do strony wyświetlającej wiadomości na stronie głównej dla zalogowanych użytkowników.
7.5.4. Dodawanie wiadomości¶
Zadanie to zrealizujemy wykorzystując widok CreateView
. Aby ułatwić
dodawanie wiadomości dostosujemy klasę widoku tak, aby użytkownik
nie musiał wprowadzać pola autor.
Na początek dopiszemy w pliku urls.py
skojarzenie adresu URL
wiadomosc/ z wywołaniem klasy CreateView
jako funkcji:
34 35 36 37 | url(r'^wiadomosc/$', login_required(
views.UtworzWiadomosc.as_view(),
login_url='/loguj'),
name='wiadomosc'),
|
Dalej kodujemy w pliku views.py
. Na początku dodajemy importy:
6 7 8 9 | from django.views.generic.edit import CreateView
from czat.models import Wiadomosc
from django.utils import timezone
from django.contrib import messages
|
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | class UtworzWiadomosc(CreateView):
model = Wiadomosc
fields = ['tekst', 'data_pub']
context_object_name = 'wiadomosci'
success_url = '/wiadomosc'
def get_initial(self):
initial = super(UtworzWiadomosc, self).get_initial()
initial['data_pub'] = timezone.now()
return initial
def get_context_data(self, **kwargs):
context = super(UtworzWiadomosc, self).get_context_data(**kwargs)
context['wiadomosci'] = Wiadomosc.objects.all()
return context
def form_valid(self, form):
wiadomosc = form.save(commit=False)
wiadomosc.autor = self.request.user
wiadomosc.save()
messages.success(self.request, "Dodano wiadomość!")
return super(UtworzWiadomosc, self).form_valid(form)
|
Dostosowując widok ogólny, tworzymy opartą na nim klasę class UtworzWiadomosc(CreateView)
.
Nieomówiona dotąd właściwość fields
pozwala wskazać pola, które mają znaleźć
się na formularzu. Jak widać, pomijamy pole autor
.
Pole to jest jednak wymagane. Aby je uzupełnić, nadpisujemy metodę
form_valid()
, która sprawdza poprawność przesłanych danych i zapisuje je w bazie:
wiadomosc = form.save(commit=False)
– tworzymy obiekt wiadomości, ale go nie zapisujemy;wiadomosc.autor = self.request.user
– uzupełniamy dane autora;wiadomosc.save()
– zapisujemy obiekt;messages.success(self.request, "Dodano wiadomość!")
– przygotowujemy komunikat, który wyświetlony zostanie po dodaniu wiadomości.
Metoda get_initial()
pozwala ustawić domyślne wartości dla wybranych pól.
Wykorzystujemy ją do zainicjowania pola data_pub
aktualna datą (initial['data_pub'] = timezone.now()
).
Metoda get_context_data()
z punktu widzenia dodawania wiadomości
nie jest potrzebna. Pozwala natomiast przekazać do szablonu dodatkowe dane,
w tym wypadku jest to lista wszystkich wiadomości: context['wiadomosci'] = Wiadomosc.objects.all()
.
Wyświetlimy je poniżej formularza dodawania nowej wiadomości.
Domyślny szablon dodawania danych nazywa się <nazwa modelu>_form.html. Możemy go utworzyć
na podstawie szablonu wiadomosc_list.html
. Otwórz go i zapisz pod nazwą
wiadomosc_form.html
. Przed listą wiadomości umieść kod wyświetlający komunikaty i formularz:
6 7 8 9 10 11 12 13 14 15 16 17 18 19 | {% if messages %}
<ul>
{% for komunikat in messages %}
<li>{{ komunikat|capfirst }}</li>
{% endfor %}
</ul>
{% endif %}
<h2>Dodaj wiadomość:</h2>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Zapisz</button>
</form>
|
Ćwiczenie: Jak zwykle, umieść link do dodawanie wiadomości na stronie głównej.
7.5.5. Edycja wiadomości¶
Widok pozwalający na edycję wiadomości i jej aktualizację dostępny będzie
pod adresem /edytuj/id_wiadomości, gdzie id_wiadomosci będzie identyfikatorem
obiektu do zaktualizowania. Zaczniemy od uzupełnienia pliku urls.py
:
38 39 40 41 | url(r'^edytuj/(?P<pk>\d+)/', login_required(
views.EdytujWiadomosc.as_view(),
login_url='/loguj'),
name='edytuj'),
|
Nowością w powyższym kodzie są wyrażenia regularne definiujące adresy z dodatkowym
parametrem, np. r'^edytuj/(?P<pk>\d+)/'
. Część /(?P<pk>\d+)
oznacza,
że oczekujemy 1 lub więcej cyfr (\d+
), które zostaną zapisane w zmiennej o nazwie
pk
(?P<pk>
) – nazwa jest tu skrótem od ang. wyrażenia primary key, co znaczy
“klucz główny”. Zmienna ta zawierać będzie identyfikator wiadomości i dostępna
będzie w klasie widoku, który obsłuży edycję wiadomości.
Na początku pliku views.py
importujemy więc potrzebny widok:
10 | from django.views.generic.edit import UpdateView
|
Dalej tworzymy klasę EdytujWiadomosc
, która dziedziczy, czyli dostosowuje wbudowany
widok UpdateView
:
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | class EdytujWiadomosc(UpdateView):
model = Wiadomosc
from czat.forms import EdytujWiadomoscForm
form_class = EdytujWiadomoscForm
context_object_name = 'wiadomosci'
template_name = 'czat/wiadomosc_form.html'
success_url = '/wiadomosci'
def get_context_data(self, **kwargs):
context = super(EdytujWiadomosc, self).get_context_data(**kwargs)
context['wiadomosci'] = Wiadomosc.objects.filter(
autor=self.request.user)
return context
def get_object(self, queryset=None):
wiadomosc = Wiadomosc.objects.get(id=self.kwargs['pk'])
return wiadomosc
|
Najważniejsza jest tu metoda get_object()
, która pobiera i zwraca wskazaną przez
identyfikator w zmiennej pk wiadomość: wiadomosc = Wiadomosc.objects.get(id=self.kwargs['pk'])
.
Omawianą już metodę get_context_data()
wykorzystujemy, aby przekazać
do szablonu listę wiadomości, ale tylko zalogowanego użytkownika
(context['wiadomosci'] = Wiadomosc.objects.filter(autor=self.request.user)
).
Właściwości model
, context_object_name
, template_name
i success_url
wyjaśniliśmy wcześniej. Jak widać, do edycji wiadomości można wykorzystać ten sam szablon,
którego użyliśmy podczas dodawania.
Formularz jednak dostosujemy. Wykorzystamy właściwość form_class
,
której przypisujemy utworzoną w nowym pliku forms.py
klasę zmieniającą
domyślne ustawienia:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # -*- coding: utf-8 -*-
# czatpro2/czat/forms.py
from django.forms import ModelForm, TextInput
from czat.models import Wiadomosc
class EdytujWiadomoscForm(ModelForm):
class Meta:
model = Wiadomosc
fields = ['tekst', 'data_pub']
exclude = ['autor']
widgets = {'tekst': TextInput(attrs={'size': 60})}
|
Klasa EdytujWiadomoscForm
oparta jest na wbudowanej klasie ModelForm
.
Właściwości formularza określamy w podklasie Meta
:
model
– oznacza to samo co w widokach, czyli model, dla którego tworzony jest formularz;fields
– to samo co w widokach, lista pól do wyświetlenia;exclude
– opcjonalnie lista pól do pominięcia;widgets
– słownik, którego klucze oznaczają pola danych, a ich wartości odpowiadające im w formularzach HTML typy pól i ich właściwości, np. rozmiar.
Żeby przetestować aktualizowanie wiadomości, w szablonie wiadomosc_list.html
trzeba wygenerować linki Edytuj dla wiadomości utworzonych przez zalogowanego użytkownika.
Wstaw w odpowiednie miejsce szablonu, tzn po tagu wyświetlającym tekst wiadomości
({{ wiadomosc.tekst }}
) poniższy kod:
12 13 14 | {% if wiadomosc.autor.username == user.username %}
• <a href="{% url 'czat:edytuj' wiadomosc.id %}">Edytuj</a>
{% endif %}
|
Ćwiczenie: Ten sam link “Edytuj” umieść również w szablonie dodawania.
7.5.6. Usuwanie wiadomości¶
Usuwanie danych realizujemy za pomocą widoku DeleteView
, który importujemy
na początku pliku urls.py
:
13 | from django.views.generic import DeleteView
|
Podobnie, jak w przypadku edycji, usuwanie powiążemy z adresem URL zawierającym
identyfikator wiadomości */usun/id_wiadomości*. W pliku urls.py
dopisujemy:
42 43 44 45 46 47 48 | url(r'^usun/(?P<pk>\d+)/', login_required(
DeleteView.as_view(
model=Wiadomosc,
template_name='czat/wiadomosc_usun.html',
success_url='/wiadomosci'),
login_url='/loguj'),
name='usun'),
|
Warto zwrócić uwagę, że podobnie jak w przypadku listy wiadomości, o ile wystarcza nam
domyślna funkcjonalność widoku wbudowanego, nie musimy niczego implementować w pliku views.py
.
Domyślny szablon dla tego widoku przyjmuje nazwę <nazwa-modelu>_confirm_delete.html,
dlatego uprościliśmy jego nazwę we właściwości template_name
. Tworzymy więc plik
wiadomosc_usun.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!-- czatpro2/czat/templates/czat/wiadomosc_usun.html -->
<html>
<body>
<h1>Wiadomości</h1>
<h2>Usuń wiadomość</h2>
<form method="POST">
{% csrf_token %}
<p>Czy na pewno chcesz usunąć wiadomość:<br /><i>{{ object }}</i>?</p>
<button type="submit">Usuń</button>
</form>
<p><a href="{% url 'czat:index' %}">Strona główna</a></p>
</body>
</html>
|
Tag {{ object }}
zostanie zastąpiony treścią wiadomości zwróconą przez funkcję
“autoprezentacji” __unicode__()
modelu.
Ćwiczenie: Wstaw link “Usuń” (• <a href="{% url 'czat:usun' wiadomosc.id %}">Usuń</a>
) za linkiem “Edytuj” w szablonach wyświetlających listę wiadomości.
7.5.7. Materiały¶
- O Django http://pl.wikipedia.org/wiki/Django_(informatyka)
- Strona projektu Django https://www.djangoproject.com/
- Co to jest framework? http://pl.wikipedia.org/wiki/Framework
- Co nieco o HTTP i żądaniach GET i POST http://pl.wikipedia.org/wiki/Http
Źródła:
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
Utworzony: | 2017-09-08 o 19:38 w Sphinx 1.4.5 |
---|---|
Autorzy: | Patrz plik “Autorzy” |