# Django 5.2.8 - dokumentacja

# Pisanie pierwszej aplikacji Django, część 2.

Ten tutorial zaczyna się tam, gdzie skończył się Tutorial 1. Będziemy uruchamiać bazę danych, stworzymy twój pierwszy model i przejdziemy krótki wstęp do automatycznie generowanego panelu administracyjnego Django.

Gdzie szukać pomocy:

Jeśli masz trudności w przejściu tego tutorialu, przejdź do sekcji Uzyskiwanie pomocy często zadawanych pytań.

## Konfiguracja bazy danych

Otwórz teraz `mysite/settings.py`. To normalny moduł Pythona ze zmiennymi poziomu modułu reprezentującymi ustawienia Django.

By default, the `DATABASES` configuration uses SQLite. If you’re new to databases, or you’re just interested in trying Django, this is the easiest choice. SQLite is included in Python, so you won’t need to install anything else to support your database. When starting your first real project, however, you may want to use a more scalable database like PostgreSQL, to avoid database-switching headaches down the road.

If you wish to use another database, see details to customize and get your database running.

W trakcie edycji `mysite/settings.py`, ustaw `TIME_ZONE` na swoją strefę czasową.

Zwróć również uwagę na ustawienie `INSTALLED_APPS` na początku pliku. Trzyma ono nazwy wszystkich aplikacji Django, które są aktywowane w tej instancji Django. Aplikacje mogę być używane w wielu projektach i możesz je pakować i udostępniać innym do użytku w ich projektach.

Domyślnie `INSTALLED_APPS` zawiera następujące aplikacje, które są w pakiecie z Django:

- `django.contrib.admin` – Panel administracyjny. Niedługo go użyjesz.

- `django.contrib.auth` – System uwierzyteniania.

- `django.contrib.contenttypes` – Framework dla typów treści.

- `django.contrib.sessions` – Framework dla sesji.

- `django.contrib.messages` – Framework powiadomień.

- `django.contrib.staticfiles` – Framework do zarządzania plikami statycznymi.

Aplikacje te są włączone domyślnie jako udogodnienie dla powszechnych spraw.

Niektóre z tych aplikacji używają przynajmniej jednej tabeli w bazie danych, więc musimy stworzyć tabele w bazie danych zanim będziemy mogli je użyć. Aby to zrobić, uruchom następującą komendę:

/ 

    $ python manage.py migrate

    ...\> py manage.py migrate

Komenda `migrate` spogląda w ustawienie `INSTALLED_APPS` i tworzy potrzebne tabele bazy danych w nawiązaniu do ustawień bazy danych w twoim pliku `mysite/settings.py` i do migracji bazy danych zawartych w aplikacji (powiemy o tym później). Zobaczysz wiadomość o każdej migracji, która zostanie zastosowana. Jeśli jesteś zainsteresowany, uruchom klienta linii poleceń twojej bazy danych i wpisz `\dt` (PostgreSQL), `SHOW TABLES;` (MariaDB, MySQL), `.tables` (SQLite) lub `SELECT TABLE_NAME FROM USER_TABLES;` (Oracle), aby wyświetlić tabele, które stworzyło Django.

Dla minimalistów

Tak jak powiedzieliśmy wyżej, domyślne aplikacje są włączone dla powszechnych przypadków użycia, ale nie każdy je potrzebuje. Jeśli nie potrzebujesz żadnej z nich, nie krępuj się zakomentować lub usunąć odpowiednią linię (odpowiednie linie) z `INSTALLED_APPS` przed uruchomieniem `migrate`. Komenda `migrate` uruchomi migracje tylko dla aplikacji w `INSTALLED_APPS`.

## Tworzenie modeli

Teraz zdefiniujemy twoje modele – w istocie layout twojej bazy danych z dodatkowymi meta-danymi.

Filozofia

Model jest pojedynczym, pełnym źródłem informacji o twoich danych. Zawiera zasadnicze pola i zachowania danych, które przechowujesz. Django stosuje się do zasady DRY. Celem jest zdefiniowanie modelu danych w jednym miejscu i automatyczne czerpanie z niego przez inne elementy.

Wliczają się w to migracje – inaczej niż na przykład w Ruby On Rails, migracje całkowicie pochodzą z pliku modeli, i są co do istoty historią, przez którą Django może przewijać, aby aktualizować schemat bazy danych, by był zgodny z bieżącymi modelami.

