8.1. 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.
Uwaga
Wymagane oprogramowanie:
- Python v. 3.x
- Django v. 1.11.2
- Interpreter bazy SQLite3
8.1.1. Środowisko¶
W katalogu domowym tworzymy wirtualne środowisko Pythona:
~$ virtualenv -p python3 pve3
~$ source pve3/bin/activate
(pve3) ~$ pip install Django==1.11.2
Ostrzeżenie
Polecenie source pve3/bin/activate
aktywuje wirtualne środowisko Pythona.
Zawsze wydajemy je przed rozpoczęciem pracy nad projektem. Innymi słowy w terminalu
ścieżka katalogu musi być poprzedzona prefiksem wirtualnego środowiska: (pve3)
.
8.1.2. Projekt i aplikacja¶
Utworzymy nowy projekt Django. Wydajemy polecenia:
(pve3) ~/$ django-admin.py startproject czat1
(pve3) ~$ cd czat1
(pve3) ~$ python manage.py migrate
startproject
– tworzy katalogczat1
z podkatalogiem ustawień projektu o takiej samej nazwie (czat1
),migrate
– tworzy inicjalną bazę danych z tabelami wykorzystywanymi przez Django.
Struktura plików projektu – w terminalu wydajemy jedno z poleceń:
(.pve) ~/czat1$ tree -L 2
[lub]
(.pve) ~/czat1$ ls -R

Zewnętrzny katalog czat1
to tylko pojemnik na projekt, jego nazwę można zmieniać.
Zawiera on:
manage.py
– skrypt Pythona do zarządzania projektem;db.sqlite3
– bazę danych w domyślnym formacie SQLite3.
Katlog projektu czat1/czat1
zawiera:
settings.py
– konfiguracja projektu;urls.py
– lista obsługiwanych adresów URL;wsgi.py
– plik konfiguracyjny wykorzystywany przez serwery WWW.
Plik __init__.py
obecny w danym katalogu wskazuje, że dany katalog jest modułem Pythona.
8.1.3. Serwer deweloperski¶
Serwer uruchamiamy poleceniem w terminalu:
(pve3) ~/czat1$ python manage.py runserver
Łączymy się z serwerem wpisując w przeglądarce adres: 127.0.0.1:8000
.
W terminalu możemy obserwować żądania obsługiwane przez serwer.
Większość zmian w kodzie nie wymaga restartowania serwera.
Serwer zatrzymujemy naciskając w terminalu skrót CTRL+C
.

8.1.4. Aplikacja¶
W ramach jednego projektu (serwisu internetowego) może działać wiele aplikacji. Utworzymy teraz aplikację czat i zbadamy jej strukturę plików:
(.pve) ~/czat1$ python manage.py startapp czat
(.pve) ~/czat1$ tree czat
lub:
(.pve) ~/czat1$ ls -R czat

