Pisanie pierwszej aplikacji Django, część 2.

Ten tutorial zaczyna się, 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.

Konfiguracja bazy danych

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

Domyślnie konfiguracja używa SQLite’a. Jeśli jesteś nowy w bazach danych lub jesteś tylko zainteresowany wypróbowaniem Django, to najprostszy wybór. SQLite jest zawarty w Pythonie, więc nie będziesz musiał nic instalować, aby mieć bazę danych. Uruchamiając swój pierwszy prawdziwy projekt, będziesz chciał użyć bardziej skalowalnej bazy danych jak PostgreSQL, aby uniknąć bólu głowy związanego ze zmianami bazy danych po drodze.

Jeśli chcesz używać innej bazy danych, zainstaluj odpowiednie wiązania dla niej i zmień następujące klucze elemencie 'default' ustawienia DATABASES, aby zgadzały się z ustawieniami połączenia do twojej bazy danych:

  • ENGINE'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql' lub 'django.db.backends.oracle'. Dostępne są także inne back-endy.
  • NAME – nazwa twojej bazy danych. Jeśli używasz SQLite’a, baza danych będzie plikiem na twoim komputerze; w tym przypadku NAME powinno być pełną bezwzględną ścieżką, zawierającą nazwę, tego pliku. Domyślna wartość, os.path.join(BASE_DIR, 'db.sqlite3'), przechowa plik w katalogu twojego projektu.

Jeśli nie używasz SQLite’a jako swojej bazy danych, musisz dodać dodatkowe ustawienia, takie jak USER, PASSWORD i HOST. Po więcej szczegółów, zobacz dokumentację DATABASES.

Dla baz danych innych niż SQLite

Jeśli używasz bazy danych innej niż SQLite, upewnij się, że do tego miejsca stworzyłeś bazę danych. Zrób to używając „CREATE DATABASE nazwa_bazy_danych;” w interaktywnym prompcie twojej bazy danych.

Również upewnij się, że użytkownik bazy danych podany w mysite/settings.py ma uprawnienia „create database”. To pozwala na automatyczne tworzenie testowej bazy danych, której będziemy potrzebować w późniejszym tutorialu.

Jeśli używasz SQLite’a, nie potrzebujesz nic tworzyć – plik bazy danych zostawnie stworzony automatycznie, kiedy będzie potrzebny.

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:

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

The migrate command looks at the INSTALLED_APPS setting and creates any necessary database tables according to the database settings in your mysite/settings.py file and the database migrations shipped with the app (we’ll cover those later). You’ll see a message for each migration it applies. If you’re interested, run the command-line client for your database and type \dt (PostgreSQL), SHOW TABLES; (MariaDB, MySQL), .schema (SQLite), or SELECT TABLE_NAME FROM USER_TABLES; (Oracle) to display the tables Django created.

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.

This includes the migrations - unlike in Ruby On Rails, for example, migrations are entirely derived from your models file, and are essentially a history that Django can roll through to update your database schema to match your current models.

In our poll app, we’ll create two models: Question and Choice. A Question has a question and a publication date. A Choice has two fields: the text of the choice and a vote tally. Each Choice is associated with a Question.

These concepts are represented by Python classes. Edit the polls/models.py file so it looks like this:

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)

Here, each model is represented by a class that subclasses django.db.models.Model. Each model has a number of class variables, each of which represents a database field in the model.

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 Choice
    - Create model Question
    - Add field question to 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.

Migrations are how Django stores changes to your models (and thus your database schema) - they’re files on disk. You can read the migration for your new model if you like; it’s the file polls/migrations/0001_initial.py. Don’t worry, you’re not expected to read them every time Django makes one, but they’re designed to be human-editable in case you want to manually tweak how Django changes things.

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 Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;

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ć.)
  • The foreign key relationship is made explicit by a FOREIGN KEY constraint. Don’t worry about the DEFERRABLE parts; it’s telling PostgreSQL to not enforce the foreign key until the end of the transaction.
  • Jest to dostosowane do bazy danych, której używasz, więc pola typowe dla bazy danych, takie jak auto_increment (MySQL), serial (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.
  • The sqlmigrate command doesn’t actually run the migration on your database - instead, it prints it to the screen so that you can see what SQL Django thinks is required. It’s useful for checking what Django is going to do or if you have database administrators who require SQL scripts for changes.

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:

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

Używamy tego zamiast po prostu napisać „python”, ponieważ manage.py ustawia zmienną środowiskową DJANGO_SETTINGS_MODULE, która daje Django pythonową ścieżkę importu do twojego pliku mysite/settings.py.

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

>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# 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=<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.

Zapisz te zmiany i uruchom nowy interaktywny shell Pythona uruchamiając znów python manage.py shell:

>>> from polls.models import Choice, Question

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

Django admin login screen

Tłumaczenie jest domyślnie włączone, więc ekran logowania może zostać wyświetlony w twoim języku, zależnie od ustawień twojej przeglądarki i jeśli Django ma tłumaczenie dla tego języka.

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:

Django admin index page

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.

Only one more thing to do: we need to tell the admin that Question objects have an admin interface. To do this, open the polls/admin.py file, and edit it to look like this:

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:

Django admin index page, now with polls displayed

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:

Polls change list page

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

Editing form for question object

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

History page for question object

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.

Back to Top