W naszej aplikacji ankietowej, stworzymy dwa modele: `Question` and `Choice`. `Question` ma pytanie i datę publikacji. `Choice` ma dwa pola: treść wyboru i podsumowanie głosów. Każdy `Choice` jest związane z `Question`.

Te koncepty są reprezentowane przez klasy Pythona. Zmień plik `polls/models.py`, aby wyglądał tak:

`polls/models.py`

    from django.db import models

    class Question(models.Model):
        question_text = models.CharField(max_length=200)
        pub_date = models.DateTimeField("date published")

    class Choice(models.Model):
        question = models.ForeignKey(Question, on_delete=models.CASCADE)
        choice_text = models.CharField(max_length=200)
        votes = models.IntegerField(default=0)

Każdy model jest reprezentowany przez klasę, która dziedziczy po `django.db.models.Model`. Każdy model ma kilka zmiennych klasowych, z których każda reprezentuje pole bazy danych w modelu.

Każde pole jest reprezentowane przez instancję klasy `Field` – na przykład `CharField` dla pól znakowych i `DateTimeField` dla dat i czasu. Mówi to Django, jakie dane przechowuje każde pole.

Nazwa każdej instancji `Field` (np. `question_text` lub `pub_date`) jest nazwą pola, w formacie przyjaznym maszynom. Użyjesz tej wartości w kodzie Pythona i twoja baza danych użyje jej jako nazwy kolumny.

Można użyć opcjonalnego pierwszego argumentu pozycyjnego w `Field`, aby wyznaczyć nazwę w postaci czytelnej dla człowieka. Jest ona używana w kilku introspekcyjnych częściach Django i może służyć jako dokumentacja. Jeśli nie podamy tego argumentu, Django będzie używać nazwy maszynowej. W tym przykładzie mamy zdefiniowaną nazwę w postaci czytelnej dla człowieka tylko dla `Question.pub_date`. Dla wszystkich innych pól w tym modelu, nazwy maszynowe będą wystarczające jako nazwy pól w formie czytelnej dla człowieka.

Niektóre klasy `Field` mają wymagane argumenty. Na przykład `CharField` wymaga podania atrybutu `max_length`. Jest on używany nie tylko w schemacie bazy danych, ale również w walidacji, jak wkrótce zobaczymy.

`Field` może mieć też kilka opcjonalnych argumentów; w tym przypadku ustawiliśmy wartość atrybutu `default` pola `votes` na 0.

I wreszcie, zwróć uwagę na zdefiniowaną relację przy użyciu `ForeignKey`. Mówi to Django, że każdy `Choice` jest związany z pojedynczym `Question`. Django wspiera wszystkie powszechne relacje w bazach danych: wiele-do-jednego, wiele-do-wielu i jeden-do-jednego.

## Włączanie modeli

Taki mały kawałek kodu modelu daje Django wiele informacji. Na ich podstawie, Django jest w stanie:

- Stworzyć schemat bazy danych (polecenia `CREATE TABLE`) dla tej aplikacji.

- Stworzyć pythonowe API dostępu do bazy danych do dostępu do obiektów `Question` i `Choice`.

Ale najpierw musimy powiedziesz naszemu projektowi, że aplikacja `polls` jest zainstalowana.

Filozofia

Aplikacje Django są „wtyczkowe”: możesz używać aplikacji w wielu projektach i możesz udostępniać aplikacje, ponieważ nie muszą one być związane z daną instalacją Django.

Aby zawrzeć aplikację w naszym projekcie, musimy dodać odniesienie do jej klasy konfiguracyjnej w ustawieniu `INSTALLED_APPS`. Klasa `PollsConfig` jest w pliku `polls/apps.py`, więc jej kropkowana ścieżka to `'polls.apps.PollsConfig'`. Zmień plik `mysite/settings.py` i dodaj tę kropkowaną ścieżkę do ustawienia `INSTALLED_APPS`. Będzie to wyglądało tak:

`mysite/settings.py`

    INSTALLED_APPS = [
        "polls.apps.PollsConfig",
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
    ]

Teraz Django wie, że ma zawierać aplikację `polls`. Uruchommy następne polecenie:

/ 

    $ python manage.py makemigrations polls

    ...\> py manage.py makemigrations polls

Powinieneś zobaczyć coś podobnego do tego:

    Migrations for 'polls':
      polls/migrations/0001_initial.py
        + Create model Question
        + Create model Choice

