├── lab_03 ├── filters.png └── readme.md ├── lab_05 ├── screen_1.png └── readme.md ├── lab_08 ├── graphql_1.png ├── graphql_2.png ├── graphql_3.png ├── graphql_4.png └── readme.md ├── lab_06 ├── postman_auth.png └── readme.md ├── readme.md ├── lab_01 └── readme.md ├── lab_02 └── readme.md ├── lab_04 └── readme.md ├── lab_09 └── readme.md └── lab_07 └── readme.md /lab_03/filters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_03/filters.png -------------------------------------------------------------------------------- /lab_05/screen_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_05/screen_1.png -------------------------------------------------------------------------------- /lab_08/graphql_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_08/graphql_1.png -------------------------------------------------------------------------------- /lab_08/graphql_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_08/graphql_2.png -------------------------------------------------------------------------------- /lab_08/graphql_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_08/graphql_3.png -------------------------------------------------------------------------------- /lab_08/graphql_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_08/graphql_4.png -------------------------------------------------------------------------------- /lab_06/postman_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kropiak/app_www/HEAD/lab_06/postman_auth.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2025/2026Z 2 | 3 | ## 1. Cel i zakres przedmiotu 4 | 5 | Celem przedmiotu jest przybliżenie studentowi zagadnień związanych z projektowaniem aplikacji WWW z wykorzystaniem języka Python oraz frameworka Django. W trakcie zajęć główny nacisk zostanie położony na backend (czyli część aplikacji związana z implementacją funkcjonalności po stronie serwera), ale zajmiemy się też budową prostych widoków, czyli frontendem. 6 | 7 | W trakcie zajęć student pozna m.in. zagadnienia takie jak: 8 | * konfiguracja bazy danych na potrzeby aplikacji 9 | * obsługa narzędzia Git 10 | * tworzenie i zarządzanie modelami we frameworku Django 11 | * migracja bazy i rozwiązywanie problemów z migracjami 12 | * implementacja logiki biznesowej (wymagań klienta) w projekcie 13 | * tworzenie API REST-owego na potrzeby różnych technologii frontendowych 14 | * obsługa i implementacja autentykacji i uwierzytelniania z użyciem Django 15 | * poznanie narzędzi zarządzania projektem Django oraz panelu administratora i jego możliwości 16 | 17 | 18 | ## 2. Oprogramowanie 19 | 20 | W trakcie zajęć do pracy niezbędne będzie posiadanie: 21 | * zainstalowanego interpretera **Pythona** w wersji 3.11 lub nowszej 22 | * **framework Django** w wersji 5.2.* - poczytaj release notes: https://docs.djangoproject.com/en/5.2/releases/5.2/ 23 | * narzędzie IDE, preferowane **PyCharm Professional** (licencja studencka) lub wersja Community. Może to być również inne oprogramowanie ze wsparciem dla języka Python np. Visual Studio Code. 24 | * narzędzie Git do zarządzania kodem projektu 25 | * możliwe, że w zależności od konfiguracji projektu niezbędne będzie zainstalowanie i konfiguracja odpowiedniego serwera bazy danych 26 | 27 | ## 3. Warunki zaliczenia przedmiotu. 28 | 29 | - Efektem finalnym pracy na zajęciach będzie **projekt API** stworzony w Django Rest Framework lub po uzgodnieniu w innym frameworku, 30 | - Studenci mogą dobrać się w pary :dancers: lub pracować samodzielnie :dancer:, 31 | - Projekt studenta będzie przechowywany w **publicznym repozytorium GitHub**, do którego prowadzący będzie miał wgląd przez cały okres trwania zajęć, 32 | - W przypadku pracy grupowej, obie osoby muszą być dodane jako collaborator do repozytorium, 33 | - Oceniane będą: 34 | - Systematyczność commitów (oraz ich [sens](https://medium.com/@steveamaza/how-to-write-a-proper-git-commit-message-e028865e5791)), 35 | - Jakość kodu programistycznego, 36 | - Implementacja zagadnień przedstawionych na zajęciach, 37 | - Poziom skomplikowania projektu (np. ilość modeli, działanie endpointów, itp.) 38 | - Proponowany temat API (co te API będzie robić) jest dowolny i będzie przedstawiony prowadzącemu na drugich zajęciach. 39 | - Prowadzący rezerwuje możliwość przeprowadzenia kolokwium w przypadku braku pracy na zajęciach przez grupę studentów. 40 | 41 | ## 4. Wymagania projektu. 42 | 43 | Zakres projektu zaliczeniowego: 44 | 1. **Modele**: 45 | * 4-5 adekwatnych do projektu modeli 46 | 2. **Uwierzytelnianie (preferowany token) i autoryzacja.** 47 | * Co najmniej 2 konta z różnym poziomem dostępu do endpointów (np. admin, user) 48 | 3. **Endpointy:** 49 | * Rejestracja użytkownika aplikacji (opcjonalne) 50 | * CRUD 51 | * Minimum 2 endpointy, które wyjdą poza schemat CRUD, np. zestawienie miesięczne zamówień, lista wypożyczeń dla użytkownika, lista towarów zaczynających się od itp. 52 | 53 | -------------------------------------------------------------------------------- /lab_08/readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2025Z 2 | 3 | ## Lab 8 - Wprowadzenie do GraphQL. 4 | 5 | GraphQL jest językiem zapytań do API, który w odróżnieniu od REST API pozwala precyzyjnie określić listę cech obiektów, które chcemy zwrócić (po stronie aplikacji klienta) w zależności od potrzeb. Oznacza to, że poprawnie definiując zapytania możemy uniknąć ofektu pobierania zbyt dużej liczby danych (overfetching) lub zbyt małej (underfetching). 6 | 7 | 8 | Poniżej znajduje się instrukcja uruchomienia modułu `graphene` pozwalającego na przetestowanie GraphQL w istniejącym projekcie Django. 9 | 10 | **Krok 1.** 11 | 12 | Instalacja graphene: 13 | 14 | ```bash 15 | pip install graphene-django 16 | ``` 17 | 18 | **Krok 2.** 19 | Dodanie definicji do pliku `settings.py` 20 | 21 | ```python 22 | INSTALLED_APPS = [ 23 | ... 24 | "graphene_django" 25 | ] 26 | ``` 27 | 28 | **Krok 3.** 29 | 30 | Edycja GŁÓWNEGO pliku `urls.py` 31 | 32 | ```python 33 | ... 34 | from django.views.decorators.csrf import csrf_exempt 35 | from graphene_django.views import GraphQLView 36 | 37 | urlpatterns = [ 38 | path('admin/', admin.site.urls), 39 | path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))), 40 | ] 41 | ``` 42 | **Krok 4.** 43 | 44 | Dodanie pliku `projekt/schema.py` (tu dla mojej aplikacji ankiety) z zawartością: 45 | 46 | ```python 47 | import graphene 48 | from graphene_django import DjangoObjectType 49 | 50 | from ankiety.models import Person, Team 51 | 52 | 53 | # dzięki wykorzystaniu klasy DjangoObjectType możemy w łatwy sposób 54 | # wskazać klasę wcześniej zdefiniowanego modelu, która zostanie wykorzystana 55 | # w schemie GraphQL umożliwiając połączenie Django QuerySet oraz zapytań 56 | # poprzez GraphQL. Podobnie jak w przypadku definicji własności w klasach 57 | # administracyjnych danego modelu (plik admin.py) tutaj też możemy określić 58 | # np. listę pól, które poprzez GraphQL chcemy wystawić z danego modelu 59 | class PersonType(DjangoObjectType): 60 | class Meta: 61 | model = Person 62 | fields = ("id", "name", "shirt_size", "miesiac_dodania", "team") 63 | # lub 64 | # fields = "__all__" 65 | 66 | class TeamType(DjangoObjectType): 67 | class Meta: 68 | model = Team 69 | fields = ("id", "name", "country") 70 | 71 | # klasa Query pozwala określić pola (tu np. all_teams, person_by_id, itd.) 72 | # które są dostępne, a następnie Resolvery, który definiują w jaki sposób 73 | # dane dla wskazanego pola będą pobierane (tu już używamy znany nam 74 | # sposób z użyciem Django QuerySet) 75 | 76 | class Query(graphene.ObjectType): 77 | # typ graphene.List określa, że zwracana będzie lista obiektów danego typu 78 | all_teams = graphene.List(TeamType) 79 | 80 | # tu określamy, że zwrócony będzie obiekt typu PersonType, a jego wyszukanie 81 | # odbędzie się na podstawie jego atrybutu id o typie Int, który jest wymagany 82 | person_by_id = graphene.Field(PersonType, id=graphene.Int(required=True)) 83 | 84 | all_persons = graphene.List(PersonType) 85 | person_by_name = graphene.Field(PersonType, name=graphene.String(required=True)) 86 | find_persons_name_by_phrase = graphene.List(PersonType, substr=graphene.String(required=True)) 87 | 88 | # resolver dla pola all_teams 89 | # root główny obiekt wartości przekazywany przez zapytanie 90 | # info informacje z resolvera 91 | # args słownik (opcjonalnie), parametrów przekazywanych do resolvera 92 | def resolve_all_teams(root, info): 93 | return Team.objects.all() 94 | 95 | def resolve_person_by_id(root, info, id): 96 | try: 97 | return Person.objects.get(pk=id) 98 | except Person.DoesNotExist: 99 | raise Exception('Invalid person Id') 100 | 101 | def resolve_person_by_name(root, info, name): 102 | try: 103 | return Person.objects.get(name=name) 104 | except Person.DoesNotExist: 105 | raise Exception(f'No Person with name \'{name}\' found.') 106 | 107 | def resolve_all_persons(root, info): 108 | """ zwraca również wszystkie powiązane obiekty team dla tego obiektu Person""" 109 | return Person.objects.select_related("team").all() 110 | 111 | def resolve_find_persons_name_by_phrase(self, info, substr): 112 | return Person.objects.filter(name__icontains=substr) 113 | 114 | 115 | schema = graphene.Schema(query=Query) 116 | ``` 117 | 118 | **Krok 5.** 119 | 120 | Dodanie wpisu do `settings.py` 121 | 122 | ```python 123 | GRAPHENE = { 124 | "SCHEMA": "schema.schema" 125 | } 126 | ``` 127 | 128 | **Krok 6.** 129 | 130 | Uruchamiamy serwer i przechodzimy pod adres http://127.0.0.1/graphql i możemy wykonać zapytania w przedstawiony poniżej sposób z efektem widocznym po prawej stronie. 131 | 132 |  133 |  134 |  135 |  136 | 137 | Oficjalna strona GraphQL: https://graphql.org/ 138 | Strona projektu Graphene Python: https://graphene-python.org/ 139 | 140 | 141 | **Zadania** 142 | 143 | 0. Pracę wykonaj na nowym branchu. 144 | 1. Odtwórz w swojej aplikacji kolejne kroki przedstawione w labie aby uruchomić możliwość korzystania z GraphQL. Wspomagaj się materiałami z wykładu. 145 | 2. Dodaj jeszcze 3 dodatkowe resolvery pozwalające na odpytanie API za pomocą GraphQL, które pozwolą np. odfiltrować obiekty po fragmencie nazwy, wyświetlić ilość obiektów z daną wartością atrybutu (np. liczba postów dla danego użytkownika). 146 | 3. Dla modelu `Post` stwórz również mutacje pozwalające na tworzenie, edycję i usuwanie encji tego typu. 147 | 148 | Zapytania GraphQL zapisz w pliku `lab_8_zadania.md` w folderze projektu na tym branchu. 149 | -------------------------------------------------------------------------------- /lab_03/readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2025Z 2 | 3 | ## Lab 3 4 | --- 5 | ### **1. Modele w Django. Ciąg dalszy** 6 | 7 | 8 | Oprócz typowego mapowania obiektowo-relacyjnego klasy modeli frameworka Django oferują dużo więcej możliwości, m.in. implementację funkcji pomocniczych, określenie domyślnego porządku sortowania zwracanych krotek, określenie parametrów walidacji i inne. Poniżej zostaną zaprezentowane niektóre z możliwości wraz z przykładami użycia. 9 | 10 | Przydatne linki do dokumentacji, które mogą się przydać w trakcie rozwiązywania zadań: 11 | 1. Lista dostępnych pól klas modeli: https://docs.djangoproject.com/pl/5.2/ref/models/fields/#model-field-types 12 | 2. Atrybuty modeli: https://docs.djangoproject.com/pl/5.2/topics/db/models/#field-options 13 | 3. Klasa QuerySet i dostępne metody: https://docs.djangoproject.com/pl/5.2/ref/models/querysets/ 14 | 15 | ### Zadania 16 | 17 | **Zadanie 1** 18 | Dodaj do aplikacji `posts` nowy model `Post` zgodnie z poniższą definicją: 19 | * pole `title` typu krótki tekst, max. 150 znaków, 20 | * pole `text` z długim tekstem, 21 | * pole `topic`, klucz obcy do modelu `Topic`, usuwanie kaskadowe, 22 | * pole `slug` typu `SlugField`, 23 | * pole `created_at` typu data i czas, wartość wstawiana automatycznie w momencie dodania rekordu, 24 | * pole `updated_at` typu data i czas, wartość aktualizowana po każdej aktualizacji instancji obiektu, 25 | * pole `created_by`, które jest kluczem obcym do wbudowanej klasy `django.contrib.auth.models.User`. 26 | 27 | **Zadanie 2** 28 | Zarejestruj model w panelu administracyjnym Django. Znajdź w dokumentacji modułu admin ([link](https://docs.djangoproject.com/pl/5.2/ref/contrib/admin/)) zobacz jak deklaruje się pola modelu tylko do odczytu i ustaw taką własność dla pola `created_at` modelu `Post`. 29 | 30 | **Zadanie 3** 31 | Przesłoń metodę `__str__()` zdefiniowanych modeli `Category`, `Topic` oraz `Post` według poniższej instrukcji: 32 | * `Category` - nazwa kategorii, 33 | * `Topic` - nazwa tematu, 34 | * `Posts` - pierwsze 5 wyrazów tekstu posta + '...' jeżeli dłuższy.* 35 | 36 | \* Jeżeli dodamy klasę PostAdmin w `admin.py` oraz okreśłimy listę kolumn do wyświetlenia, należy to obsłużyć jako dodatkowe pole w klasie `PostAdmin` (definiowane przez osadzenie funkcji w tej klasie). 37 | 38 | **Zadanie 4** 39 | Bazując na przykładzie z dokumentacji https://docs.djangoproject.com/pl/5.2/topics/db/models/#meta-options dodaj właściwość `META` sortując domyślnie model `Category` po nazwie alfabetycznie, `Topic` podobnie, a `Post` po dacie dodania od najnowszych. 40 | 41 | **Zadanie 5** 42 | 43 | W panelu administracyjnym dla dodanych modeli nie są widoczne wszystkie pola na liście wszystkich obiektów lecz tylko jedno z nich (domyślnie id lub to co w przesłoniętej funkcji `__str__()`). Aby to zrobić należy najpierw zdefiniować w pliku `admin.py` klasę admin dla danego modelu (ModelAdmin), a następnie zdefiniować listę pól do wyświetlenia w zmiennej `list_display`. Przykład poniżej: 44 | 45 | **_Listing 1_** 46 | ```python 47 | # dla przykładowej klasy 48 | class PersonAdmin(admin.ModelAdmin): 49 | # zmienna list_display przechowuje listę pól, które mają się wyświetlać w widoku listy danego modelu w panelu administracynym 50 | list_display = ['name', 'shirt_size'] 51 | 52 | # ten obiekt też trzeba zarejestrować w module admin 53 | admin.site.register(Person, PersonAdmin) 54 | ``` 55 | 56 | **Zadanie 6** 57 | Dodaj klasy `ModelAdmin` dla wszystkich zdefiniowanych modeli i zmień listę wyświetlanych kolumn w panelu administracyjnym. 58 | 59 | **Zadanie 7** 60 | Zmodyfikuj kod aplikacji tak, żeby na liście modeli `Post` w panelu administracyjnym wyświetlana została również kolumna `Topic` o postaci 'nazwa topiku (nazwa kategorii)' np. `Batman (Filmy)`. Podpowiedź: sprawdź w dokumentacji modułu admin opis działania adnotacji `@admin.display` ([link](https://docs.djangoproject.com/pl/5.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display)). 61 | 62 | --- 63 | W panelu administracyjnym możliwe jest również dodanie filtrów do widoków. Cały proces polega na dodaniu pola `filter_list` w klasie admin danego modułu: 64 | 65 | **_Listing 2_** 66 | ```python 67 | # przykład i wizualizacja dla modeli Person oraz Team. 68 | 69 | class PersonAdmin(admin.ModelAdmin): 70 | list_filter = ['team'] 71 | ``` 72 |  73 | 74 | 75 | W zależności od typu pola zostanie wyświetlony odpowiedni filtr. W przypadku niektórych rodzajów i dużej liczby unikalnych wartości używanie filtrów może być niepraktyczne ze względów wydajnościowych i wizualnych. 76 | 77 | --- 78 | 79 | **Zadanie 8** 80 | Dodaj filtr dla tematów (topic), kategorii oraz użytkowników dla klasy `Post` w panelu administracyjnym. Dodaj filtry nazw dla tematów oraz kategorii. Przetestuj działanie filtrów. 81 | 82 | **Zadanie 9** 83 | Przeczytaj informację o możiwości automatycznego generowania wartości pola w dokumentacji(https://docs.djangoproject.com/pl/5.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.prepopulated_fields) i zastosuj to dla pola `SlugField` modelu `Post`, które będzie generowane z pola `title`. 84 | 85 | **Zadanie 10** 86 | Korzystając z dokumentacji API klasy `QuerySet` z pkt. 3 wykonaj następujące zapytania za pomocą shella Django (**kod Pythona z zapytaniami umieść w pliku lab_3_zadanie_10.md w swoim repozytorium**): 87 | * wyświetl wszystkie obiekty modelu `Category`, 88 | * wyświetl obiekt modelu `Category` z id = 3, 89 | * wyświetl obiekty modelu `Category`, których nazwa rozpoczyna się na wybraną przez Ciebie literę alfabetu (tak, żeby był co najmniej jeden wynik), 90 | * wyświetl unikalną listę nazw kategorii ze wszystkich dodanych tematów (topików), 91 | * wyświetl tytuły postów posortowane alfabetycznie malejąco, 92 | * dodaj nową instancję obiektu klasy `Category` i zapisz w bazie. 93 | 94 | **Zadanie 11** 95 | Jeżeli nie robiłeś/-aś tego wcześniej to zatwierdź wszystkie zmiany w danym branchu i spróbuj wykonać merge z główną gałęzią. Proponuję wykonać tę operację przy pomocy interfejsu IDE PyCharm, aby przetestować wbudowane narzędzie do wspomagania procesu merge (o ile wystąpią konflikty). -------------------------------------------------------------------------------- /lab_01/readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2024Z 2 | 3 | ## Lab 1 4 | --- 5 | 6 | 7 | # 1. Warto zacząć od... 8 | 9 | 1.1 Zanim rozpoczniesz przygotowanie swojego pierwszego projektu z użyciem Django 4.2 rzuć okiem na release notes dla tej wersji: https://docs.djangoproject.com/en/5.2/releases/5.2/ 10 | 11 | 1.2 Zachęcam również do zaglądnięcia do kilku ciekawych tutoriali (poza oficjalnym oczywiście) wprowadzających do tworzenia aplikacji z użyciem Django: 12 | * https://realpython.com/get-started-with-django-1/ (i więcej kursów z tej strony w tematyce Django: https://realpython.com/tutorials/django/ ) 13 | * https://www.w3schools.com/django/ 14 | 15 | 16 | 17 | # Zadania 18 | 19 | 1. Jeżeli korzystasz na zajęciach ze swojego sprzętu to zainstaluj niezbędne oprogramowanie. 20 | 2. Stwórz środowisko wirtualne z interpreterem python na potrzeby swojego projektu. 21 | 3. Zainstaluj niezbędne paczki (aktualnie pakiet o nazwie `django` powinien wystarczyć, wskaż tu wersję 5.2). W razie problemów z instalacją rzuć okiem na oficjalną dokumentację pod adresem https://docs.djangoproject.com/pl/5.2/intro/install/ 22 | 4. Przygotuj swój pierwszy projekt oraz aplikację Django (warto znać różnicę między tymi pojęciami) bazując na oficjalnym tutorialu znajdującym się pod adresem https://docs.djangoproject.com/pl/5.2/intro/tutorial01/. Uruchom wbudowany serwer www (polecenie znajdziesz w tutorialu lub w przykładzie poniżej zadań) i sprawdź czy projekt się uruchamia. 23 | 5. Skonfiguruj lokalne i zdalne repozytorium git. Przygotuj odpowiedni plik `.gitignore` dla projektu Django i wykonaj inicjalny commit oraz push do zdalnego repo. Przetestuj czy konfiguracja jest odpowiednia. 24 | 6. Zastanów się nad tematem Twojego projektu i schematem bazy danych, który będzie potrzebny do jej działania. Możesz również wybrać na tym etapie silnik bazy danych, na którym będziesz chciał osadzić swoje dane. 25 | 26 | ## Przydatne instrukcje do zadań 27 | 28 | ### Zadanie 1: Instalacja niezbędnego oprogramowania 29 | 30 | 1. **Zainstaluj Python**: 31 | - Sprawdź, czy Python jest zainstalowany na twoim komputerze. Możesz to zrobić, uruchamiając polecenie w terminalu: 32 | ```bash 33 | python --version 34 | ``` 35 | - Jeśli nie masz Pythona zainstalowanego, pobierz go ze strony [oficjalnej Pythona](https://www.python.org/downloads/) i zainstaluj. 36 | 37 | 2. **Zainstaluj pip**: `pip` to menedżer pakietów Pythona. W większości instalacji Pythona `pip` jest domyślnie instalowany. Możesz to sprawdzić poleceniem: 38 | ```bash 39 | pip --version 40 | ``` 41 | 42 | ## Zadanie 2: Stworzenie środowiska wirtualnego 43 | 44 | 1. **Utwórz folder projektu**: 45 | - Otwórz terminal i przejdź do folderu, w którym chcesz utworzyć projekt. 46 | - Utwórz nowy folder: 47 | ```bash 48 | mkdir myproject 49 | cd myproject 50 | ``` 51 | 52 | 2. **Stwórz środowisko wirtualne**: 53 | 54 | - Uruchom polecenie, aby stworzyć wirtualne środowisko: 55 | ```bash 56 | python -m venv .venv 57 | # lub wykorzystując pakiet virtualenv 58 | python -m virtualenv .venv 59 | ``` 60 | 61 | - Aktywuj środowisko wirtualne (korzystając z narzędzi takich jak PyCharm lub VSC ta operacja powinna się wykonać automatycznie w momencie uruchomienia okna terminala wewnątrz w/w narzędzi): 62 | - Na Windows: 63 | ```bash 64 | .venv\\Scripts\\activate 65 | ``` 66 | - Na macOS/Linux: 67 | ```bash 68 | source .venv/bin/activate 69 | ``` 70 | Utworzenie środowiska wirtualnego jak już rozumiemy cały proces może być wykonane również za pomocą GUI narzędzi PyCharm lub VSC. W przypadku tego pierwszego możliwe jest to na etapie tworzenia nowego projektu lub w dowolnym momencie pracy nad nim. W przypadku VSC mamy możliwość wykonać to po zainstalowaniu pluginu do obsługi Pythona i skorzystaniu z **Command palette**, które będzie zawierało odpowiednie opcje. Proces tez zostanie zaprezentowany przez prowadzącego w trakcie zajęć. 71 | 72 | 73 | ## Zadanie 3: Instalacja niezbędnych paczek 74 | 75 | 1. **Zainstaluj Django**: 76 | - W aktywowanym środowisku wirtualnym, upewnij się, że masz zainstalowany pakiet `django`: 77 | ```bash 78 | # wyświetlenie listy zainstalowanych paczek 79 | pip list 80 | # instalacja Django (najnowsza wersja major 5, minor 5.2) 81 | pip install django==5.2.* 82 | # lub konkretnie (w momencie pisania dokumentacji najnowsza wersja release) 83 | pip install django==5.2.7 84 | ``` 85 | 86 | 2. **Rozwiązywanie problemów z instalacją**: 87 | - W razie problemów z instalacją, sprawdź oficjalną dokumentację [Django](https://docs.djangoproject.com/pl/5.2/intro/install/) w celu uzyskania wskazówek dotyczących instalacji. 88 | 89 | ## Zadanie 4: Przygotowanie pierwszego projektu i aplikacji Django 90 | 91 | 1. **Stwórz nowy projekt Django**: 92 | - W terminalu, upewnij się, że jesteś w katalogu głównym swojego projektu (blog). Następnie użyj polecenia: 93 | ```bash 94 | django-admin startproject blog 95 | ``` 96 | - Przejdź do folderu projektu: 97 | ```bash 98 | cd blog 99 | ``` 100 | 101 | 2. **Stwórz aplikację Django**: 102 | - W folderze projektu utwórz nową aplikację: 103 | ```bash 104 | python manage.py startapp posts 105 | ``` 106 | - **Rozróżnienie**: Projekt Django to kontener dla aplikacji, podczas gdy aplikacja to konkretna część projektu, która wykonuje określone zadania (np. obsługa użytkowników, bloga itp.). 107 | 108 | 3. **Uruchom wbudowany serwer**: 109 | - Aby uruchomić serwer, użyj polecenia: 110 | ```bash 111 | python manage.py runserver 112 | ``` 113 | - Sprawdź, czy aplikacja działa, otwierając przeglądarkę i wpisując adres `http://127.0.0.1:8000/`. 114 | 115 | ## Zadanie 5: Konfiguracja repozytorium Git 116 | 117 | 1. **Zainicjalizuj lokalne repozytorium**: 118 | - W folderze projektu, zainicjalizuj repozytorium Git: 119 | ```bash 120 | git init 121 | ``` 122 | 123 | 2. **Przygotuj plik `.gitignore`**: 124 | - Utwórz plik `.gitignore` w katalogu głównym projektu. Dodaj następujące linie, aby zignorować pliki środowiska wirtualnego i inne pliki, które nie powinny być wersjonowane: 125 | ``` 126 | .venv/ 127 | *.pyc 128 | __pycache__/ 129 | db.sqlite3 130 | ``` 131 | 132 | 3. **Wykonaj inicjalny commit**: 133 | - Dodaj wszystkie pliki do repozytorium: 134 | ```bash 135 | git add . 136 | ``` 137 | - Wykonaj commit: 138 | ```bash 139 | git commit -m "Initial commit" 140 | ``` 141 | 142 | 4. **Skonfiguruj zdalne repozytorium**: 143 | - Utwórz zdalne repozytorium na platformie, takiej jak GitHub, GitLab lub Bitbucket. 144 | - Po utworzeniu zdalnego repozytorium, połącz je z lokalnym repozytorium: 145 | ```bash 146 | git remote add origin https://github.com/username/repo.git 147 | ``` 148 | 149 | 5. **Wykonaj push do zdalnego repo**: 150 | - Prześlij zmiany do zdalnego repozytorium: 151 | ```bash 152 | git push -u origin main 153 | ``` 154 | - Sprawdź, czy konfiguracja jest odpowiednia, przeglądając zdalne repozytorium. -------------------------------------------------------------------------------- /lab_02/readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2025Z 2 | 3 | ## Lab 2 4 | --- 5 | 6 | **UWAGA!** Pamiętaj, aby na początku zajęć rozpocząć pracę na nowym branchu repozytorium! 7 | 8 | ### **1. Narzędzia administracyjne frameworka Django.** 9 | 10 | Django posiada narzędzie administracyjne w postaci skryptu `manage.py` oraz polecenia `django-admin` dzięki którym możemy wykonać wiele czynności administracyjnych, takich jak dodanie użytkownika administracyjnego, wykonanie migracji bazy danych, uruchomienie serwera i inne. Pełną listę poleceń można znaleźć w oficjalnej dokumentacji pod adresem: https://docs.djangoproject.com/pl/5.2/ref/django-admin/ oraz https://docs.djangoproject.com/en/5.2/ref/contrib/admin/ 11 | 12 | Dodatkowe i przydatne narzędzia można dodać poprzez instalację zewnętrznych modułów, np. [django-admin-tools](https://github.com/django-admin-tools/django-admin-tools) (**archiwalnie istniał problem z instalacją z Django 4.2**),[django-debug-toolbar](https://django-debug-toolbar.readthedocs.io/en/latest/index.html) lub [django-extensions](https://pypi.org/project/django-extensions/). 13 | 14 | Jeżeli w trakcie poprzednich zajęć wykonana została tylko pierwsza część oficjalnego tutoriala, to aby móc wykonać ćwiczenia i zadania dla bieżącego laboratorium należy wykonać poniższe czynności. 15 | 16 | **Stworzenie schematu bazy danych** 17 | 18 | >**Uwaga** Jak zostało już wspomniane na pierwszych zajęciach praca w trakcie zajęć powinn iść dwutorowo, tj. potrzebne są dwa projekty Django, jeden do wykonania zadań w trakcie zajęć oraz jako środowisko eksperymentalne, a drugi projekt Django to projekt na zaliczenie. Od studenta zależy czy będzie to jedno dobrze zorganizowane repozytorium czy dwa oddzielne, jednak zalecane są oddzielne. Będzie to również miało wpływ na przygotowanie odpowiedniej konfiguracji połączenia z bazą danych przed wykonaniem operacji `migrate` (patrz dokumentacja: https://docs.djangoproject.com/en/5.2/ref/databases/). 19 | Istnieje również dość ryzykowna opcja polegająca na stworzeniu oddzielnych aplikacji wewnątrz projektu, z której jedna będzie aplikacją testową, a druga docelowym projektem, którymi dodatkowo można zarządzać z poziomu oddzielnych gałęzi repozytorium. Jest to jednak rozwiązanie dość problematyczne, a w szczególności wymagające dużej uwagi w trakcie przełączania między nimi. 20 | 21 | Aby podłączyć bazę danych MySQL do projektu Django, należy wykonać kilka kroków, w tym instalację odpowiedniego sterownika, konfigurację projektu Django oraz utworzenie bazy danych. 22 | 23 | Zalecane jest korzystać z bazy danych SQLite, gdyż nie będzie sprawiać to problemów w trakcie prezentacji projektu na zaliczenie. 24 | 25 | Poniższa operacja stworzy schematy dla wszystkich zainstalowanych aplikacji (zmienna `INSTALLED_APPS` w pliku `settings.py`). Jeżeli nie są niezbędne to można wybrane linie zakomentować lub dodać na końcu polecenia nazwy aplikacji, dla których chcemy stworzyć schemat. 26 | 27 | ```console 28 | # przygotowanie plików migracji 29 | python manage.py makemigrations 30 | 31 | # wykonanie migracji 32 | python manage.py migrate 33 | ``` 34 | 35 | Warto przeczytać dokumentację dotyczącą tego polecenia, gdyż posiada ciekawe opcje, dość często potrzebne w trakcie początkowej pracy z modelem bazy danych i częstymi migracjami: https://docs.djangoproject.com/pl/5.2/ref/django-admin/#migrate 36 | 37 | 38 | **Stworzenie superużytkownika** 39 | 40 | ```console 41 | python manage.py createsuperuser 42 | ``` 43 | 44 | Ten użytkownik jest niezbędny, aby możliwe było zalogowanie się do panelu administracyjnego i wykonywanie w nim czynności administracyjnych. 45 | 46 | Zaloguj się do panelu administracyjnego i sprawdź dostępne w nim opcje na tym etapie tworzenia aplikacji. 47 | Adres panelu przy domyślnych ustawieniach to: http://127.0.0.1:8000/admin 48 | 49 | ### 2. Schemat bazy danych. 50 | 51 | Każdy projekt potrzebuje jakiejś formy trwałego przechowywania choćby niewielkiej ilości danych, ale większość z nich przewiduje również powiększanie tych zbiorów przez aktywność użytkowników i rozwój aplikacji. Jeżeli nie dokonałeś jeszcze wyboru to zastanów się nad docelowym silnikiem bazy danych. 52 | 53 | Tworzenie schematu bazy na potrzeby projektu Django można przeprowadzić na dwa sposoby. 54 | 55 | **Sposób 1.** 56 | Tworzymy odpowiednie implementacje klasy `django.db.models.Model` z API Django (patrz **listing 1**), a następnie poprzez migrację tworzymy schemat w docelowej bazie. 57 | 58 | **Sposób 2** 59 | Iżynieria wsteczna (ang. reverse engineering). Schemat bazy danych możemy przygotować bezpośrednio poprzez polecenia SQL lub narzędzia graficzne, a następnie poleceniem `manage.py inspectdb` wygenerować kod w języku Python zgodny z deklaracją, którą należy przygotować postępując sposobem pierwszym. Ten kod zostanie wyświetlony w konsoli i można strumień przekierować do pliku. Przykład poniżej. 60 | 61 | ```console 62 | # dopisanie wyjścia polecenia po lewej stronie znaków >> do pliku 63 | manage.py inspectdb >> models.py 64 | ``` 65 | 66 | ### 3. Modele w Django. 67 | 68 | **UWAGA!** 69 | Tworzymy w projekcie z labu numer 1 nową aplikację o nazwie `blog`, w której implementujemy kolejne przykłady zaprezentowane w materiałach. 70 | 71 | > Dokumentacja: https://docs.djangoproject.com/pl/5.2/topics/db/models/ 72 | 73 | Definicje modeli dodajemy w pliku `blog/posts/models.py`. 74 | Poniżej zaprezentowane zostaną dwa przykładowe modele, przygotowanie migracji, propagacje modeli na bazę danych oraz zarządzanie nimi z poziomu panelu administracyjnego. 75 | 76 | __*Listing 1:*__ 77 | ```python 78 | 79 | class Category(models.Model): 80 | name = models.CharField(max_length=60) 81 | 82 | 83 | class Topic(models.Model): 84 | name = models.CharField(max_length=60) 85 | category = models.ForeignKey(Category, on_delete=models.CASCADE) 86 | created = models.DateTimeField(auto_now_add=True) 87 | 88 | ``` 89 | 90 | Jeżeli chcemy aby mechanizm migracji wykrywał stworzone modele w naszej aplikacji i migrował je na bazę danych to należy dodać naszą aplikację do zmiennej `INSTALLED_APPS` w pliku `settings.py`: 91 | 92 | ```python 93 | INSTALLED_APPS = [ 94 | 'django.contrib.admin', 95 | 'django.contrib.auth', 96 | 'django.contrib.contenttypes', 97 | 'django.contrib.sessions', 98 | 'django.contrib.messages', 99 | 'django.contrib.staticfiles', 100 | # 'posts.apps.PostsConfig', 101 | # lub 102 | 'posts' 103 | ] 104 | ``` 105 | 106 | Następnie trzeba przygotować plik migracji poleceniem `manage.py makemigrations`, który przeanalizuje kod modeli i pozwoli nam na tym etapie rozwiązać ewentualne problemy ze spójnością danych, błędami w kodzie, wartościami null i innymi problemami, którymi nie będziemy musieli zajmować się na etapie propagacji modeli na schemat bazy danych. 107 | 108 | Ostatni krok to wykonanie migracji poprzez polecenie `manage.py migrate`, które stworzy odpowiednie tabele w domyślnej bazie danych (zdefiniowana w pliku `settings.py`). 109 | 110 | #### 3.1 Rejestracja modeli w panelu administracyjnym Django. 111 | 112 | Po wykonaniu migracji modele nie będą domyślnie widoczne w panelu administracyjnym Django. 113 | Należy je najpierw zarejestrować w pliku `admin.py` znajdującym się w folderze naszej aplikacji: 114 | 115 | ```python 116 | # modele musimy zaimportować 117 | from .models import Category, Topic 118 | 119 | # a następnie zarejestrować (pokazano najprostszy przypadek) 120 | admin.site.register(Category) 121 | admin.site.register(Topic) 122 | ``` 123 | 124 | Odświeżając teraz widok w penelu administracyjnym powinniśmy mieć możliwość zarządzania obiektami zarejestrowanych modeli. 125 | 126 | 127 | **ZADANIA** 128 | 129 | 1. Dodaj pole `description` do modelu `Category` o typie `TextField`. Przygotuj plik migracji (`makemigrations`), a następnie wykonaj migrację i sprawdź czy zmiany zostały propagowane na bazę (np. poprzez panel administracyjny lub przeglądając strukturę bazy danych). Przejrzyj zawartość pliku migracji. 130 | 2. Za pomocą polecenia `showmigrations` wylistuj wszystkie migracje dla danej aplikacji, a następnie sprawdzając w dokumentacji działanie polecenia `migrate` wycofaj ostatnią migrację. 131 | 3. Zainstaluj pakiet `django-debug-toolbar` i skonfiguruj go (link na początku tego pliku z materiałami). 132 | 4. Poprzez panel administracyjny przetestuj dodanie, modyfikację i usunięcie instancji modeli `Category` oraz `Topic`. Przeanalizuj co się dzieje w aplikacji poprzez `django debug toolbar`. 133 | 5. Zatwierdź niezbędne zmiany w repozytorium i wypchnij je do zdalnego repozytorium. Jeżeli nie zostało to jeszcze wykonane, należy dodać prowadzącego jako kolaboratora repozytorium w celu łatwiejszego dostepu do kodu projektu rozwijanego na zajęciach w celach archiwizacji oraz ewentualnej pomocy w rozwiązywaniu problemów w kodzie. 134 | 135 | 136 | ### Dodatkowe narzędzia i materiały. 137 | 138 | * Do obsługi wielu źródeł baz danych może przydać się darmowe narzędzie o nazwie DBeaver, które posiada również możliwość tworzenia schematów bazy danych a następnie generowania poleceń SQL z takiego schematu. Więcej: https://dbeaver.com/2022/07/07/forward-engineering-with-erd/ 139 | -------------------------------------------------------------------------------- /lab_04/readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2025Z 2 | 3 | ## Lab 4 - Django Rest Framework i serializacja danych. 4 | --- 5 | 6 | 7 | `Serializer` pozwala na konwersję złożonych danych na rodzime typy danych w języku Python, które można następnie łatwo przekształcić w JSON, XML lub inne typy treści. Serializatory zapewniają również deserializację, umożliwiając przekształcenie przeanalizowanych danych z powrotem w złożone typy po uprzednim sprawdzeniu poprawności danych przychodzących. 8 | 9 | `Serializer` w środowisku REST działa bardzo podobnie do klas `Form` i `ModelForm` w Django. W naszych projektach będziemy korzystać z: 10 | 11 | - `Serializer` - ogólny sposób kontrolowania requestów, 12 | - `ModelSerializer` - sprawdzanie modeli, inicjalizacja. 13 | 14 | Po dokładne informacje o serializacji odsyłamy do informacji z wykładu oraz [dokumentacji DRF](https://www.django-rest-framework.org/api-guide/serializers/). 15 | 16 | ## Przykład klasy do serializacji danych 17 | 18 | **__Listing 1__** 19 | ```python 20 | from rest_framework import serializers 21 | from .models import Person, Team, MONTHS, SHIRT_SIZES 22 | 23 | 24 | class PersonSerializer(serializers.Serializer): 25 | 26 | # pole tylko do odczytu, tutaj dla id działa też autoincrement 27 | id = serializers.IntegerField(read_only=True) 28 | 29 | # pole wymagane 30 | name = serializers.CharField(required=True) 31 | 32 | # pole mapowane z klasy modelu, z podaniem wartości domyślnych 33 | # zwróć uwagę na zapisywaną wartość do bazy dla default={wybór}[0] oraz default={wybór}[0][0] 34 | # w pliku models.py SHIRT_SIZES oraz MONTHS zostały wyniesione jako stałe do poziomu zmiennych skryptu 35 | # (nie wewnątrz modelu) 36 | shirt_size = serializers.ChoiceField(choices=SHIRT_SIZES, default=SHIRT_SIZES[0][0]) 37 | miesiac_dodania = serializers.ChoiceField(choices=MONTHS.choices, default=MONTHS.choices[0][0]) 38 | 39 | # odzwierciedlenie pola w postaci klucza obcego 40 | # przy dodawaniu nowego obiektu możemy odwołać się do istniejącego poprzez inicjalizację nowego obiektu 41 | # np. team=Team({id}) lub wcześniejszym stworzeniu nowej instancji tej klasy 42 | team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all()) 43 | 44 | # przesłonięcie metody create() z klasy serializers.Serializer 45 | def create(self, validated_data): 46 | return Person.objects.create(**validated_data) 47 | 48 | # przesłonięcie metody update() z klasy serializers.Serializer 49 | def update(self, instance, validated_data): 50 | instance.name = validated_data.get('name', instance.name) 51 | instance.shirt_size = validated_data.get('shirt_size', instance.shirt_size) 52 | instance.miesiac_dodania = validated_data.get('miesiac_dodania', instance.miesiac_dodania) 53 | instance.team = validated_data.get('team', instance.team) 54 | instance.save() 55 | return instance 56 | ``` 57 | 58 | Przetestowanie działania serializatora możemy również przeprowadzić z poziomu shella Django. Przykład kolejnych operacji poniżej. 59 | 60 | _**Listing 2**_ 61 | ```python 62 | 63 | from ankiety.models import Person 64 | from ankiety.serializers import PersonSerializer 65 | from rest_framework.renderers import JSONRenderer 66 | from rest_framework.parsers import JSONParser 67 | 68 | # 1. stworzenie nowej instancji klasy Person (opcjonalne, mamy panel admin do tego również) 69 | person = Person(name='Adam', miesiac_dodania=1) 70 | # utrwalenie w bazie danych 71 | person.save() 72 | 73 | # 2. inicjalizacja serializera 74 | serializer = PersonSerializer(person) 75 | serializer.data 76 | # output - natywny typ danych Pythona (dictionary) 77 | {'id': 16, 'name': 'Adam', 'shirt_size': ('S', 'Small'), 'miesiac_dodania': 1, 'team': None} 78 | 79 | # warto również zwrócić uwagę na wartość zmiennej shirt_size, która przyjęła wartość jako krotkę (która została zamieniona na typ str), gdyż w modelu został domyślny wybór określony jako SHIRT_SIZE[0] co jest pierwszą krotką dla tej kolekcji o wartości ('S', 'Small') 80 | # zamiana na SHIRT_SIZE[0][0] wskazałaby wartość 'S' i powinna również działać poprawnie dla modelu Person 81 | # output po zmianach w modelu 82 | # {'id': 19, 'name': 'Genowefa', 'shirt_size': 'S', 'miesiac_dodania': 1, 'team': None} 83 | 84 | # 3. serializacja danych do formatu JSON 85 | content = JSONRenderer().render(serializer.data) 86 | content 87 | 88 | # output 89 | b'{"id":16,"name":"Adam","shirt_size":"S","miesiac_dodania":1,"team":null}' 90 | 91 | # w takiej formie możemy przesłać obiekt (lub cały graf obiektów) przez sieć i po "drugiej stronie" dokonać deserializacji odtwarzając graf i stan obiektów 92 | 93 | import io 94 | 95 | stream = io.BytesIO(content) 96 | data = JSONParser().parse(stream) 97 | 98 | # tworzymy obiekt dedykowanego serializera i przekazujemy sparsowane dane 99 | deserializer = PersonSerializer(data=data) 100 | # sprawdzamy, czy dane przechodzą walidację (aktualnie tylko domyślna walidacja, dedykowana zostanie przedstawiona na kolejnych zajęciach) 101 | deserializer.is_valid() 102 | # output 103 | # False 104 | 105 | # to oznacza pojawienie się błędu walidacji 106 | deserializer.errors 107 | # output 108 | # {'team': [ErrorDetail(string='Pole nie może mieć wartości null.', code='null')]} 109 | 110 | # w samym modelu określone są dwa atrybuty null=True, blank=True, ale jak widać serializer nie bierze tego pod uwagę 111 | # musimy w klasie PersonSerializer zmodyfikować wartość dla pola team 112 | # dodając atrybut allow_null=True i uruchomić całe testowanie raz jeszcze 113 | 114 | # aby upewnić się w jaki sposób wyglądają pola wczytanego serializera/deserializera, możemy wywołać zmienną deserializer.fields, aby wyświetlić te dane 115 | deserializer.fields 116 | 117 | # lub 118 | repr(deserializer) 119 | 120 | # po powyższych zmianach walidacja powinna już się powieść 121 | # możemy sprawdzić jak wyglądają dane obiektów po deserializacji i walidacji 122 | deserializer.validated_data 123 | # output 124 | # OrderedDict([('name', 'Adam'), ('shirt_size', 'S'), ('miesiac_dodania', 1), ('team', None)]) 125 | 126 | # oraz utrwalamy dane 127 | deserializer.save() 128 | # sprawdzamy m.in. przyznane id 129 | deserializer.data 130 | ``` 131 | 132 | 133 | W przykładzie powyżej widać sporo nadmiarowej pracy w stosunku do zdefiniowanych wcześniej modeli (możemy oczywiście chcieć serializować również inne obiekty niż modele z naszego projektu) i na pewno pojawiła się refleksja - "Czy można wykorzystać jakąś część kodu z klas modeli?". Otóż można, wykorzystując klasę `ModelSerializer` z Django Rest Framework. 134 | 135 | 136 | ## Przykład klasy do serializacji danych (dziedziczącej po klasie ModelSerializer) 137 | 138 | Dokumentacja: https://www.django-rest-framework.org/api-guide/serializers/#modelserializer 139 | 140 | _**Listing 3**_ 141 | ```python 142 | class PersonModelSerializer(serializers.ModelSerializer): 143 | class Meta: 144 | # musimy wskazać klasę modelu 145 | model = Person 146 | # definiując poniższe pole możemy określić listę właściwości modelu, 147 | # które chcemy serializować 148 | fields = ['id', 'name', 'miesiac_dodania', 'shirt_size', 'team'] 149 | # definicja pola modelu tylko do odczytu 150 | read_only_fields = ['id'] 151 | ``` 152 | 153 | Powyższa klasa serializatora wykorzystuje wszystkie własności pól z klasy modelu, co znacznie zmniejsza ilość powielanego kodu i redukuje czas i ilość pracy niezbędny do dokonania zmian walidacji pól modelu. Te cechy zostaną pobrane do serializatora z definicji modelu, więc nie musimy ich przechowywać w dwóch miejscach. 154 | 155 | Testowanie kodu odbywa się adekwatnie do przykładu z listingu nr 1. 156 | 157 | 158 | # Zadania 159 | 160 | Celem ćwiczeń będzie stworzenie klas odpowiedzialnych za serializację danych w naszym projekcie API. Następujące polecenia powinny być wykonane dla każdego modelu w projekcie, do którego będziemy mieli dostęp zewnętrzny (przez dostęp do endpointu, np. dodanie nowej osoby do systemu). 161 | 162 | 163 | 1. Tworzymy nowy branch na potrzeby tego labu. 164 | 2. Zainstaluj moduł `djangorestframework` do środowiska wirtualnego i skonfiguruj DRF. 165 | 3. W **aplikacji**, w której znajduje się nasz model otwieramy/tworzymy plik `serializers.py` lub pakiet `serializers/`, a tam definiujemy nasze klasy serializatorów. 166 | 4. Napisz 1 klasę serializatora dla swojej aplikacji dziedziczącą po klasie `serializers.Serializer`. 167 | 5. Resztę modeli zaimplementuj w postaci serializatorów `ModelSerializer` jeżeli to możliwe. 168 | 6. Napisz kod prezentujący wykorzystanie wybranych dwóch serializatorów (patrz listing 2) ze swojej aplikacji i umieść go w pliku markdown o nazwie `drf_serializer_test.md` w folderze `./{twoja_aplikacja}/docs/` (utwórz folder `docs`). 169 | 170 | 171 | Do konsoli django można również przekazać cały plik z kodem testującym przekazując go jako potok wejściowy: 172 | ```console 173 | python manage.py shell < ./sciezka/do_pliku.py 174 | ``` 175 | 176 | Należy jednak pamiętać o tym, że w ten sposób musimy wykonywać importy z podaniem nazwy aplikacji np. `from ankiety.models import Person`, a nie `from .models import Person`. 177 | 178 | Minusem jest niestety dość nieczytelny output, gdzie znaczniki wyjścia konsoli Pythona (czyli >>>) mogą się wielokrotnie powtarzać. 179 | 180 | 181 | Dodatkowe wskazówki: 182 | 183 | * Nadpisujemy odpowiednie metody `create`, `update` itp. w momencie gdy musimy zapisać obiekty w inny niż domyślny sposób (np. chcemy dodać aktualnie zalogowanego użytkownika jako właściciela stworzonego modelu, chcemy zamienić wielkość znaków, usunąć jakieś znaki z tekstu itp.). 184 | -------------------------------------------------------------------------------- /lab_09/readme.md: -------------------------------------------------------------------------------- 1 | # Aplikacje WWW, semestr 2025Z 2 | 3 | ## 1. Szablony stron w Django. 4 | 5 | Framework Django dostarcza mechanizmu szablonów, dzięki któremu tworzenie modułowych stron opartych o HTML i CSS jest dużo prostsze niż tworzenie ich oddzielnie dla każdego z widoków w aplikacji. Wyobraź to sobie jako szablon, który definiuje różne obszary na naszej stronie np. nagłówek, gdzie znajduje się logo, może nawigacja. Poniżej możemy mieć dodatkowe obszary np. w układzie kolumnowym albo kafelki. Odwiedzając kolejne podstrony cała strona zazwyczaj nie zmienia się diametralnie, ale jest podmieniana tylko treść w niektórych obszarach. To znacznie ułatwia zarządzanie samym szablonem, gdyż zmniejsza ilość pracy w celu zmiany głównego szablonu dla całego serwisu, ale również ułatwia definicję kolejnych widoków i treści, która ma zostać podmieniona tylko w wybranych obszarach szablonu. 6 | 7 | Cały proces od przygotowania szablonu (-ów) do finalnego wyświetlenia jednego z widoków w ramach tego szablonu będzie wymagał sporo pracy, więc zostanie opisany w kolejnych krokach. 8 | 9 | **Krok 1 - Przygotowanie odpowiedniej struktury folderów i plików w projekcie.** 10 | 11 | Najpierw przygotujemy strukturę katalogów oraz kilka pustych plików HTML, w których umieścimy szablon oraz póki co dwie podstrony. Struktur została zaprezentowana poniżej. 12 | 13 | 14 | ```console 15 | 16 | blog 17 | - posts 18 | - templates 19 | - posts 20 | - category 21 | - detail.html 22 | - list.html 23 | - base.html 24 | ``` 25 | 26 | **Krok 2 - stworzenie szablonu bazowego.** 27 | 28 | W pliku `base.html` umieszczamy poniższą zawartość. 29 | 30 | _**Listing 1**_ 31 | ```html 32 | 33 | 34 |
35 |Nazwa: {{ category.name }}
73 |Opis: {{ category.description }}
74 |Nazwa: {{ category.name }}
155 |Opis: {{ category.description }}
156 | {% endblock %} 157 | ``` 158 | 159 | Teraz dodanie widoku w pliku `posts\views.py` 160 | 161 | _**Listing 6**_ 162 | ```python 163 | def category_detail(request, id): 164 | # pobieramy konkretny obiekt Category 165 | category = Category.objects.get(id=id) 166 | 167 | return render(request, 168 | "posts/category/detail.html", 169 | {'category': category}) 170 | ``` 171 | 172 | I dodajemy mapowanie URL na nowy widok w pliku `posts\urls.py` 173 | 174 | _**Listing 7**_ 175 | 176 | ```python 177 | from django.urls import path 178 | 179 | from . import views 180 | 181 | urlpatterns = [ 182 | path("welcome", views.welcome_view), 183 | path("categories", views.category_list), 184 | path("category/