7.4. Czat (cz. 1)¶
Zastosowanie Pythona i frameworka Django do stworzenia aplikacji internetowej Czat; prostego czata, w którym zarejestrowani użytkownicy będą mogli wymieniać się krótkimi wiadomościami.
Attention
Wymagane oprogramowanie:
- Python v. 2.7.x
- Django v. 1.10.x
- Interpreter bazy SQLite3
7.4.1. Projekt i aplikacja¶
Tworzymy nowy projekt Django oraz szkielet naszej aplikacji. W katalogu domowym wydajemy polecenia w terminalu:
~$ django-admin.py startproject czatpro
~$ cd czatpro
~/czatpro$ python manage.py migrate
~/czatpro$ django-admin.py startapp czat
Powstanie katalog projektu czatpro
z podkatalogiem ustawień o takiej samej nazwie czatpro
.
Utworzona zostanie również inicjalna baza danych z tabelami wykorzystywanymi przez Django.
Dostosowujemy ustawienia projektu: rejestrujemy naszą aplikację w projekcie, ustawiamy polską wersję językową oraz lokalizujemy
datę i czas. Edytujemy plik czatpro/settings.py
:
# czatpro/czatpro/settings.py
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'czat', # rejestrujemy aplikację
)
LANGUAGE_CODE = 'pl' # ustawienie języka
TIME_ZONE = 'Europe/Warsaw' # ustawienie strefy czasowej
Note
Jeżeli w jakimkolwiek pliku, np. settings.py
chcemy używać polskich znaków,
musimy na początku wstawić deklarację kodowania: # -*- coding: utf-8 -*-
Teraz uruchomimy serwer deweloperski, wydając polecenie:
~/czatpro$ python manage.py runserver
Po wpisaniu w przeglądarce adresu 127.0.0.1:8000 zobaczymy stronę powitalną.
Note
- Domyślnie serwer nasłuchuje na porcie
8000
, można to zmienić, podając port w poleceniu:python manage.py runserver 127.0.0.1:8080
. - Lokalny serwer deweloperski zatrzymujemy za pomocą skrótu
Ctrl+C
.
Budowanie aplikacji w Django nawiązuje do wzorca projektowego MVC, czyli Model-Widok-Kontroler. Więcej informacji na ten temat umieściliśmy w osobnym materiale MVC.
7.4.2. Model danych¶
Budując aplikację, zaczynamy od zdefiniowania modelu (zob. model), czyli klasy opisującej tabelę zawierającą wiadomości. Atrybuty klasy odpowiadają polom tabeli. Instancje tej klasy będą reprezentować wiadomości utworzone przez użytkowników, czyli rekordy tabeli. Każda wiadomość będzie zwierała treść, datę dodania oraz wskazanie autora (użytkownika).
W pliku ~/czatpro/czat/models.py
wpisujemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # -*- coding: utf-8 -*-
# czatpro/czat/models.py
from django.db import models
from django.contrib.auth.models import User
class Wiadomosc(models.Model):
"""Klasa reprezentująca wiadomość w systemie"""
tekst = models.CharField(max_length=250)
data_pub = models.DateTimeField()
autor = models.ForeignKey(User)
|
Opisując klasę Wiadomosc
podajemy nazwy poszczególnych właściwości (pól) oraz typy przechowywanych w nich danych.
Po zdefiniowaniu przynajmniej jednego modelu możemy zaktualizować bazę danych,
czyli zmienić/dodać potrzebne tabele:
~/czatpro$ python manage.py makemigrations czat
~/czatpro$ python manage.py migrate
Note
Domyślnie Django korzysta z bazy SQLite zapisanej w pliku db.sqlite3
.
Warto zobaczyć, jak wygląda. W terminalu wydajemy polecenie python manage.py dbshell
,
które otworzy bazę w interpreterze sqlite3
. Następnie:
* .tables
- pokaże listę tabel;
* .schema czat_wiadomosc
- pokaże instrukcje SQL-a użyte do utworzenia podanej tabeli
* .quit
- wyjście z interpretera.
7.4.3. Panel administracyjny¶
Utworzymy panel administratora dla projektu, dzięki czemu będziemy mogli zacząć
dodawać użytkowników i wprowadzać dane. Otwieramy więc plik ~/czat/czat/admin.py
i rejestrujemy w nim nasz model jako element panelu:
1 2 3 4 5 6 7 8 | # -*- coding: utf-8 -*-
# czatpro/czat/admin.py
from django.contrib import admin
from czat.models import Wiadomosc # importujemy nasz model
# rejestrujemy model Wiadomosc w panelu administracyjnym
admin.site.register(Wiadomosc)
|
Note
Warto zapamiętać, że każdy model, funkcję, formularz czy widok, których chcemy użyć,
musimy najpierw zaimportować za pomocą klauzuli typu from <skąd> import <co>
.
Do celów administracyjnych potrzebne nam będzie odpowiednie konto. Tworzymy je, wydając w terminalu poniższe polecenie. Django zapyta o nazwę, email i hasło administratora. Podajemy: “admin”, “”, “admin”.
~/czatpro$ python manage.py createsuperuser
Po ewentualnym ponownym uruchomieniu serwera wchodzimy na adres 127.0.0.1:8000/admin/. Logujemy się podając dane wprowadzone podczas tworzenia bazy. Otrzymamy dostęp do panelu administracyjnego, w którym możemy dodawać nowych użytkowników i wiadomości [1].
[1] | Bezpieczna aplikacja powinna dysponować osobnym mechanizmem rejestracji użytkowników i dodawania wiadomości, tak by nie trzeba było udostępniać panelu administracyjnego osobom postronnym. |
7.4.3.1. Ćwiczenie 1¶
Po zalogowaniu na konto administratora dodaj użytkownika “adam”. Na stronie szczegółów, która wyświetli się po jego utworzeniu, zaznacz opcję “W zespole”, następnie w panelu “Dostępne uprawnienia” zaznacz opcje dodawania (add), zmieniania (change) oraz usuwania (del) wiadomości (wpisy typu: “czat | wiadomosc | Can add wiadomosc”) i przypisz je użytkownikowi naciskając strzałkę w prawo.
Przeloguj się na konto “adam” i dodaj dwie przykładowe wiadomości. Następnie utwórz w opisany wyżej sposób kolejnego użytkownika o nazwie “ewa” i po przelogowaniu się dodaj co najmniej 1 wiadomość.
7.4.3.2. Model w panelu¶
W formularzu dodawania wiadomości widać, że etykiety nie są spolszczone, z kolei
dodane wiadomości wyświetlają się na liście jako “Wiadomosc object”.
Aby poprawić te niedoskonałości, uzupełniamy plik models.py
:
10 11 12 13 14 15 16 17 18 19 20 21 | """Klasa reprezentująca wiadomość w systemie"""
tekst = models.CharField(u'wiadomość', max_length=250)
data_pub = models.DateTimeField(u'data publikacji')
autor = models.ForeignKey(User)
class Meta: # ustawienia dodatkowe
verbose_name = u'wiadomość' # nazwa obiektu w języku polskim
verbose_name_plural = u'wiadomości' # nazwa obiektów w l.m.
ordering = ['data_pub'] # domyślne porządkowanie danych
def __unicode__(self):
return self.tekst # "autoprezentacja"
|
W definicji każdego pola jako pierwszy argument dopisujemy spolszczoną etykietę,
np. u'data publikacji'
. W podklasie Meta
podajemy nazwy modelu w liczbie
pojedynczej i mnogiej. Dodajemy też funkcję __unicode__
, której zadaniem
jest “autoprezentacja” klasy, czyli wyświetlenie treści wiadomości.
Po odświeżeniu panelu administracyjnego (np. klawiszem F5
) nazwy zostaną spolszczone.
Note
Prefiks u
wymagany w Pythonie v.2 przed łańcuchami znaków oznacza
kodowanie w unikodzie (ang. unicode) umożliwiające wyświetlanie m.in. znaków narodowych.
Tip
W Pythonie v.3 zamiast nazwy funkcji _unicode__
należy użyć str
.
7.4.4. Widoki i szablony¶
Panel administracyjny już mamy, ale po wejściu na stronę główną zwykły użytkownik niczego poza standardowym powitaniem Django nie widzi. Zajmiemy się teraz stronami po stronie (:-)) użytkownika.
Aby utworzyć stronę główną, zakodujemy pierwszy widok (zob. więcej »»»),
czyli funkcję o przykładowej nazwie index()
, którą powiążemy z adresem URL głównej strony (/).
Najprostszy widok zwraca jakiś tekst: return HttpResponse("Witaj w aplikacji Czat!")
.
W pliku views.py
umieszczamy:
1 2 3 4 5 6 7 8 9 | # -*- coding: utf-8 -*-
# czatpro/czat/views.py
from django.http import HttpResponse
def index(request):
"""Strona główna aplikacji."""
return HttpResponse("Witaj w aplikacji Czat!")
|
Teraz musimy powiązać widok z adresem url. Na początku do pliku projektu czatpro/urls.py
dopiszemy import ustawień z naszej aplikacji:
19 20 21 22 23 | urlpatterns = [
url(r'^', include('czat.urls', namespace='czat')),
url(r'^czat/', include('czat.urls', namespace='czat')),
url(r'^admin/', include(admin.site.urls)),
]
|
Parametr namespace='czat'
definiuje przestrzeń nazw, w której dostępne będą zdefiniowane
dla naszej aplikacji mapowania między adresami url a widokami.
Następnie tworzymy (!) plik czat/urls.py
o następującej treści:
1 2 3 4 5 6 7 8 9 | # -*- coding: utf-8 -*-
# czatpro/czat/urls.py
from django.conf.urls import url
from czat import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
|
Podstawową funkcją wiążącą adres z widokiem jest url()
. Jako pierwszy parametr przyjmuje wyrażenie
regularne oznaczane r
przed łańcuchem dopasowania. Symbol ^
to początek,
$
– koniec łańcucha. Zapis r'^$'
to adres główny serwera.
Drugi parametr wskazuje widok (funkcję), która ma obsłużyć dany adres.
Trzeci parametr name
pozwala zapamiętać skojarzenie url-a i widoku pod nazwą,
której będzie można użyć np. do wygenerowania adresu linku.
Przetestujmy nasz widok wywołując adres 127.0.0.1:8000
. Powinniśmy zobaczyć tekst
podany jako argument funkcji HttpResponse()
:
Zazwyczaj odpowiedzią na wywołanie jakiegoś adresu URL będzie jednak jakaś
strona zapisana w języku HTML. Szablony takich stron umieszczamy w podkatalogu
templates/nazwa aplikacji
. Tworzymy więc katalog:
~/czatpro$ mkdir -p czat/templates/czat
Następnie tworzymy szablon ~/czatpro/czat/templates/czat/index.html
, który zawiera:
1 2 3 4 5 6 7 | <!-- czatpro/czat/templates/czat/index.html -->
<html>
<head></head>
<body>
<h1>Witaj w aplikacji Czat!</h1>
</body>
</html>
|
W pliku views.py
zmieniamy instrukcje odpowiedzi:
4 5 6 7 8 9 10 11 | # from django.http import HttpResponse
from django.shortcuts import render
def index(request):
"""Strona główna aplikacji."""
# return HttpResponse("Witaj w aplikacji Czat!")
return render(request, 'czat/index.html')
|
Po zaimportowaniu funkcji render()
używamy jej do zwrócenia szablonu.
Jako pierwszy argument podajemy obiekt typu HttpRequest
zawierający informacje o żądaniu,
a jako drugi nazwę szablonu z katalogiem nadrzędnym.
Po uruchomieniu serwera i wpisaniu adresu 127.0.0.1:8000 zobaczymy tekst, który umieściliśmy w szablonie:
7.4.5. (Wy)logowanie¶
Udostępnimy użytkownikom możliwość logowania i wylogowywania się, aby mogli dodawać i przeglądać wiadomości.
Na początku w pliku views.py
, jak zawsze, dopisujemy importy wymaganych obiektów,
później dodajemy widoki loguj()
i wyloguj()
:
6 7 8 9 | from django.contrib.auth import login, logout
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
from django.contrib import messages
|
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | def loguj(request):
"""Logowanie użytkownika"""
from django.contrib.auth.forms import AuthenticationForm
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
login(request, form.get_user())
messages.success(request, "Zostałeś zalogowany!")
return redirect(reverse('czat:index'))
kontekst = {'form': AuthenticationForm()}
return render(request, 'czat/loguj.html', kontekst)
def wyloguj(request):
"""Wylogowanie użytkownika"""
logout(request)
messages.info(request, "Zostałeś wylogowany!")
return redirect(reverse('czat:index'))
|
Widoki mogą obsługiwać zarówno żądania typu GET, kiedy użytkownik chce tylko zobaczyć
jakieś dane na stronie, oraz POST, gdy wysyła informacje poprzez formularz, aby np. zostały zapisane.
Typ żądania rozpoznajemy w instrukcji warunkowej if request.method == 'POST':
.
W widoku logowania korzystamy z wbudowanego w Django formularza AuthenticationForm
,
dzięki temu nie musimy “ręcznie” sprawdzać poprawności przesłanych danych. Po wypełnieniu
formularza przesłanymi danymi (form = AuthenticationForm(request, request.POST)
)
robi to metoda is_valid()
. Jeżeli nie zwróci ona błędu,
możemy zalogować użytkownika za pomocą funkcji login()
,
której przekazujemy żądanie (obiekt typu HttpRequest
) i informację o użytkowniku
zwrócone przez metodę get_user()
formularza.
Tworzymy również informację zwrotną dla użytkownika, wykorzystując system komunikatów:
messages.error(request, "...")
. Tak utworzone komunikaty możemy odczytać
w każdym szablonie ze zmiennej messages
.
Na żądanie wyświetlenia strony (typu GET), widok logowania zwraca szablon loguj.html
,
któremu w słowniku kontekst
udostępniamy pusty formularz logowania:
return render(request, 'czat/loguj.html', kontekst)
.
Wylogowanie polega na użyciu funkcji logout(request)
– wyloguje ona
użytkownika, którego dane zapisane są w przesłanym żądaniu. Po utworzeniu
informacji zwrotnej podobnie jak po udanym logowaniu przekierowujemy użytkownika
na stronę główną (return redirect(reverse('index'))
) z żądaniem jej wyświetlenia (typu GET).
Dalej potrzebny nam szablon logowania ~/czatpro/czat/templates/czat/loguj.html
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!-- czatpro/czat/templates/czat/loguj.html -->
<html>
<body>
<h1>Witaj w aplikacji Czat!</h1>
<h2>Logowanie użytkownika</h2>
{% if not user.is_authenticated %}
<form action="." method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Zaloguj</button>
</form>
{% else %}
<p>Jesteś już zalogowany jako {{ user.username }}</p>
<ul>
<li><a href="{% url 'czat:index'%}">Strona główna</a></li>
</ul>
{% endif %}
</body>
</html>
|
Na początku widzimy, jak sprawdzić, czy użytkownik jest zalogowany ({% if not user.is_authenticated %}
),
co pozwala różnicować wyświetlaną treść. Użytkownikom niezalogowanym wyświetlamy
formularz. W tym celu musimy ręcznie wstawić znacznik <form>
, zabezpieczenie formularza
{% csrf_token %}
oraz przycisk typu submit.
Natomiast przekazany do szablonu formularz Django potrafi wyświetlić automatycznie,
np. używając znaczników akapitów: {{ form.as_p }}
.
Trzeba również zapamiętać, jak wstawiamy odnośniki do zdefiniowanych widoków.
Służy do tego kod typu {% url 'czat:index' %}
– w cudzysłowach podajemy
na początku przestrzeń nazw przypisaną do aplikacji w pliku projektu czatpro/urls.py
(namespace='czat'
), a później nazwę widoku zdefiniowaną w pliku aplikacji
czat/urls.py
(name='index'
).
Komunikaty zwrotne przygotowane dla użytkownika w widokach wyświetlimy po
uzupełnieniu szablonu index.html
. Po znaczniku <h1>
wstawiamy poniższy kod:
7 8 9 10 11 12 13 | {% if messages %}
<ul>
{% for komunikat in messages %}
<li>{{ komunikat|capfirst }}</li>
{% endfor %}
</ul>
{% endif %}
|
Jak widać na przykładach, w szablonach używamy tagów {% %}
pozwalających korzystać
z instrukcji warunkowych if
, pętli for
, czy instrukcji generujących linki url
.
Tagi {{ }}
umożliwiają wyświetlanie wartości przekazanych zmiennych,
np. {{ komunikat }}
lub wywoływanie metod obiektów, np. {{ form.as_p }}.
Zwracany tekst można dodatkowo formatować za pomocą filtrów,
np. wyświetlać go z dużej litery {{ komunikat|capfirst }}
.
Pozostaje skojarzenie widoków z adresami URL. W pliku czat/urls.py
dopisujemy reguły:
9 10 | url(r'^loguj/$', views.loguj, name='loguj'),
url(r'^wyloguj/$', views.wyloguj, name='wyloguj'),
|
Możesz przetestować działanie dodanych funkcji wywołując w przeglądarce adresy:
127.0.0.1:8000/loguj
i 127.0.0.1:8000/wyloguj
. Przykładowy formularz
wygląda tak:
7.4.5.1. Ćwiczenie 2¶
Adresów logowania i wylogowywania nikt w serwisach nie wpisuje ręcznie. Wstaw zatem odpowiednie linki do szablonu strony głównej po bloku wyświetlającym komunikaty. Użytkownik niezalogowany powinien zobaczyć odnośnik Zaloguj, użytkownik zalogowany – Wyloguj. Przykładowe działanie stron może wyglądać tak:
7.4.6. Dodawanie wiadomości¶
Chcemy, by zalogowani użytkownicy mogli dodawać wiadomości, a także przeglądać wiadomości innych.
Jak zwykle, zaczynamy od widoku o nazwie np. wiadomosci()
powiązanego z adresem /wiadomosci,
który zwróci szablon wiadomosci.html
. W odpowiedzi na żądanie GET wyświetlimy
formularz dodawania oraz listę wiadomości. Kiedy dostaniemy żądanie typu POST
(tzn. kiedy użytkownik wyśle formularz), spróbujemy zapisać nową wiadomość w bazie.
Do pliku views.py
dodajemy importy i kod funkcji:
10 11 | from czat.models import Wiadomosc
from django.utils import timezone
|
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | def wiadomosci(request):
"""Dodawanie i wyświetlanie wiadomości"""
if request.method == 'POST':
tekst = request.POST.get('tekst', '')
if not 0 < len(tekst) <= 250:
messages.error(
request,
"Wiadomość nie może być pusta, może mieć maks. 250 znaków!")
else:
wiadomosc = Wiadomosc(
tekst=tekst,
data_pub=timezone.now(),
autor=request.user)
wiadomosc.save()
return redirect(reverse('wiadomosci'))
wiadomosci = Wiadomosc.objects.all()
kontekst = {'wiadomosci': wiadomosci}
return render(request, 'czat/wiadomosci.html', kontekst)
|
Po sprawdzeniu typu żądania wydobywamy treść przesłanej wiadomości
ze słownika request.POST
za pomocą metody get('tekst', '')
. Jej pierwszy argument
to nazwa pola formularza użytego w szablonie, które chcemy odczytać.
Drugi argument oznacza wartość domyślną, przydatną, jeśli
pole będzie niedostępne.
Po sprawdzeniu długości wiadomości (if not 0 < len(tekst) <= 250:
),
możemy ją utworzyć wykorzystując konstruktor naszego modelu, podając
jako nazwane argumenty wartości kolejnych pól:
Wiadomosc(tekst=tekst, data_pub=timezone.now(), autor=request.user)
.
Zapisanie nowej wiadomości w bazie sprowadza się do polecenia wiadomosc.save()
.
Pobranie wszystkich wiadomości z bazy realizuje kod: Wiadomosc.objects.all()
.
Widać tu, że używamy systemu ORM, a nie “surowego” SQL-a.
Zwrócony obiekt umieszczamy w słowniku kontekst
i przekazujemy do szablonu.
Zadaniem szablonu zapisanego w pliku ~/czat/czat/templates/wiadomosci.html
będzie wyświetlenie komunikatów zwrotnych, np. błędów, formularza dodawania
i listy wiadomości.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!-- czatpro/czat/templates/czat/wiadomosci.html -->
<html>
<head></head>
<body>
<h1>Witaj w aplikacji Czat!</h1>
{% if messages %}
<ul>
{% for komunikat in messages %}
<li>{{ komunikat|capfirst }}</li>
{% endfor %}
</ul>
{% endif %}
<h2>Dodaj wiadomość</h2>
<form action="." method="POST">
{% csrf_token %}
<input type="text" name="tekst" />
<input type="submit" value="Zapisz" />
</form>
<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>
</body>
</html>
|
Powyżej widać, że inaczej niż w szablonie logowania formularz przygotowaliśmy ręcznie (<input type="text" name="tekst" />
),
dalej pokażemy, jak można sprawić, aby framework robił to za nas. Widać również, że możemy
wyświetlać atrybuty przekazanych w kontekście obiektów reprezentujących dane pobrane z bazy,
np. {{ wiadomosc.tekst }}
.
Widok wiadomosci()
wiążemy z adresem /wiadomosci w pliku czat/urls.py
,
nadając mu nazwę wiadomosci:
11 | url(r'^wiadomosci/$', views.wiadomosci, name='wiadomosci'),
|
7.4.6.1. Ćwiczenie 3¶
- W szablonie widoku strony głównej dodaj link do wiadomości dla zalogowanych użytkowników.
- W szablonie wiadomości dodaj link do strony głównej.
- Zaloguj się i przetestuj wyświetlanie [2] i dodawanie wiadomości pod adresem 127.0.0.1:8000/wiadomosci/. Sprawdź, co się stanie po wysłaniu pustej wiadomości.
[2] | Jeżeli nie dodałeś do tej pory żadnej wiadomości, lista na początku będzie pusta. |
Poniższe zrzuty prezentują efekty naszej pracy:
Przetestuj działanie aplikacji.
7.4.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” |