Uruchamiając `makemigrations`, mówisz Django, że dokonałeś jakichś zmian w swoich modelach (w tym przypadku stworzyłeś nowe) i chcesz, aby te zmiany były przechowane jako migracja.

Migracje, to sposób w jaki Django przechowuje zmiany twoich modeli (i przez to zmiany schematu bazy danych) – są plikami na dysku. Możesz przeczytać migrację dla twojego nowego modelu, jeśli masz ochotę; to plik `polls/migrations/0001_initial.py`. Nie przejmuj się, nie musisz ich czytać za każdym razem, kiedy Django je tworzy, ale zostały one zaprojektowane, aby były modyfikowalne przez dewelopera, na wypadek, gdybyś chciał ręcznie poprawić to, jak Django zmienia rzeczy.

Jest komenda, która uruchomi dla ciebie migracje i będzie zarządzać automatycznie schematem bazy danych – nazywa się `migrate` i zaraz do niej dojdziemy – ale najpierw zobaczmy, jaki kod SQL ta migracja uruchomiła. Komenda `sqlmigrate` bierze jako argument nazwy migracji i zwraca ich SQL:

/ 

    $ python manage.py sqlmigrate polls 0001

    ...\> py manage.py sqlmigrate polls 0001

Powinieneś zobaczyć coś podobnego do tego (sformatowaliśmy dla czytelności):

    BEGIN;
    --
    -- Create model Question
    --
    CREATE TABLE "polls_question" (
        "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
        "question_text" varchar(200) NOT NULL,
        "pub_date" timestamp with time zone NOT NULL
    );
    --
    -- Create model Choice
    --
    CREATE TABLE "polls_choice" (
        "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
        "choice_text" varchar(200) NOT NULL,
        "votes" integer NOT NULL,
        "question_id" bigint NOT NULL
    );
    ALTER TABLE "polls_choice"
      ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
        FOREIGN KEY ("question_id")
        REFERENCES "polls_question" ("id")
        DEFERRABLE INITIALLY DEFERRED;
    CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

    COMMIT;

Zwróć uwagę na następującą rzecz:

- Dokładny output będzie różnił się w zależności od bazy danych, której używasz. Przykład powyżej jest wygenerowany dla PostgreSQLa.

- Nazwy tabel są wygenerowanie automatycznie przez połączenie nazwy aplikacji (`polls`) i nazwy modelu małymi literami – `question` i `choice`. (Możesz nadpisać to zachowanie.)

- Klucze główne (ID) są dodane automatycznie. (Możesz to też nadpisać.)

- W konwencji, Django dodaje `„_id”` do nazwy pola klucza obcego. (Tak, także to możesz nadpisać.)

- Relacja klucza obcego jest wyraźna przez constraint `FOREIGN KEY`. Nie przejmuj się częścią `DEFERRABLE`; to mówi PostgreSQLowi, aby nie egzekwował więzu klucza obcego aż do końca transakcji.

- Jest to dostosowane do bazy danych, której używasz, więc pola typowe dla bazy danych, takie jak `auto_increment` (MySQL), `bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY` (PostgreSQL) lub `integer primary key autoincrement` (SQLite) są obsługiwane dla ciebie automatycznie. To samo tyczy się wstawiania nazw pól w cudzysłowy – na przykład używania pojedynczego lub podwójnego cudzysłowu.

- Komenda `sqlmigrate` nie uruchomi migracji na twojej bazie danych – zamiast tego drukuje ją na ekran, abyś zobaczył, jaki kod SQL jest według Django wymagany. Jest to przydatne do sprawdzenia co zamierza zrobić Django lub jeśli masz administratorów bazy danych, którzy oczekują skryptów SQL na potrzeby zmian.

Jeśli jesteś zainteresowany, możesz też uruchomić `python manage.py check`; sprawdzi to jakiekolwiek problemy w twoim projekcie bez robienia migracji lub dotykania bazy danych.

Teraz uruchom ponowinie `migrate`, aby stworzyć te tabele modeli w twojej bazie danych:

/ 

    $ python manage.py migrate
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, polls, sessions
    Running migrations:
      Rendering model states... DONE
      Applying polls.0001_initial... OK

    ...\> py manage.py migrate
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, polls, sessions
    Running migrations:
      Rendering model states... DONE
      Applying polls.0001_initial... OK

