7.1. Czat 1

Zastosowanie 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:

  • Środowisko wirtualne Pythona v. 3.x

  • Django v. 5.1.x

  • Opcjonalnie: interpreter bazy SQLite3

7.1.1. Środowisko pracy

Wskazówka

Do tworzenia aplikacji z użyciem Django możesz użyć dowolnych narzędzi, np. terminala i ulubionego edytora kodu. Sugerujemy jednak wykorzystanie środowiska PyCharm, ponieważ w dużym stopniu ułatwia pracę nad projektami w języku Python.

Przed rozpoczęciem pracy przygotuj w katalogu projekty_django` wirtualne środowisko Pythona i w aktywnym środowisku zainstaluj pakiet Django:

Terminal nr
(.venv) ~/projekty_django$ pip install django==5.1.6

Ostrzeżenie

Za każdym razem przed rozpoczęciem pracy nad projektem upewnij się, że środowisko wirtualne zostało aktywowane.

7.1.2. Projekt i aplikacja

Tworzymy nowy projekt o nazwie czat1. W terminalu w katalogu projekty_django wydajemy polecenia:

Terminal nr
(.venv) ~/projekty_django$ django-admin startproject czat1
(.venv) ~/projekty_django$ cd czat1
(.venv) ~/projekty_django/czat1$ python manage.py migrate
../../_images/pycharm_05.png

Polecenie django-admin.py startproject utworzy katalog czat1 – nazwa tego katalogu może być zmieniana. Wewnątrz znajdziemy katalog projektu o takiej samej nazwie – czat1 – oraz skrypt manage.py, który służy do zarządzania projektem.

Katalog projektu projekty_django/czat1/czat1 będzie zawierał widoczne poniżej pliki:

../../_images/pycharm_06.png

Polecenie manage.py migrate tworzy domyślną bazę danych SQLite3 zapisaną w pliku db.sqlite3. W bazie tworzone są tabele do przechowywania danych aplikacji domyślnie dostarczanych przez Django, m. in.:

  • admin – obsługa panelu administracyjnego,

  • auth – obsługa autentykacji użytkowników,

  • messages – obsługa komunikatów dla użytkownika.

7.1.2.1. Serwer deweloperski

Serwer, który służy do testowania działania aplikacji, uruchamiamy w katalogu zawierającym skrypt manage.py wydając polecenie w terminalu:

(.venv) ~/projekty_django/czat1$ python manage.py runserver

Informacja

W PyCharmie można również skonfigurować uruchamianie serwera. Rozwijamy listę Current File z górnego paska narzędzi i wybieramy Edit Configurations. Następnie klikamy ikonę plus, a następnie Python. W polu „Name” wpisujemy nazwę, np. Runserver. W polu „script” klikamy ikonę katalogu i wskazujemy położenie skryptu manage.py. Pod spodem jako parametr skryptu wpisujemy runserver. Jako katalog roboczy wskazujemy katalog projekty_django/czat1.

../../_images/pycharm_07.png

Od tej pory możemy uruchamiać serwer klikając przycisk Run na górnym pasku narzędzi lub skrótu SHIFT+F10.

Łączymy się z serwerem wpisując w przeglądarce adres: 127.0.0.1:8000.

Wskazówka

Jeżeli serwer uruchomiliśmy w terminalu, można również kliknąć adres http://127.0.0.1:8000 z wciśniętym klawiszem CTRL, aby otworzyć przeglądarkę.

../../_images/czat01_03_install_worked.png

W terminalu możemy obserwować żądania obsługiwane przez serwer. Serwer zatrzymujemy naciskając w terminalu skrót CTRL+C lub za pomocą przycisku Stop (CTRL+F2).

../../_images/pycharm_08.png

7.1.2.2. Aplikacja

W ramach jednego projektu (serwisu internetowego) może działać wiele aplikacji. Utworzymy teraz aplikację czat i zbadamy jej strukturę plików:

(.venv) ~/projekty_django/czat1$ python manage.py startapp czat
(.venv) ~/projekty_django/czat1$ ls -R czat
../../_images/django_aplikacja.png

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.

7.1.2.3. Ustawienia projektu

Ustawienia projektu przechowywane są w pliku czat1/settings.py, w którym możemy np.:

  • zarejestrować aplikację w projekcie,

  • ustawić polską wersję językową,

  • zlokalizować datę i czas.

Edytujemy plik czat1/settings.py i wprowadzamy:

Plik settings.pyKod nr
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 języka

TIME_ZONE = 'Europe/Warsaw'  # ustawienie strefy czasowej

Informacja

Lista INSTALLED_APPS zawiera aplikacje zarejestrowane w projekcie, część z nich dostarczana jest przez Django.

Uruchom ponownie serwer deweloperski i sprawdź w przeglądarce, jak wygląda strona powitalna.

../../_images/django_spolszczone.png

7.1.3. Model danych i migracje

Budowanie aplikacji w Django nawiązuje do wzorca projektowego MVC, czyli Model-Widok-Kontroler (zob. materiał MVC).

Zaczynamy od zdefiniowania modelu (zob. model bazy danych), czyli klasy, która posłuży do utworzenia tabeli w bazie danych zawierającej wiadomości. Atrybuty klasy odpowiadają polom tabeli. Instancje (obiekty) tej klasy reprezentować będą wiadomości utworzone przez użytkowników, czyli rekordy tabeli. Każda wiadomość będzie zwierała treść, datę dodania oraz identyfikator autora (użytkownika).

W pliku czat/models.py wpisujemy:

Plik models.pyKod nr
 1from django.contrib.auth.models import User
 2from django.db import models
 3
 4
 5class Wiadomosc(models.Model):
 6
 7    """Klasa reprezentująca wiadomość w systemie"""
 8    tekst = models.CharField('treść wiadomości', max_length=250)
 9    data_pub = models.DateTimeField('data publikacji', auto_now_add=True)
10    autor = models.ForeignKey(User, on_delete=models.CASCADE)
11
12    class Meta:  # ustawienia dodatkowe
13        verbose_name = 'wiadomość'  # nazwa obiektu w języku polskim
14        verbose_name_plural = 'wiadomości'  # nazwa obiektów w l.m.
15        ordering = ['data_pub']  # domyślne porządkowanie danych
16
17    def __str__(self):
18        return self.tekst  # "autoprezentacja"

Definiują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;

  • DateTimeField – pole daty i czasu;

  • ForeignKey – pole klucza obcego, czyli relacji; wymaga nazwy powiązanego modelu jako pierwszego argumentu.

Właściwości pól:

  • pierwszy argument w definicji pola, np. treść wiadomości określa przyjazną nazwę używaną np. w formularzach;

  • on_delete=models.CASCADE – jeżeli klucz obcy, w tym przypadku wskazujący autora, zostanie usunięty, usunięte zostaną również dodane przez niego wiadomości;

  • max_length – maksymalna długość pola znakowego;

  • auto_now_add=True – data i czas wstawione zostaną automatycznie.

Podklasa Meta pozwala określić formy liczby pojedynczej (verbose_name) i mnogiej (verbose_name_plural) używane podczas wypisywania obiektów, a także domyślny sposób sortowania wiadomości (ordering = ['data_pub']) wg daty dodania.

W metodzie __str__() decydujemy o tym, co powinno zostać zwrócone, jeżeli będziemy chcieli wypisać obiekt naszej klasy. W typ przypadku zwracamy treść wiadomości, co będzie przydatne np. w panelu administracyjnym.

7.1.3.1. Migracje

Operacje wykonywane na bazie danych nazywane są w Django migracjami. Każda migracja powiązana jest z aplikacją wchodzącą w skład projektu. Domyślnie wykonywane są migracje dla aplikacji admin, auth, contenttypes oraz sessions dostarczanych przez Django.

Po dodaniu lub zmianie modelu należy utworzyć migrację, aby w bazie danych zostały utworzone lub zmodyfikowane tabele, w których zapisywane są przetwarzane w aplikacji dane. Następnie migrację należy wykonać. W tym celu wydajemy polecenia:

Terminal nr
(.venv) ~/projekty_django/czat1$ python manage.py makemigrations czat
(.venv) ~/projekty_django/czat1$ python manage.py migrate
../../_images/django_migracje.png

Informacja

Migracje zapisywane są w podkatalogu migrations.

Domyślnie Django korzysta z bazy SQLite zapisanej w pliku db.sqlite3. Możemy zobaczyć, co zawiera. W terminalu wydajemy polecenie python manage.py dbshell, które otworzy bazę w powłoce sqlite3, o ile będzie zainstalowana w systemie. 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.

../../_images/sqlite3_tables.png

7.1.4. Panel administracyjny

Dostarczany przez Django panel administratora pozwala zarządzać użytkownikami i wprowadzać dane dla zarejestrowanych modeli. W pliku czat/admin.py umieszczamy kod:

Plik admin.pyKod nr
1from .models import Wiadomosc
2
3# rejestrujemy model Wiadomosc w panelu administracyjnym
4admin.site.register(Wiadomosc)

Na początku importujemy zawartość pliku models.py, a następnie rejestrujemy w panelu zdefiniowany wcześniej model: admin.site.register(models.Wiadomosc).

7.1.4.1. Ćwiczenia

  1. Utwórz konto administratora. W terminalu wydaj polecenie:

    Terminal nr
    (.venv) ~/projekty_django/czat1$ python manage.py createsuperuser
    

– na pytanie o nazwę, email i hasło administratora, podajemy: „admin”, ENTER, „zaq1@WSX”.

../../_images/django_superuser.png
  1. Uruchom/zrestartuj serwer, w przeglądarce wpisz adres 127.0.0.1:8000/admin/ i zaloguj się na konto administratora.

    ../../_images/django_admin_panel.png
  2. Dodaj użytkowników „adam” i „ewa” z hasłami „zaq1@WSX”.

    Na stronie „Zmień użytkownik”, która wyświetli się po kliknięciu przycisku Zapisz i kontynuuj edycję, zaznacz opcję „W zespole”, aby użytkownicy mogli zalogować się do panelu administracyjnego.

    ../../_images/django_admin_w_zespole.png

    W sekcji „Uprawnienia użytkownika” zaznacz prawa dodawania (add), zmieniania (change), usuwania (del) oraz wyświetlania (view) wiadomości (wpisy typu: „Czat | wiadomosc | Can add wiadomosc”) i przypisz je użytkownikowi naciskając strzałkę w prawo.

    ../../_images/django_admin_uprawnienia.png
  3. Zaloguj się na utworzone konta. Jako „adam” dodaj dwie przykładowe wiadomości, jako „ewa” – jedną.

    ../../_images/django_admin_wiadomosci.png

7.1.5. Strona główna

Tworzenie stron dostępnych w serwisie przebiega zazwyczaj według następującego schematu:

  1. zdefiniowanie adresu URL, pod którym dostępna będzie strona, i powiązanie go z widokiem,

  2. napisanie widoku, tj. funkcji obsługującej adres URL, która obsługuje żądanie i zwraca odpowiedź,

  3. dodanie szablonu, w którym umieszczamy dane dostarczane przez widok.

7.1.5.1. Definicja adresu URL

Konfiguracja adresów aplikacji powinna znajdować się w pliku czat/urls.py, który trzeba utworzyć.

Tworzymy więc nowy plik czat/urls.py i uzupełniamy go kodem:

Plik urls.pyKod nr
1from django.urls import path
2from . import views
3
4app_name = 'czat'  # przestrzeń nazw aplikacji
5urlpatterns = [
6    path('', views.index, name='index'),
7]
  • app_name = 'czat' – określamy przestrzeń nazw, w której dostępne będą mapowania między adresami URL a widokami,

  • path() – funkcja wiąże zdefiniowany adres URL z widokiem,

  • '' – pierwszym argumentem funkcji path() jest ciąg znaków definiujący adres, w tym przypadku ciąg pusty '' oznacza domyślny adres aplikacji,

  • views.index – przykładowy widok, czyli nazwa funkcji zdefiniowanej w pliku czat/views.py,

  • name='index' – nazwa, która pozwoli na generowanie adresów URL dla linków w kodzie HTML.

Zdefiniowane adresy URL aplikacji musimy włączyć do konfiguracji adresów URL projektu. W pliku czat1/urls.py dopisujemy:

Plik urls.pyKod nr
16from django.contrib import admin
17from django.urls import path, include
18
19urlpatterns = [
20    path('', include('czat.urls')),
21    path('admin/', admin.site.urls),
22]
  • include() – funkcja pozwala na import (dołączenie) adresów URL podanej aplikacji,

  • 'czat.urls' – plik konfiguracyjny adresów URL aplikacji.

7.1.5.2. Dodanie widoku

Drugim krokiem podczas dodawania strony jest dodanie widoku (zob. widok, więcej »»»), czyli w tym przypadku funkcji index() w pliku views.py:

Plik views.pyKod nr
1from django.shortcuts import render
2from django.http import HttpResponse
3
4def index(request):
5    """Strona główna aplikacji."""
6    return HttpResponse('Witaj w aplikacji Czat!')

Każdy widok powinien zwracać do klienta (przeglądarki) obiekt typu HttpResponse. W najprostszym przypadku może on zawierać tylko tekst, np.: return HttpResponse("Witaj w aplikacji Czat!").

7.1.5.2.1. Ćwiczenie

Uruchom serwer deweloperski i wejdź na stronę główną, tj. adres URL: 127.0.0.1:8000/admin/ – powinieneś zobaczyć tekst podany jako argument metody HttpResponse().

7.1.5.3. Dodanie szablonu

Typową odpowiedzią na żądanie jest zwrócenie do przeglądarki strony HTML. Takie zadanie realizujemy za pomocą szablonów, tj. plików HTML, które umieszczamy w podkatalogu templates/nazwa_aplikacji.

W katalogu aplikacji tworzymy więc katalogi templates/czat, a w nim plik index.html. Możemy to zrobić za pomocą poleceń wydawanych w terminalu, np.:

Terminal nr
(.venv) ~/projekty_django/czat1$ mkdir -p czat/templates/czat
(.venv) ~/projekty_django/czat1$ touch czat/templates/czat/index.html

Wskazówka

Do tworzenie katalogów i pustych plików możemy użyć systemowego menedżera plików lub używanego edytora programistycznego.

Np. w PyCharmie, aby utworzyć wymagane katalogi i pliki klikamy prawym klawiszem katalog aplikacji czat, wybieramy New / File i wpisujemy templates/czat/index.html.

Do szablonu templates/czat/index.html dodajemy kod:

Plik index.html. Kod nr
1<!-- templates/czat/index.html -->
2<html>
3  <head></head>
4  <body>
5    <h1>Witaj w aplikacji Czat!</h1>
6  </body>
7</html>

W pliku views.py zmieniamy funkcję index() tak, aby zwracała utworzony szablon:

Plik views.pyKod nr
4def index(request):
5    """Strona główna aplikacji."""
6    # return HttpResponse("Witaj w aplikacji Czat!")
7    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.

7.1.5.3.1. Ćwiczenie

Uruchom serwer deweloperski i sprawdź w przeglądarce, jak wygląda strona powitalna – powinna zawierać tekst wpisany w szablonie.

../../_images/django_strona_glowna_2.png

7.1.6. (Wy)logowanie

Chcemy, żeby użytkownicy, którzy nie będą mogli logować się do panelu administracyjnego, również mieli możliwość dodawania i przeglądania wiadomości. Musimy im więc stworzyć możliwość logowania i wylogowywania się. Podobnie jak w przypadku strony głównej zaczniemy od zdefiniowania adresów URL, następnie dodamy powiązane z nimi widoki, a na końcu zmienimy i utworzymy odpowiednie szablony.

7.1.6.1. Adresy URL

W pliku czat/urls.py definiujemy adresy URL:

Plik urls.pyKod nr
5urlpatterns = [
6    path('', views.index, name='index'),
7    path('loguj/', views.loguj, name='loguj'),
8    path('wyloguj/', views.wyloguj, name='wyloguj'),

7.1.6.2. Widoki

W pliku views.py uzupełniamy importy:

Plik views.pyKod nr
1from django.shortcuts import render, redirect
2# from django.http import HttpResponse
3from django.contrib.auth import login, logout
4from django.urls import reverse
5from django.contrib import messages

– i dodajemy widoki loguj() i wyloguj()

Plik views.pyKod nr
13def loguj(request):
14    """Logowanie użytkownika"""
15    from django.contrib.auth.forms import AuthenticationForm
16    if request.method == 'POST':
17        form = AuthenticationForm(request, request.POST)
18        if form.is_valid():
19            login(request, form.get_user())
20            messages.success(request, 'Zostałeś zalogowany!')
21            return redirect(reverse('czat:index'))
22
23    kontekst = {'form': AuthenticationForm()}
24    return render(request, 'czat/loguj.html', kontekst)
25
26def wyloguj(request):
27    """Wylogowanie użytkownika"""
28    logout(request)
29    messages.info(request, 'Zostałeś wylogowany!')
30    return redirect(reverse('czat:index'))

Logowanie rozpoczyna się od żadania typu GET wysłanego na adres /loguj. Widok loguj() zwraca wtedy szablon: return render(request, 'czat/loguj.html', kontekst). W słowniku kontekst w kluczu form przekazujemy do szablonu pusty formularz logowania – instancję klasy AuthenticationForm().

Kiedy użytkownik wypełni formularz logowania danymi i kliknie przycisk „Zaloguj”, otrzymamy żądanie typu POST. Po wykryciu takiego żądania (if request.method == 'POST':) 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()).

Przygotowujemy również informację zwrotną dla użytkownika, wykorzystując system komunikatów: messages.success(request, '...'). Tak utworzone komunikaty możemy odczytać w każdym szablonie ze zmiennej messages.

Na koniec przekierowujemy użytkownika na stronę główną (return redirect(reverse('index'))) z żądaniem (typu GET) jej wyświetlenia.

Wylogowanie polega na użyciu funkcji logout(request) – wyloguje ona użytkownika, którego dane zapisane są w przesłanym żądaniu.

Informacja

Warto zauważyć, że każdy widok otrzymuje jako pierwszy argument obiekt request zawierający wszystkie dane przetwarzanego żądania, w tym dane przesłane z formularza – request.POST.

7.1.6.3. Szablony

Tworzymy i uzupełniamy szablon logowania templates/czat/loguj.html kodem:

Plik loguj.html Kod nr
 1<!-- templates/czat/loguj.html -->
 2<html>
 3  <head></head>
 4  <body>
 5    <h1>Witaj w aplikacji Czat!</h1>
 6
 7    <h2>Logowanie użytkownika</h2>
 8    {% if not user.is_authenticated %}
 9      <form action="." method="POST">
10        {% csrf_token %}
11        {{ form.as_p }}
12        <button type="submit">Zaloguj</button>
13      </form>
14    {% else %}
15      <p>Jesteś już zalogowany jako {{ user.username }}</p>
16      <ul>
17        <li><a href="{% url 'czat:index'%}">Strona główna</a></li>
18      </ul>
19    {% endif %}
20
21  </body>
22</html>

W szablonach wykorzystujemy specjalne tagi dwóch rodzajów:

  • {% instrukcja %} – pozwalają używać instrukcji sterujących, np. warunkowych lub pętli,

  • {{ zmienna }} – służą stawianiu wartości zmiennych lub wywoływaniu metod obiektów przekazanych do szablonu.

Zastosowanie:

  • {% if not user.is_authenticated %} – instrukcja sprawdza, czy aktualny użytkownik nie jest zalogowany,

  • {% csrf_token %} – zabezpieczenie formularza przed atakiem typu csrf,

  • {{ form.as_p }} – wyświetla pola formularza w akapitach,

  • {% url 'czat:index' %} – generuje adres URL: w cudzysłowach podajemy przestrzeń nazw naszej aplikacji (app_name), a później nazwę widoku (name) zdefiniowane w pliku czat/urls.py,

  • {{ user.username }} – obiekt user zawiera dane zalogowanego użytkownika, m. in. jego nazwę, którą wyświetlamy.

Uzupełniamy szablon index.html. Po znaczniku <h1> wstawiamy poniższy kod:

Plik index.html Kod nr
 7      {% if messages %}
 8        <ul>
 9        {% for komunikat in messages %}
10           <li>{{ komunikat|capfirst }}</li>
11        {% endfor %}
12        </ul>
13      {% endif %}
14
15    {% if not user.is_authenticated %}
16      <a href="{% url 'czat:loguj' %}">Zaloguj</a>
17    {% else %}
18      <form action="{% url 'wyloguj' %}" method="post">
19        {% csrf_token %}
20        <input type="submit" value="Wyloguj się">
21      </form>
22    {% endif %}
  • {% if messages %} – sprawdzamy, czy mamy jakieś komunikaty zwrotne,

  • {% for komunikat in messages %} – w pętli odczytujemy kolejne komunikaty i …

  • {{ komunikat|capfirst }} – wyświetlamy przy użyciu filtra z dużej litery.

Jeżeli użytkownik został zalogowany, wyświetlamy formularz zawierający tylko przycisk „Wyloguj się”. Po jego kliknięciu zostanie wysłane żądanie typu POST, które zostanie obsłużone przez omawiany wyżej widok wyloguj().

7.1.6.3.1. Ćwiczenie

  1. Dodaj w panelu administracyjnym użytkownika „ola” z hasłem „zaq1@WSX”. Nie zaznaczaj opcji „W zespole”.

  2. Uruchom serwer deweloperski i zaloguj się pod adresem 127.0.0.1:8000/loguj podając dane utworzonego użytkownika.

../../_images/django_zaloguj_form.png

Po zalogowaniu się powinniśmy zobaczyć komunikat potwierdzający oraz przycisk pozwalający na wylogowanie się:

../../_images/django_wyloguj_form.png
  1. Wyloguj się – powinniśmy zobaczyć komunikat o wylogowaniu:

../../_images/django_wyloguj.png

7.1.7. Django ORM

Obsługa żądania typu GET, czyli wyświetlenie wiadomości wymaga odczytu (ang. Read) danych z bazy. W języku SQL używamy do tego klauzuli SELECT. Z kolei żądania typu POST wiążą się z dodawaniem (ang. Create), modyfikacją (ang. Update) i usuwaniem (ang. Delete) danych w bazie. W języku SQL służą do tego klauzule INSERT, UPDATE i DELETE.

Django nie wymaga zapytań w języku SQL, chociaż są one obsługiwane. Zamiast tego oferuje niezależny od konkretnej bazy danych interfejs programistyczny (API) działający jak system ORM. Jego podstawowym założeniem jest to, że każdy model danych odpowiada tablicy w bazie danych, jego instancje (obiekty) reprezentują konkretne rekordy, a właściwości modelu (obiektu) odpowiadają polom rekordu.

7.1.7.1. Odczyt danych

Do odczytu danych (ang. Read) używamy managera objects, który zwraca kolekcję obiektów QuerySet odpowiadającą klauzuli SQL SELECT. Rekordy w kolekcji można filtrować, co odpowiada klauzulom SQL WHERE i LIMIT.

Przećwiczmy kilka przykładów. W terminalu w katalogu projekty_django/czat1 wydajemy polecenie:

Terminal nr
(.venv) ~/projekty_django/czat1$ python manage.py shell -v 2
>>> import datetime
>>> from django.utils import timezone

Uruchomiona zostanie powłoka Pythona w trybie interaktywnym z zaimportowanymi obiektami. Dodatkowo importujemy moduły potrzebne do operacji na datach i czasie.

Teraz możemy wpisywać po kolei poniższe przykłady kodu:

  • wiadomosci = Wiadomosc.objects.all() – za pomocą metody all() pobieramy wszystkie obiekty typu Wiadomosc z bazy i zapisujemy pod nazwą wiadomosci,

  • wiadomosci – zobaczymy, że wiadomosci to typ QuerySet zawierający listę obiektów typu Wiadomosc,

  • wiadomosci.filter(autor_id=2) – metoda filter() z kolekcji rekordów wyodrębni te, których autor ma identyfikator (pole id obiektu typu User) równy 2;

  • wiadomosci.filter(autor__username__exact='adam') – z kolekcji rekordów wyodrębniamy te, których nazwa autora (właściwość username obiektu typu User) równa jest «adam»,

  • wiadomosci.exclude(tekst__contains='Pierwsza') – z kolekcji rekordów usunie te, które zawierają ciąg znaków Pierwsza,

  • Wiadomosc.objects.filter(data_pub__lte=timezone.now()-datetime.timedelta(days=10)) – zwróci wiadomości opublikowane przed 10 dniami,

  • w1 = Wiadomosc.objects.get(pk=1) – metoda get() zwraca jeden obiekt, który spełnia podany jako argument warunek, w tym przypadku identyfikator obiektu (ang. pk to skrót od primary key – klucz główny) musi być równy 1,

  • w1 – zobaczymy, że w1 zawiera obiekt typu Wiadomosc.

Informacja

Metody filter(**kwargs) i get(**kwargs) przyjmują argumenty w ogólnej postaci field__lookuptype=value, gdzie:

  • field – to nazwa właściwości modelu odpowiadająca polu rekordu,

  • __ – podwójne podkreślenie,

  • lookuptype=value – rodzaj dopasowania podanej wartości, tj. warunek, jaki ma spełniać dana właściwość (pole rekordu).

Często używane rodzaje dopasowań wartości sprawdzanego pola:

  • __exact='ciąg_znaków') – musi odpowiadać dokładnie ciągowi znaków,

  • __contains='ciąg_znaków' – musi zawierać ciąg znaków,

  • __in=obiekt_iterowalny – zawiera się w obiekcie iterowalnym, tj. tupli, liście, queryset lub ciągu znaków,

  • __gt, __gte – większe od, większe lub równe,

  • __lt, __lte – mniejsze od, mniejsze lub równe,

7.1.7.2. Dodawanie danych

Dodawanie danych do bazy (ang. Create) polega na utworzeniu instancji wybranego modelu z wykorzystaniem nazwanych argumentów i wywołaniu metody save(), która zapisze go w bazie, czyli wykona operację INSERT SQL. Poniżej przykład, który wykonujemy w uruchomionej wcześniej powłoce:

  • user = User.objects.get(username__exact='adam') – utworzenie obiektu użytkownika o nazwie «adam»,

  • Wiadomosc.objects.filter(autor=user) – odczyt wiadomości danego użytkownika,

  • wiadomosc = Wiadomosc(tekst='Trzecia wiadomość Adama', autor=user) – utworzenie instancji nowej wiadomości,

  • wiadomosc.save() – zapisanie wiadomości w bazie.

Inną metodą utworzenia i zapisania obiektu w bazie danych jest użycie metody create(), np.:

  • Wiadomosc.objects.create(tekst='Czwarta wiadomość Adama', autor=user) – tworzy i od razu zapisuje nową wiadomość w bazie,

  • Wiadomosc.objects.filter(autor=user) – ponowny odczyt wiadomości danego użytkownika, powinniśmy zobaczyć dwie nowe wiadomości.

7.1.8. Wiadomości

Chcemy, by zalogowani użytkownicy mogli przeglądać i dodawać wiadomości.

Zaczynamy od zdefiniowania adresu URL obsługiwanego przez widok wiadomosci(). W pliku czat/urls.py do słownika urlpatterns dopisujemy:

Plik urls.pyKod nr
9    path('wiadomosci/', views.wiadomosci, name='wiadomosci'),

Następnie dodajemy widok o nazwie wiadomosci(). W pliku views.py umieszczamy import i kod funkcji:

Plik views.pyKod nr
6from .models import Wiadomosc
Plik views.pyKod nr
32def wiadomosci(request):
33    """Dodawanie i wyświetlanie wiadomości"""
34    if request.method == 'POST':
35        tekst = request.POST.get('tekst', '')
36        if not 0 < len(tekst) <= 250:
37            messages.error(
38                request,
39                "Wiadomość nie może być pusta, może mieć maks. 250 znaków!")
40        else:
41            wiadomosc = Wiadomosc(
42                tekst=tekst,
43                autor=request.user)
44            wiadomosc.save()
45            return redirect(reverse('czat:wiadomosci'))
46
47    wiadomosci = Wiadomosc.objects.all()
48    kontekst = {'wiadomosci': wiadomosci}
49    return render(request, 'czat/wiadomosci.html', kontekst)
  • return render(request, 'czat/wiadomosci.html', kontekst) – zwracamy szablon, do którego przekazujemy słownik kontekst zawierający wiadomości.

Obsługa żądania typu POST, czyli przetworzenie danych z przesłanego formularza:

  • tekst = request.POST.get('tekst', '') – wiadomość pobieramy ze słownika request.POST za pomocą metody get('tekst', ''), pierwszy argument to nazwa pola formularza, 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.

W pliku templates/czat/wiadomosci.html przygotowujemy szablon, który będzie wyświetlał komunikaty zwrotne, np. błędy, a także formularz dodawania i listę wiadomości:

Plik wiadomosci.html. Kod nr
 1<!-- templates/czat/wiadomosci.html -->
 2<html>
 3  <head></head>
 4  <body>
 5    <h1>Witaj w aplikacji Czat!</h1>
 6
 7    {% if messages %}
 8      <ul>
 9        {% for komunikat in messages %}
10           <li>{{ komunikat|capfirst }}</li>
11        {% endfor %}
12        </ul>
13    {% endif %}
14
15    <h2>Dodaj wiadomość</h2>
16    <form action="." method="POST">
17      {% csrf_token %}
18      <input type="text" name="tekst" />
19      <input type="submit" value="Zapisz" />
20    </form>
21
22    <h2>Lista wiadomości:</h2>
23    <ol>
24      {% for wiadomosc in wiadomosci %}
25        <li>
26          <strong>{{ wiadomosc.autor.username }}</strong> ({{ wiadomosc.data_pub }}):
27          <br /> {{ wiadomosc.tekst }}
28        </li>
29      {% endfor %}
30    </ol>
31
32  </body>
33</html>
  • <input type="text" name="tekst" /> – „ręczne” przygotowanie formularza, czyli wstawienie kodu HTML pola do wprowadzania tekstu wiadomości,

  • {{ wiadomosc.tekst }} – wyświetlanie treści wiadomości odczytywanych z listy przekazanej w kontekście.

7.1.8.1. Ćwiczenie

  1. W szablonie strony głównej dodaj link „Dodaj wiadomość”, który będzie widoczny dla zalogowanych użytkowników.

  2. W szablonie wiadomości dodaj link „Strona główna”.

  3. Zaloguj się jako użytkownik „ola” i dodaj wiadomość [1]. Sprawdź, co się stanie po próbie dodania pustej wiadomości.

Poniższe zrzuty prezentują efekty naszej pracy:

../../_images/django_czat_index.png
../../_images/django_wiadomosci.png

7.1.9. Materiały

  1. O Django

  2. Strona projektu Django

  3. Co to jest framework?

  4. Protokół HTTP i żądania GET, POST


Licencja Creative Commons 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:

2026-05-30 o 19:12 w Sphinx 7.3.7

Autorzy:

Robert Bednarz