Katalog aplikacji czat1/czat
zawiera:
apps.py
– ustawienia aplikacji;admin.py
– konfigurację panelu administracyjnego;models.py
– plik definiujący modele danych przechowywanych w bazie;views.py
– plik zawierający funkcje lub klasy definiujące tzw. widoki (ang. views), obsługujące żądania klienta przychodzące do serwera.
8.1.5. Ustawienia projektu¶
Dostosujemy ustawienia projektu: zarejestrujemy aplikację w projekcie,
ustawimy polską wersję językową oraz zlokalizujemy datę i czas.
Edytujemy plik czat1/settings.py
:
# czat1/settings.py
INSTALLED_APPS = [
'czat.apps.CzatConfig', # rejestrujemy aplikacje czat
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
LANGUAGE_CODE = 'pl' # ustawienie jezyka
TIME_ZONE = 'Europe/Warsaw' # ustawienie strefy czasowej
Uruchom ponownie serwer deweloperski i sprawdź w przeglądarce, jak wygląda strona powitalna.

8.1.6. Model danych¶
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.
Zaczynamy więc 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 czat/models.py
wpisujemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # -*- coding: utf-8 -*-
# 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('treść wiadomości', max_length=250)
data_pub = models.DateTimeField('data publikacji', auto_now_add=True)
autor = models.ForeignKey(User)
|
Opisując klasę Wiadomosc
podajemy nazwy poszczególnych właściwości (pól)
oraz typy przechowywanych w nich danych.
Informacja
Typy pól:
CharField
– pole znakowe, przechowuje niezbyt długie napisy, np. nazwy;Date(Time)Field
– pole daty (i czasu);ForeignKey
– pole klucza obcego, czyli relacji; wymaga nazwy powiązanego modelu jako pierwszego argumentu.
Właściwości pól:
verbose_name
lub napis podany jako pierwszy argument – przyjazna nazwa pola;max_length
– maksymalna długość pola znakowego;help_text
– tekst podpowiedzi;auto_now_add=True
– data (i czas) wstawione zostaną automatycznie.
Utworzenie migracji – po dodaniu lub zmianie modelu należy zaktualizować bazę danych, tworząc tzw. migrację, czyli zapis zmian:
(.pve) ~/czat1$ python manage.py makemigrations czat
(.pve) ~/czat1$ python manage.py migrate

Informacja
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.
8.1.7. Panel administracyjny¶
Panel administratora pozwala dodawać użytkowników i wprowadzać dane.
W pliku czat/admin.py
umieszczamy kod:
1 2 3 4 5 6 7 8 | # -*- coding: utf-8 -*-
# czatpro/czat/admin.py
from django.contrib import admin
from czat import models # importujemy nasz model
# rejestrujemy model Wiadomosc w panelu administracyjnym
admin.site.register(models.Wiadomosc)
|
Po zaimportowaniu modelu rejestrujemy go w panelu: admin.site.register(models.Wiadomosc)
.
Informacja
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>
.
Konto administratora tworzymy wydając w terminalu polecenie:
(.pve) ~/czat1$ python manage.py createsuperuser
– na pytanie o nazwę, email i hasło administratora, podajemy: “admin”, “”, “zaq1@WSX”.
8.1.7.1. Ćwiczenie¶
- Uruchom/zrestartuj serwer, w przeglądarce wpisz adres 127.0.0.1:8000/admin/ i zaloguj się na konto administratora.

Dodaj użytkowników “adam” i “ewa” z hasłami “zaq1@WSX”.
Na stronie, która wyświetla się po utworzeniu konta, zaznacz opcję “W zespole”. W sekcji “Dostępne uprawnienia” zaznacz prawa 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.

- Z konta “adam” dodaj dwie przykładowe wiadomości, a z konta “ewa” – jedną.

8.1.8. Uzupełnienie modelu¶
W formularzu dodawania wiadomości widać, że etykiety opisujące nasz model
nie są spolszczone. Uzupełniamy więc plik czat/models.py
:
8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Wiadomosc(models.Model):
"""Klasa reprezentująca wiadomość w systemie"""
tekst = models.CharField('treść wiadomości', max_length=250)
data_pub = models.DateTimeField('data publikacji', auto_now_add=True)
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 __str__(self):
return self.tekst # "autoprezentacja"
|
Podklasa Meta
pozwala zdefiniować formy liczby pojedynczej i mnogiej oraz
domyślny sposób sortowania wiadomości (ordering = ['data_pub']
).
Zadaniem funkcji __str__()
jest “autoprezentacja” klasy,
czyli w naszym wypadku wyświetlenie treści wiadomości.
Odśwież panel administracyjny (np. klawiszem F5
).

8.1.9. Strona główna¶
Aby utworzyć stronę główną, zakodujemy pierwszy widok (zob. więcej »»»),
czyli funkcję o zwyczajowej nazwie index()
. W pliku views.py
umieszczamy:
1 2 3 4 5 6 7 8 9 10 | # -*- coding: utf-8 -*-
# czat/views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
"""Strona główna aplikacji."""
return HttpResponse("Witaj w aplikacji Czat!")
|
Najprostszy widok zwraca do klienta (przeglądarki) jakiś tekst:
return HttpResponse("Witaj w aplikacji Czat!")
.
Adresy URL, które ma obsługiwać nasza aplikacja, definiujemy w pliku czat/urls.py
.
Tworzymy nowy plik i uzupełniamy go kodem:
1 2 3 4 5 6 7 8 9 10 | # -*- coding: utf-8 -*-
# czat/urls.py
from django.conf.urls import url
from . import views # import widoków aplikacji
app_name = 'czat' # przestrzeń nazw aplikacji
urlpatterns = [
url(r'^$', views.index, name='index'),
]
|
app_name = 'czat'
– określamy przestrzeń nazw, w której dostępne będą mapowania między adresami url a widokami naszej aplikacji,url()
– funkcja, która wiąże zdefiniowany adres URL z widokiem,r'^$'
– wyrażenie regularne opisujące adres URL, symbol^
to początek,$
– koniec łańcucha. Zapisr'^$'
to adres główny serwera;views.index
– przykładowy widok, czyli funkcja zdefiniowana w plikuczat/views.py
;name='index'
– nazwa, która pozwoli na generowanie adresów url dla linków w kodzie HTML.
Konfigurację adresów URL naszej aplikacji musimy włączyć do konfiguracji adresów URL projektu.
W pliku czat1/urls.py
dopisujemy:
16 17 18 19 20 21 22 23 | from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^', include('czat.urls')),
url(r'^admin/', admin.site.urls),
]
|
include()
– funkcja pozwala na import adresów URL wskazanej aplikacji,'czat.urls'
– plik konfiguracyjny aplikacji.
Przetestuj stronę główną wywołując adres 127.0.0.1:8000
.