Komenda `migrate` bierze wszystkie migracje, które jeszcze nie zostały zastosowane (Django śledzi, które są już zastosowane używając specjalnej tabeli w twojej bazie danych nazwanej `django_migrations`) i uruchamia je na twojej bazie danych – to znaczy synchronizuje zmiany, które zrobiłeś w modelach ze schematem danych w bazie danych.

Migracje są potężnym narzędziem, pozwalając ci zmieniać twoje modele w czasie, gdy rozwijasz swój projekt, bez konieczności usuwania bazy danych lub tabel i tworzenia nowych – specjalizuje się w upgrade’owaniu twojej bazy danych na żywo, bez tracenia danych. Omówimy je bardziej dogłębnie w późniejszej części tutorialu, lecz teraz zapamiętaj trzy kroki do tworzenia zmian w modelach:

- Zmień swoje modele (w `models.py`).

- Uruchom `python manage.py makemigrations`, aby stworzyć migracje dla tych zmian

- Uruchom `python manage.py migrate`, aby zastosować te zmiany na bazie danych.

Powodem, dla którego komendy do tworzenia i stosowania migracji są rozdzielne, jest to, że będziesz commitował migracje do swojego systemu kontroli wersji i zawrzesz je w swojej aplikacji; one nie tylko ułatwiają dewelopment, ale też są użyteczne dla innych deweloperów i w produkcji.

Przeczytaj dokumentację django-admin, aby uzyskać pełną informację, co może robić narzędzie `manage.py`.

## Zabawa z API

Teraz wskoczmy do interaktywnego shella Pythona i pobawmy się z otwartym API, które daje Django. Aby wywołać Pythonowego shella, użyj tej komendy:

/ 

    $ python manage.py shell

    ...\> py manage.py shell

We’re using this instead of simply typing „python”, because `manage.py` sets the `DJANGO_SETTINGS_MODULE` environment variable, which gives Django the Python import path to your `mysite/settings.py` file. By default, the `shell` command automatically imports the models from your `INSTALLED_APPS`.

Kiedy już jesteś w shellu, zbadaj API bazy danych:

    # No questions are in the system yet.
    >>> Question.objects.all()
    <QuerySet []>

    # Create a new Question.
    # Support for time zones is enabled in the default settings file, so
    # Django expects a datetime with tzinfo for pub_date. Use timezone.now()
    # instead of datetime.datetime.now() and it will do the right thing.
    >>> from django.utils import timezone
    >>> q = Question(question_text="What's new?", pub_date=timezone.now())

    # Save the object into the database. You have to call save() explicitly.
    >>> q.save()

    # Now it has an ID.
    >>> q.id
    1

    # Access model field values via Python attributes.
    >>> q.question_text
    "What's new?"
    >>> q.pub_date
    datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.UTC)

    # Change values by changing the attributes, then calling save().
    >>> q.question_text = "What's up?"
    >>> q.save()

    # objects.all() displays all the questions in the database.
    >>> Question.objects.all()
    <QuerySet [<Question: Question object (1)>]>

Ale zaraz. `<Question: Question object (1)>` nie jest pomocną reprezentacją tego obiektu. Poprawmy to edytując model `Question` (w pliku `polls/models.py`) i dodając metodę `__str__()` zarówno do `Question` jak i `Choice`:

`polls/models.py`

    from django.db import models

    class Question(models.Model):
        # ...
        def __str__(self):
            return self.question_text

    class Choice(models.Model):
        # ...
        def __str__(self):
            return self.choice_text

Dodanie metod `__str__()` do twoich modeli jest ważne, nie tylko dla twojej własnej wygody, gdy używasz interaktywnego prompta, ale także dlatego, że reprezentacje obiektów są używane w automatycznie generowanym panelu administracyjnym Django.

Dodajmy też własną metodę do tego modelu:

`polls/models.py`

    import datetime

    from django.db import models
    from django.utils import timezone

    class Question(models.Model):
        # ...
        def was_published_recently(self):
            return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

Zwróć uwagę na `import datetime` i `from django.utils import timezone`, odwołujące się kolejno do standardowego modułu Pythona `datetime` i narzędzi Django związanych ze strefami czasowymi w `django.utils.timezone`. Jeśli nie jesteś zaznajomiony z obsługą stref czasowych w Pythonie, możesz dowiedzieć się więcej w dokumentacji wsparcia stref czasowych.

