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” |