8.1.10. Widoki i szablony¶
Typową odpowiedzią na wywołanie jakiegoś adresu URL są strony zapisane w języku HTML.
Szablony takich stron umieszczamy w podkatalogu aplikacja/templates/aplikacja
.
Tworzymy więc katalog:
(pve3) ~/czat1$ mkdir -p czat/templates/czat
Następnie tworzymy szablon templates/czat/index.html
, który zawiera:
1 2 3 4 5 6 7 | <!-- templates/czat/index.html -->
<html>
<head></head>
<body>
<h1>Witaj w aplikacji Czat!</h1>
</body>
</html>
|
W pliku views.py
zmieniamy instrukcję odpowiedzi:
4 5 6 7 8 9 10 11 | from django.shortcuts import render
# from django.http import HttpResponse
def index(request):
"""Strona główna aplikacji."""
# return HttpResponse("Witaj w aplikacji Czat!")
return render(request, 'czat/index.html')
|
Funkcja render()
jako pierwszy parametr pobiera obiekt typu HttpRequest
zawierający informacje
o żądaniu, 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:

8.1.11. (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
, 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
|
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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'))
|
Logowanie rozpoczyna się od wyświetlenia odpowiedniej strony – to żądanie typu GET.
Widok logowania zwraca wtedy szablon: return render(request, 'czat/loguj.html', kontekst)
.
Parametr kontekst
to słownik, który pod kluczem form
zawiera pusty formularz logowania
utworzony w instrukcji AuthenticationForm()
.
Wypełnienie formularza danymi i przesłanie ich na serwer to żądanie typu POST.
Wykrywamy je w instrukcji if request.method == 'POST':
. Następnie tworzymy instancję
formularza wypełnioną przesłanymi danymi: form = AuthenticationForm(request, request.POST)
.
Jeżeli dane są poprawne if form.is_valid():
, możemy zalogować użytkownika
za pomocą funkcji login(request, form.get_user())
.
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
.
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).
Szablon logowania templates/czat/loguj.html
zawiera kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <!-- templates/czat/loguj.html -->
<html>
<head></head>
<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>
|
W szablonach wykorzystujemy tagi dwóch rodzajów:
{% instrukcja %}
– pozwalają używać instrukcji sterujących, np. warunkowych lub pętli,{{ zmienna }}
– służą wyświetlaniu wartości zmiennych lub wywoływaniu metod obiektów przekazanych do szablonu.{% if not user.is_authenticated %}
– instrukcja sprawdza, czy aktualny użytkownik jest zalogowany,{% csrf_token %}
– zabezpieczenie formularza przed atakiem typu csrf,{{ form.as_p }}
– automatyczne wyświetlenie pól formularza w akapitach,{% url 'czat:index' %}
– wstawienie adresu do odnośnika: w cudzysłowach podajemy przestrzeń nazw naszej aplikacji (app_name
), a później nazwę widoku (name
) zdefiniowane w plikuczat/urls.py
,{{ user.username }}
– tak wyświetlamy nazwę zalogowanego użytkownika.
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 %}
|
{% if messages %}
– sprawdzamy, czy mamy jakieś komunikaty,{% for komunikat in messages %}
– w pętli pobieramy kolejne komunikaty...{{ komunikat|capfirst }}
– i wyświetlamy z dużej litery za pomocą filtra.
Mapowanie adresów URL na widoki – w pliku czat/urls.py
dopisujemy reguły:
10 11 | url(r'^loguj/$', views.loguj, name='loguj'),
url(r'^wyloguj/$', views.wyloguj, name='wyloguj'),
|
Działanie dodanych funkcji testujemy pod adresami: 127.0.0.1:8000/loguj
i 127.0.0.1:8000/wyloguj
.
Używamy nazw i haseł utworzonych wcześniej użytkowników.
Przykładowy formularz wygląda tak:

8.1.11.1. Ćwiczenie¶
Adresów logowania i wylogowywania nikt nie wpisuje ręcznie. Wstaw kod generujący 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:


8.1.12. Dodawanie wiadomości¶
Chcemy, by zalogowani użytkownicy mogli dodawać wiadomości, a także przeglądać wiadomości innych.
Zaczynamy od dodania widoku o nazwie np. wiadomosci()
.
Do pliku views.py
dodajemy import i kod funkcji:
10 | from czat.models import Wiadomosc
|
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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,
autor=request.user)
wiadomosc.save()
return redirect(reverse('czat:wiadomosci'))
wiadomosci = Wiadomosc.objects.all()
kontekst = {'wiadomosci': wiadomosci}
return render(request, 'czat/wiadomosci.html', kontekst)
|
Obsługa żądania typu GET (wyświetlenie wiadomości i formularza):
wiadomosci = Wiadomosc.objects.all()
– pobieramy wszystkie wiadomości z bazy, używając wbudowanego w Django systemu ORM.return render(request, 'czat/wiadomosci.html', kontekst)
– zwracamy szablon, któremu przekazujemy słownikkontekst
zawierający wiadomości.
Obsługa żądania typu POST (przesłanie danych z formularza):
tekst = request.POST.get('tekst', '')
– wiadomość pobieramy ze słownikarequest.POST
za pomocą metodyget('tekst', '')
, pierwszy argument to nazwa pola formularza użytego w szablonie, drugi argument to wartość domyślna, jeśli pole będzie niedostępne.if not 0 < len(tekst) <= 250:
– sprawdzenie minimalnej i maksymalnej długości wiadomości,Wiadomosc(tekst=tekst, autor=request.user)
– utworzenie instancji wiadomości za pomocą konstruktora modelu, któremu przekazujemy wartości wymaganych pól,wiadomosc.save()
– zapisanie nowej wiadomości w bazie.
Szablon zapisany w pliku templates/czat/wiadomosci.html
będzie wyświetlał komunikaty zwrotne, np. błędy, a także formularz dodawania
i listę 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 | <!-- 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>
|
<input type="text" name="tekst" />
– “ręczne” przygotowanie formularza, czyli wstawienie kodu HTML pola do wprowadzania tekstu wiadomości,{{ wiadomosc.tekst }}
– wyświetlenie właściwości obiektu przekazanego w kontekście.
Adres URL, obsługiwany przez widok wiadomosci()
, definiujemy w
pliku czat/urls.py
, nadając mu nazwę wiadomosci:
12 | url(r'^wiadomosci/$', views.wiadomosci, name='wiadomosci'),
|
8.1.12.1. Ćwiczenie¶
- W szablonie widoku strony głównej dodaj link “Dodaj wiadomość” dla zalogowanych użytkowników.
- W szablonie wiadomości dodaj link “Strona główna”.
- Zaloguj się i przetestuj wyświetlanie [1] i dodawanie wiadomości pod adresem 127.0.0.1:8000/wiadomosci/. Sprawdź, co się stanie po wysłaniu pustej wiadomości.
[1] | 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:


8.1.13. 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: | 2022-05-22 o 19:52 w Sphinx 1.5.3 |
---|---|
Autorzy: | Patrz plik “Autorzy” |