Save these changes and start a new Python interactive shell. (If a three-chevron prompt (>>>) indicates you are still in the shell, you need to exit first using `exit()`). Run `python manage.py shell` again to reload the models.

    # Make sure our __str__() addition worked.
    >>> Question.objects.all()
    <QuerySet [<Question: What's up?>]>

    # Django provides a rich database lookup API that's entirely driven by
    # keyword arguments.
    >>> Question.objects.filter(id=1)
    <QuerySet [<Question: What's up?>]>
    >>> Question.objects.filter(question_text__startswith="What")
    <QuerySet [<Question: What's up?>]>

    # Get the question that was published this year.
    >>> from django.utils import timezone
    >>> current_year = timezone.now().year
    >>> Question.objects.get(pub_date__year=current_year)
    <Question: What's up?>

    # Request an ID that doesn't exist, this will raise an exception.
    >>> Question.objects.get(id=2)
    Traceback (most recent call last):
        ...
    DoesNotExist: Question matching query does not exist.

    # Lookup by a primary key is the most common case, so Django provides a
    # shortcut for primary-key exact lookups.
    # The following is identical to Question.objects.get(id=1).
    >>> Question.objects.get(pk=1)
    <Question: What's up?>

    # Make sure our custom method worked.
    >>> q = Question.objects.get(pk=1)
    >>> q.was_published_recently()
    True

    # Give the Question a couple of Choices. The create call constructs a new
    # Choice object, does the INSERT statement, adds the choice to the set
    # of available choices and returns the new Choice object. Django creates
    # a set (defined as "choice_set") to hold the "other side" of a ForeignKey
    # relation (e.g. a question's choice) which can be accessed via the API.
    >>> q = Question.objects.get(pk=1)

    # Display any choices from the related object set -- none so far.
    >>> q.choice_set.all()
    <QuerySet []>

    # Create three choices.
    >>> q.choice_set.create(choice_text="Not much", votes=0)
    <Choice: Not much>
    >>> q.choice_set.create(choice_text="The sky", votes=0)
    <Choice: The sky>
    >>> c = q.choice_set.create(choice_text="Just hacking again", votes=0)

    # Choice objects have API access to their related Question objects.
    >>> c.question
    <Question: What's up?>

    # And vice versa: Question objects get access to Choice objects.
    >>> q.choice_set.all()
    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
    >>> q.choice_set.count()
    3

    # The API automatically follows relationships as far as you need.
    # Use double underscores to separate relationships.
    # This works as many levels deep as you want; there's no limit.
    # Find all Choices for any question whose pub_date is in this year
    # (reusing the 'current_year' variable we created above).
    >>> Choice.objects.filter(question__pub_date__year=current_year)
    <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

    # Let's delete one of the choices. Use delete() for that.
    >>> c = q.choice_set.filter(choice_text__startswith="Just hacking")
    >>> c.delete()

Po więcej informacji na temat relacji między modelami, zobacz Dostęp do powiązanych obiektów.

## Wprowadzenie do panelu administracyjnego Django

Filozofia

Wygenerowanie strony administracyjnej dla pracowników lub klientów do dodawania, zmieniania i usuwania treści jest żmudną pracą, która nie wymaga dużo kreatywności. Z tego powodu Django całkowicie automatyzuje tworzenie interfejsu administracyjnego.

Django było pisane w środowisku publicystów, z bardzo wyraźnym oddzieleniem „wydawców treści” i „publiczną” witryną. Managerowie stron korzystają z systemu, aby dodać nowe historie, wydarzenia, wyniki sportowe, itp. i ta zawartość jest wyświetlana na publicznej witrynie. Django rozwiązuje problem tworząc jednolity interfejs dla administratorów witryn do edycji treści.

Panel administracyjny nie jest przeznaczony do użycia przez odwiedzających stronę. Jest dla menadżerów witryny.

### Tworzenie konta administratora

Najpierw musimy stworzyć użytkownika, który może logować się do panelu administracyjnego. Uruchom następującą komendę:

/ 

    $ python manage.py createsuperuser

    ...\> py manage.py createsuperuser

Wprowadź swoją pożądaną nazwę użytkownika i naciśnij enter.

    Username: admin

Zostaniesz poproszony o swój adres e-mail:

    Email address: admin@example.com

Ostatnim krokiem jest wprowadzenie hasła. Zostaniesz poproszony o wprowadzenie swojego hasła dwa razy, drugi raz na potwierdzenie pierwszego.

    Password: **********
    Password (again): *********
    Superuser created successfully.

### Uruchom serwer deweloperski

Panel administracyjny Django jest domyślnie uruchomiony. Uruchommy serwer deweloperski i poznajmy go.

Jeśli serwer nie jest uruchomiony, wystartuj go w ten sposób:

/ 

    $ python manage.py runserver

    ...\> py manage.py runserver

Teraz otwórz przeglądarkę internetową i wejdź na „/admin/” swojej lokalnej domeny – na przykład http://127.0.0.1:8000/admin/. Powinieneś zobaczyć ekran logowania panelu administracyjnego:

Jako że tłumaczenie jest domyślnie włączone, jeśli ustawisz `LANGUAGE_CODE`, ekran logowania zostanie wyświetlony w twoim języku (jeśli Django ma odpowiednie tłumaczenie).

### Wejście do panelu administracyjnego

Teraz spróbuj się zalogować kontem superusera, które stworzyłeś w poprzednim kroku. Powinieneś zobaczyć stronę główną panelu administracyjnego Django:

Powinieneś zobaczyć kilka typów modyfikowalnej treści: grupy i użytkowników. Pochodzą one z `django.contrib.auth`, frameworka uwierzytelniania Django.

### Udostępnij aplikację ankietową do modyfikowania w panelu administracyjnym

Ale gdzie jest nasza aplikacja ankietowa? Nie wyświetla się na stronie głównej panelu.

Jest do zrobienia jeszcze jedna rzecz: musimy powiedzieć panelowi, że obiekty `Question` mają interfejs administracyjny. Aby to zrobić, otwórz plik `polls/admin.py` i zmień go, aby wyglądał tak:

`polls/admin.py`

    from django.contrib import admin

    from .models import Question

    admin.site.register(Question)

### Poznaj funkcjonalność wolnego panelu administracyjnego

Kiedy już zarejestrowaliśmy `Question`, Django wie, że powinno być wyświetlane na stronie głównej panelu:

Kliknij w „Questions”. Jesteś teraz na stronie „change list” dla pytań. Ta strona wyświetla wszystkie pytania w bazie danych i pozwala wybrać ci jedno do zmiany. Jest tutaj pytanie „What’s up?”, które stworzyliśmy wcześniej:

Kliknij w pytanie „What’s up?”, aby je edytować:

Rzeczy, na które należy zwrócić tutaj uwagę:

- Formularz jest automatycznie wygenerowany z modelu `Question`.

- Różne typu pól modeli (`DateTimeField`, `CharField`) mają odpowiednie widgety kontrolek HTML. Każdy typ pola wie jak się wyświetlać w panelu Django.

- Każde `DateTimeField` dostaje wolne javascriptowe skróty. Daty dostają skrót „Dzisiaj” i popup z kalendarzem a czas dostaje skrót „Teraz” i wygodny popup, który wylistowuje często wprowadzane godziny.

Dolna część strony daje ci klika możliwości:

- Zapisz – Zapisuje zmiany i powraca do strony change-list dla tego typu obiektów.

- Zapisz i kontynuuj edycję – Zapisuje zmiany i przeładowuję stronę panelu dla tego obiektu.

- Zapisz i dodaj nowe – Zapisuje zmiany i ładuje nowy, pusty formularz dla tego typu obiektu.

- Usuń – Wyświetla stronę potwierdzenia usunięcia.

Jeśli wartość pola „Date published” nie zgadza się z czasem, w którym stworzyłeś pytanie w Tutorialu 1, prawdopodobnie oznacza to, że zapomniałeś podać poprawnej wartości ustawienia `TIME_ZONE`. Zmień je, przeładuj stronę i sprawdź, że pojawia się poprawna wartość.

Zmień „Date published” klikając skróty „Dzisiaj” i „Teraz”. Następniej kliknij „Zapisz i kontynuuj edycję”. Następnie kliknij w „Historia” w prawym górnym rogu. Zobaczysz stronę wylistowującą wszystkie zmiany dokonane na tym obiekcie przez panel administracyjny Django, z czasem i nazwą użytkownika osoby, która zrobiła zmianę:

Jeśli czujesz się dobrze z API modeli i zaprzyjaźniłeś się z panelem administracyjnym, przeczytaj 3. część tego tutoriala, aby dowiedzieć się jak dodać więcej widoków do naszej aplikacji ankietowej.