Введение
Этот туториал начинается там, где закончился Создание базового приложения для опросов. Мы настроим базу данных, создадим свою первую модель и кратко познакомимся с автоматически создаваемым административным сайтом Django.
💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал
Этот туториал начинается там, где закончился Создание базового приложения для опросов. Мы настроим базу данных, создадим свою первую модель и кратко познакомимся с автоматически создаваемым административным сайтом Django.
Теперь откройте mysite/settings.py
. Это обычный Python-модуль с переменными уровня модуля, представляющими настройки Django.
По умолчанию конфигурация использует SQLite. Если вы впервые сталкиваетесь с базами данных или просто хотите попробовать Django, это самый простой выбор. SQLite входит в состав Python, поэтому вам не нужно ничего дополнительно устанавливать для поддержки своей базы данных. Однако, когда вы начинаете свой первый настоящий проект, вы, возможно, захотите использовать более масштабируемую базу данных, например, PostgreSQL, чтобы избежать головной боли при смене базы данных впоследствии.
Если вы хотите использовать другую базу данных, установите соответствующие биндинги базы данных <database-installation>
и измените следующие ключи в элементе 'default'
словаря DATABASES
, чтобы они соответствовали вашим настройкам подключения к базе данных:
ENGINE <DATABASE-ENGINE>
-- Либо 'django.db.backends.sqlite3'
, 'django.db.backends.postgresql'
, 'django.db.backends.mysql'
или 'django.db.backends.oracle'
. Доступны и другие бэкенды NAME
-- Имя вашей базы данных. Если вы используете SQLite, база данных будет файлом на вашем компьютере; в этом случае NAME
должен быть полным абсолютным путём, включая имя файла, к этому файлу. Значение по умолчанию, BASE_DIR / 'db.sqlite3'
, сохранит файл в директории вашего проекта.Если вы не используете SQLite в качестве своей базы данных, необходимо добавить дополнительные настройки, такие как USER
, PASSWORD
и HOST
. Подробнее см. в справочной документации по DATABASES
.
Для баз данных, отличных от SQLite
Если вы используете базу данных, отличную от SQLite, убедитесь, что вы создали базу данных к этому времени. Сделайте это с помощью команды "CREATE DATABASE database_name;
" в интерактивном提示符 вашей базы данных.
Также убедитесь, что пользователь базы данных, указанный в mysite/settings.py
, имеет права "создавать базу данных". Это позволяет автоматически создавать тестовую базу данных <the-test-database>
, которая понадобится в последующем туториале.
Если вы используете SQLite, вы не нужно создавать ничего заранее - файл базы данных будет создан автоматически, когда он понадобится.
Во время редактирования mysite/settings.py
установите TIME_ZONE
в вашу временную зону.
Также обратите внимание на настройку INSTALLED_APPS
в начале файла. В ней хранятся имена всех Django-приложений, активированных в этом экземпляре Django. Приложения могут использоваться в нескольких проектах, и вы можете упаковать и распространить их для использования другими в своих проектах.
По умолчанию INSTALLED_APPS
содержит следующие приложения, все они входят в состав Django:
django.contrib.admin
-- Административный сайт. Вы вскоре будете с ним работать.django.contrib.auth
-- Система аутентификации.django.contrib.contenttypes
-- Фреймворк для типов содержимого.django.contrib.sessions
-- Фреймворк для сессий.django.contrib.messages
-- Фреймворк для сообщений.django.contrib.staticfiles
-- Фреймворк для управления статическими файлами.Эти приложения включены по умолчанию для удобства при общем случае.
Однако некоторые из этих приложений используют по крайней мере одну таблицу базы данных, поэтому мы должны создать таблицы в базе данных, прежде чем сможем их использовать. Для этого выполните следующую команду:
cd ~/project/mysite
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
Команда migrate
смотрит на настройку INSTALLED_APPS
и создает любые необходимые таблицы базы данных в соответствии с настройками базы данных в вашем файле mysite/settings.py
и миграциями базы данных, поставляемыми вместе с приложением (мы поговорим об этом позже). Вы увидите сообщение для каждой миграции, которую она применяет. Если вы заинтересованы, запустите командную оболочку для своей базы данных и введите \dt
(PostgreSQL), SHOW TABLES;
(MariaDB, MySQL), .tables
(SQLite) или SELECT TABLE_NAME FROM USER_TABLES;
(Oracle), чтобы отобразить таблицы, созданные Django.
Для минималистов
Как мы говорили выше, стандартные приложения включены для общего случая, но не всем они нужны. Если вы не нуждаетесь в одном или нескольких из них, не стесняйтесь закомментировать или удалить соответствующую строку из INSTALLED_APPS
перед запуском migrate
. Команда migrate
будет выполнять миграции только для приложений в INSTALLED_APPS
.
Теперь мы определим ваши модели - по существу, схему вашей базы данных, с дополнительными метаданными.
Модель - это единственный определяющий источник информации о ваших данных. Она содержит основные поля и поведение данных, которые вы храните. Django следуют принципу DRY <dry>
. Цель - определить вашу модель данных в одном месте и автоматически получать из нее все необходимое.
Это включает в себя миграции - в отличие, например, от Ruby On Rails, миграции полностью определяются вашим файлом моделей и представляют собой, по сути, историю, которую Django может использовать для обновления схемы вашей базы данных, чтобы она соответствовала вашим текущим моделям.
В нашем приложении для опросов мы создадим две модели: Question
(Вопрос) и Choice
(Вариант ответа). Question
имеет вопрос и дату публикации. Choice
имеет два поля: текст варианта ответа и счетчик голосов. Каждый Choice
связан с Question
.
Эти концепции представлены в виде Python-классов. Отредактируйте файл 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)
Здесь каждая модель представлена классом, который наследуется от django.db.models.Model
. Каждая модель имеет ряд переменных класса, каждая из которых представляет собой поле базы данных в модели.
Каждое поле представлено экземпляром класса ~django.db.models.Field
- например, ~django.db.models.CharField
для текстовых полей и ~django.db.models.DateTimeField
для дат и времени. Это сообщает Django, какой тип данных хранит каждое поле.
Имя каждого экземпляра ~django.db.models.Field
(например, question_text
или pub_date
) - это имя поля в формате, удобном для машин. Вы будете использовать это значение в своем Python-коде, а ваша база данных будет использовать его в качестве имени столбца.
Вы можете использовать необязательный первый позиционный аргумент для ~django.db.models.Field
, чтобы указать человекочитаемое имя. Это используется в нескольких интроспективных частях Django и служит одновременно и документацией. Если это поле не указано, Django будет использовать машиночитаемое имя. В этом примере мы определили только человекочитаемое имя для Question.pub_date
. Для всех других полей в этой модели имя поля в машиночитаемом формате будет достаточно и в качестве его человекочитаемого имени.
Некоторые классы ~django.db.models.Field
требуют обязательных аргументов. Например, ~django.db.models.CharField
требует, чтобы вы указали для него ~django.db.models.CharField.max_length
. Это используется не только в схеме базы данных, но и при валидации, как мы вскоре увидим.
~django.db.models.Field
также может иметь различные необязательные аргументы; в этом случае мы установили значение ~django.db.models.Field.default
для votes
равным 0.
Наконец, обратите внимание, что определена связь, с использованием ~django.db.models.ForeignKey
. Это сообщает Django, что каждый Choice
связан с одним Question
. Django поддерживает все общие типы отношений базы данных: многие-ко-одному, многие-ко-многим и один-ко-одному.
Этот небольшой кусок кода модели дает Django много информации. С его помощью Django может:
CREATE TABLE
инструкции) для этого приложения.Question
и Choice
.Но сначала нам нужно сообщить нашему проекту, что приложение polls
установлено.
Приложения Django "плаггируемы": вы можете использовать приложение в нескольких проектах, и вы можете распространять приложения, потому что они не должны быть связаны с определенной установкой Django.
Чтобы включить приложение в наш проект, нам нужно добавить ссылку на его класс конфигурации в настройку INSTALLED_APPS
. Класс PollsConfig
находится в файле polls/apps.py
, поэтому его полный путь с точками - 'polls.apps.PollsConfig'
. Отредактируйте файл mysite/settings.py
и добавьте этот полный путь с точками в настройку INSTALLED_APPS
. Он будет выглядеть так:
INSTALLED_APPS = [
"polls.apps.PollsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
Теперь Django знает, что нужно включить приложение polls
. Запустим еще одну команду:
python manage.py makemigrations polls
Вы должны увидеть что-то похожее на следующее:
Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
Запустив makemigrations
, вы сообщаете Django, что вы внесли некоторые изменения в ваши модели (в этом случае, вы создали новые) и что вы хотите, чтобы изменения были сохранены в виде миграции.
Миграции - это то, как Django хранит изменения в ваших моделях (и, следовательно, в схеме вашей базы данных) - это файлы на диске. Вы можете прочитать миграцию для вашей новой модели, если хотите; это файл polls/migrations/0001_initial.py
. Не беспокойтесь, вам не нужно каждый раз читать их, когда Django создает их, но они предназначены для редактирования человеком, если вы хотите вручную настроить, как Django изменяет вещи.
Есть команда, которая будет выполнять миграции для вас и автоматически управлять схемой вашей базы данных - это команда migrate
, и мы поговорим об этом сейчас - но сначала давайте посмотрим, какой SQL будет выполняться при этой миграции. Команда sqlmigrate
принимает имена миграций и возвращает их SQL:
python manage.py sqlmigrate polls 0001
Вы должны увидеть что-то похожее на следующее (мы отформатировали его для удобства чтения):
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;
Обратите внимание на следующее:
polls
) и нижнего регистра имени модели - question
и choice
. (Вы можете переопределить это поведение.)"_id"
к имени поля внешнего ключа. (Да, вы также можете переопределить это.)FOREIGN KEY
. Не беспокойтесь о частях DEFERRABLE
; это говорит PostgreSQL не накладывать ограничение внешнего ключа до конца транзакции.auto_increment
(MySQL), bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
(PostgreSQL) или integer primary key autoincrement
(SQLite), обрабатываются для вас автоматически. То же относится к кавычкам имен полей - например, к использованию двойных или одинарных кавычек.sqlmigrate
на самом деле не выполняет миграцию на вашей базе данных - вместо этого она выводит ее на экран, чтобы вы могли увидеть, какой SQL Django считает необходимым. Это полезно для проверки того, что Django собирается сделать, или если у вас есть администраторы базы данных, которым требуются SQL-скрипты для изменений.Если вы заинтересованы, вы также можете запустить python manage.py check <check>
; это проверяет наличие любых проблем в вашем проекте без создания миграций или изменения базы данных.
Теперь запустите migrate
еще раз, чтобы создать эти таблицы моделей в вашей базе данных:
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0001_initial... OK
Команда migrate
берет все миграции, которые еще не были применены (Django отслеживает, какие из них применены, с помощью специальной таблицы в вашей базе данных, называемой django_migrations
) и выполняет их для вашей базы данных - по сути, синхронизируя изменения, которые вы внесли в ваши модели, с схемой в базе данных.
Миграции очень мощны и позволяют вам изменять ваши модели по мере развития вашего проекта, не удаляя при этом вашу базу данных или таблицы и не создавая новые - она специализируется на обновлении вашей базы данных в реальном времени без потери данных. Мы рассмотрим их более подробно в более поздней части этого туториала, но пока помните три шага по изменению моделей:
models.py
).python manage.py makemigrations <makemigrations>
, чтобы создать миграции для этих измененийpython manage.py migrate <migrate>
, чтобы применить эти изменения к базе данных.Причина, по которой есть отдельные команды для создания и применения миграций, заключается в том, что вы будете коммитить миграции в свою систему контроля версий и распространять их вместе с вашим приложением; они не только облегчают ваше развитие, но и могут быть использованы другими разработчиками и в продакшене.
Читайте документацию по django-admin </ref/django-admin>
для получения полной информации о том, что может делать утилита manage.py
.
Теперь давайте займемся в интерактивной оболочке Python и поэкспериментируем с бесплатным API, которое предоставляет Django. Чтобы вызвать Python-оболочку, используйте эту команду:
python manage.py shell
Мы используем ее вместо простого ввода "python", потому что manage.py
устанавливает переменную окружения DJANGO_SETTINGS_MODULE
, которая дает Django путь к импорту Python в ваш файл mysite/settings.py
.
Как только вы в оболочке, изучите API базы данных </topics/db/queries>
:
>>> from polls.models import Choice, Question ## Импортируем классы моделей, которые только что написали.
## В системе еще нет вопросов.
>>> Question.objects.all()
<QuerySet []>
## Создаем новый вопрос.
## В настройках по умолчанию включена поддержка часовых поясов, поэтому
## Django ожидает datetime с tzinfo для pub_date. Используйте timezone.now()
## вместо datetime.datetime.now() и все будет в порядке.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
## Сохраняем объект в базу данных. Нужно явно вызвать save().
>>> q.save()
## Теперь у него есть ID.
>>> q.id
1
## Доступ к значениям полей модели через атрибуты Python.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2023, 9, 7, 1, 18, 48, 335644, tzinfo=datetime.timezone.utc)
## Меняем значения, изменяя атрибуты, а затем вызываем save().
>>> q.question_text = "What's up?"
>>> q.save()
## objects.all() отображает все вопросы в базе данных.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
Подождите минутку. <Question: Question object (1)>
не очень информативное представление этого объекта. Исправим это, отредактировав модель Question
(в файле polls/models.py
) и добавив метод ~django.db.models.Model.__str__
как для Question
, так и для Choice
:
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
Важно добавить методы ~django.db.models.Model.__str__
к вашим моделям, не только для вашего удобства при работе с интерактивной командной строкой, но и потому, что представления объектов используются повсюду в автоматически создаваемом административном интерфейсе Django.
Давайте также добавим к этой модели пользовательский метод:
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)
Обратите внимание на добавление import datetime
и from django.utils import timezone
, чтобы ссылаться на стандартный модуль datetime
Python и утилиты Django, связанные с часовыми поясами, в django.utils.timezone
соответственно. Если вы не знакомы с обработкой часовых поясов в Python, вы можете узнать больше в документации по поддержке часовых поясов </topics/i18n/timezones>
.
Сохраните эти изменения и запустите новую Python-интерактивную оболочку, запустив python manage.py shell
снова:
>>> from polls.models import Choice, Question
## Убедимся, что наша добавка к __str__() сработала.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
## Django предоставляет богатое API для поиска в базе данных, которое полностью управляется
## ключевыми аргументами.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith="What")
<QuerySet [<Question: What's up?>]>
## Получаем вопрос, опубликованный в этом году.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
## Запрашиваем ID, который не существует, это вызовет исключение.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.
## Поиск по первичному ключу - это наиболее распространенный случай, поэтому Django предоставляет
## быстрый способ для точного поиска по первичному ключу.
## Следующая строка эквивалентна Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>
## Убедимся, что наша пользовательская функция сработала.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
## Добавим несколько вариантов ответа для вопроса. Вызов create() создает новый
## объект Choice, выполняет оператор INSERT, добавляет вариант ответа в набор
## доступных вариантов и возвращает новый объект Choice. Django создает
## набор для хранения "другой стороны" отношения ForeignKey
## (например, варианты ответа для вопроса), который можно получить через API.
>>> q = Question.objects.get(pk=1)
## Отобразим все варианты ответа из связанного набора объектов - пока их нет.
>>> q.choice_set.all()
<QuerySet []>
## Создадим три варианта ответа.
>>> 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 имеют API-доступ к их связанным объектам Question.
>>> c.question
<Question: What's up?>
## И наоборот: объекты Question получают доступ к объектам Choice.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
## API автоматически следит за связями столько, сколько вам нужно.
## Используйте двойные нижние подчеркивания, чтобы разделить связи.
## Это работает на любом уровне вложенности, который вы хотите; ограничений нет.
## Найдем все варианты ответа для любого вопроса, у которого pub_date
## попадает в этот год (переиспользуем переменную 'current_year', которую мы создали выше).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
## Удалим один из вариантов ответа. Используем для этого delete().
>>> c = q.choice_set.filter(choice_text__startswith="Just hacking")
>>> c.delete()
Для получения более подробной информации о связях моделей см. Доступ к связанным объектам </ref/models/relations>
. Для более подробного описания того, как использовать двойные нижние подчеркивания для выполнения поиска по полям через API, см. Поиск по полям <field-lookups-intro>
. Для полной информации о API базы данных см. наш Справочник по API базы данных </topics/db/queries>
.
Генерация административных сайтов для вашего персонала или клиентов для добавления, изменения и удаления контента - это утомительная работа, которая не требует большей креативности. Поэтому Django полностью автоматизирует создание административных интерфейсов для моделей.
Django был написан в редакционной среде, с четким разделением между "публикацией контента" и "публичным" сайтом. Менеджеры сайта используют систему для добавления новостей, мероприятий, спортивных результатов и т.д., и этот контент отображается на публичном сайте. Django решает проблему создания единого интерфейса для администраторов сайта для редактирования контента.
Административный интерфейс не предназначен для использования посетителями сайта. Это для менеджеров сайта.
Сначала нам нужно создать пользователя, который сможет войти в административный сайт. Запустите следующую команду:
python manage.py createsuperuser
Введите желаемое имя пользователя и нажмите Enter.
Username: admin
Затем вас попросят ввести желаемый адрес электронной почты:
Email address: admin@example.com
Последний шаг - ввести ваш пароль. Вас попросят ввести пароль дважды, второе время для подтверждения первого.
Password: 12345678
Password (again): 12345678
This password is too common.
This password is entirely numeric.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
Административный сайт Django активирован по умолчанию. Запустим сервер разработки и изучим его.
Если сервер не запущен, запустите его так:
python manage.py runserver
Теперь откройте веб-браузер в вкладке VNC и перейдите по адресу "/admin/" на вашем локальном домене - например, http://127.0.0.1:8000/admin/
. Вы должны увидеть экран входа в административный интерфейс:
Поскольку перевод </topics/i18n/translation>
включен по умолчанию, если вы установите LANGUAGE_CODE
, экран входа будет отображаться на заданном языке (если Django имеет соответствующие переводы).
Теперь попробуйте войти с помощью учетной записи суперпользователя, которую вы создали на предыдущем шаге. Вы должны увидеть главную страницу административного интерфейса Django:
Вы должны увидеть несколько типов редактируемого контента: группы и пользователей. Они предоставляются django.contrib.auth
, фреймворком аутентификации, поставляемым вместе с Django.
Но где наше приложение для опросов? Его не отображается на главной странице административного интерфейса.
Еще одно действие нужно совершить: мы должны сообщить административному интерфейсу, что объекты Question
имеют административный интерфейс. Для этого откройте файл polls/admin.py
и отредактируйте его так, чтобы он выглядел следующим образом:
from django.contrib import admin
from.models import Question
admin.site.register(Question)
Теперь, когда мы зарегистрировали Question
, Django знает, что его нужно отобразить на главной странице административного интерфейса:
Нажмите "Вопросы". Теперь вы на странице "списка изменений" для вопросов. Эта страница отображает все вопросы в базе данных и позволяет выбрать один для изменения. Там есть вопрос "Что нового?", который мы создали ранее:
Нажмите на вопрос "Что нового?", чтобы отредактировать его:
Обратите внимание на следующее:
Question
.~django.db.models.DateTimeField
, ~django.db.models.CharField
) соответствуют соответствующим виджетам ввода HTML. Каждый тип поля знает, как отобразить себя в административном интерфейсе Django.~django.db.models.DateTimeField
получает бесплатные JavaScript-шорткаты. Для дат есть ярлык "Сегодня" и календарь-выпадающий, а для времени - ярлык "Теперь" и удобный выпадающий список, в котором перечислены часто вводимые времена.В нижней части страницы вам предоставляется несколько вариантов:
Если значение "Дата публикации" не соответствует времени, когда вы создали вопрос в разделе Создание базового приложения для опросов, это, вероятно, означает, что вы забыли установить правильное значение для настройки TIME_ZONE
. Измените ее, перезагрузите страницу и проверьте, появляется ли правильное значение.
Измените "Дата публикации", нажав на ярлыки "Сегодня" и "Теперь". Затем нажмите "Сохранить и продолжить редактирование". Затем нажмите "История" в верхнем правом углу. Вы увидите страницу, на которой перечислены все изменения, сделанные с помощью административного интерфейса Django для этого объекта, с меткой времени и именем пользователя, который внес изменения:
Когда вы будете знакомы с API моделей и ознакомитесь с административным сайтом, прочитайте раздел Создание представлений для публичного интерфейса, чтобы узнать, как добавить больше представлений в наше приложение для опросов.
Поздравляем! Вы завершили лабораторную работу по настройке базы данных. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.