Написание вашей первой Django-приложения, часть 2
Этот учебник начинается там, где Учебник 1 закончился. Мы настроим базу данных, создадим вашу первую модель и получим краткое введение в автоматически сгенерированный административный интерфейс Django.
Где получить помощь:
Если у вас возникнут трудности с прохождением этого учебника, перейдите к разделу Получение помощи раздела вопросов и ответов.
Настройка базы данных
Теперь откройте mysite/settings.py. Это обычный Python-модуль с переменными уровня модуля, представляющими настройки Django.
По умолчанию конфигурация DATABASES использует SQLite. Если вы новичок в базах данных или просто хотите попробовать Django, это самый простой выбор. SQLite включен в Python, поэтому вам не нужно устанавливать ничего дополнительного для поддержки вашей базы данных. Однако при создании вашего первого реального проекта вы можете захотеть использовать более масштабируемую базу данных, например PostgreSQL, чтобы избежать проблем со сменой базы данных в будущем.
Если вы хотите использовать другую базу данных, см. подробности настройки и запуска вашей базы данных.
Во время редактирования 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– Фреймворк для управления статическими файлами.
Эти приложения включаются по умолчанию для удобства в общем случае.
Некоторые из этих приложений используют хотя бы одну таблицу базы данных, поэтому нам нужно создать таблицы в базе данных, прежде чем мы сможем их использовать. Для этого выполните следующую команду:
$ python manage.py migrate
...\> py manage.py migrate
Команда 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. Цель состоит в том, чтобы определить вашу модель данных в одном месте и автоматически вывести из нее другие данные.
Это включает в себя миграции — в отличие от Ruby On Rails, например, миграции полностью выведены из файла ваших моделей и представляют собой, по существу, историю, по которой Django может проходить, чтобы обновить схему вашей базы данных для соответствия текущим моделям.
В нашем приложении опросов мы создадим две модели: Question и Choice. Опрос содержит вопрос и дату публикации. Выбор имеет два поля: текст выбора и количество голосов. Каждый выбор связан с опросом.
Эти концепции представлены классами Python. Измените файл polls/models.py, чтобы он выглядел следующим образом:
polls/models.pyfrom 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. Каждая модель имеет ряд переменных класса, каждая из которых представляет поле базы данных в модели.
Каждое поле представлено экземпляром класса Field — например, CharField для символьных полей и DateTimeField для дат и времени. Это сообщает Django, какой тип данных содержит каждое поле.
Имя каждого экземпляра Field (например, question_text или pub_date) — это имя поля в машиночитаемом формате. Вы будете использовать это значение в своем коде Python, а ваша база данных будет использовать его в качестве имени столбца.
Вы можете использовать необязательный первый позиционный аргумент к Field, чтобы указать имя, понятное человеку. Это используется в нескольких интроспективных частях Django и служит документацией. Если это поле не указано, Django будет использовать машиночитаемое имя. В этом примере мы определили только удобочитаемое имя для Question.pub_date. Для всех других полей в этой модели в качестве удобочитаемого имени будет достаточно машиночитаемого имени.
У некоторых классов Field есть обязательные аргументы. CharField, например, требует, чтобы вы предоставили max_length.
Класс Field также может иметь различные необязательные аргументы; в этом случае мы установили значение default для votes в 0.
Наконец, обратите внимание на определение связи с помощью ForeignKey. Это сообщает Django, что каждый Choice связан с одним Question. Django поддерживает все распространенные отношения баз данных: «многие к одному», «многие ко многим» и «один к одному».
Активация моделей
Этот небольшой фрагмент кода модели предоставляет Django много информации. С его помощью Django может:
- Создать схему базы данных (
CREATE TABLE-заявления) для этого приложения. - Создать API доступа к базе данных Python для доступа к объектам
QuestionиChoice.
Но сначала мы должны сообщить нашему проекту, что приложение polls установлено.
Философия
Приложения Django «подключаемы»: Вы можете использовать приложение в нескольких проектах, и вы можете распространять приложения, потому что они не должны быть привязаны к конкретной установке Django.
Для включения приложения в наш проект нам нужно добавить ссылку на его конфигурационный класс в настройку INSTALLED_APPS. Класс PollsConfig находится в файле polls/apps.py, поэтому его путь с точками — 'polls.apps.PollsConfig'. Измените файл mysite/settings.py и добавьте этот путь с точками в настройку INSTALLED_APPS. Он будет выглядеть так:
mysite/settings.pyINSTALLED_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
...\> py 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
...\> py 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;
Обратите внимание на следующее:
- Точный вывод будет зависеть от используемой базы данных. Приведённый выше пример сгенерирован для PostgreSQL.
- Имена таблиц автоматически генерируются путём объединения имени приложения (
polls) и имени модели в нижнем регистре —questionиchoice. (Вы можете переопределить это поведение.) - Первичные ключи (ID) добавляются автоматически. (Вы тоже можете переопределить это.)
- По соглашению, Django добавляет
"_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; это проверяет наличие каких-либо проблем в вашем проекте, не производя миграций и не затрагивая базу данных.
Теперь снова запустите migrate, чтобы создать эти таблицы моделей в вашей базе данных:
$ 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
Команда migrate берёт все миграции, которые не были применены (Django отслеживает, какие были применены, используя специальную таблицу в вашей базе данных под названием django_migrations) и запускает их на вашей базе данных — по сути, синхронизируя изменения, которые вы внесли в свои модели, со схемой в базе данных.
Миграции очень мощный инструмент, позволяющий изменять модели по мере развития проекта, без необходимости удалять базу данных или таблицы и создавать новые — он специализируется на обновлении вашей базы данных в режиме реального времени без потери данных. Мы рассмотрим их более подробно в последующих частях учебника, но пока запомните пошаговую инструкцию по внесению изменений в модель:
- Измените свои модели (в
models.py). - Запустите
python manage.py makemigrations, чтобы создать миграции для этих изменений. - Запустите
python manage.py migrate, чтобы применить эти изменения к базе данных.
Причина, по которой для создания и применения миграций существуют отдельные команды, заключается в том, что вы будете коммитить миграции в систему управления версиями и отправлять их вместе с вашим приложением; они не только облегчают разработку, но и могут быть использованы другими разработчиками и в производственной среде.
Прочитайте документацию по django-admin для получения полной информации о том, что может делать утилита manage.py.
Работа с API
Теперь давайте перейдём в интерактивную оболочку Python и поиграем с бесплатным API, который предоставляет Django. Для запуска Python-оболочки используйте следующую команду:
$ python manage.py shell
...\> py manage.py shell
Мы используем это вместо простого ввода «python», потому что manage.py устанавливает переменную окружения DJANGO_SETTINGS_MODULE, которая сообщает Django путь импорта Python к вашему файлу mysite/settings.py.
После перехода в оболочку изучите API базы данных:
>>> 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=datetime.timezone.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)>]>
Подождите минутку. <Question: Question object (1)> — не очень полезное представление об этом объекте. Давайте исправим это, отредактировав модель Question (в файле polls/models.py), и добавим метод __str__() для Question и Choice.
polls/models.pyfrom 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
Важно добавлять методы __str__() к вашим моделям, не только для вашего удобства при работе с интерактивной оболочкой, но и потому, что представления объектов используются во всём автоматически генерируемом администратором Django.
Давайте также добавим пользовательский метод в эту модель:
polls/models.pyimport 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, для ссылки на стандартный модуль Python datetime и утилиты Django, связанные с часовыми поясами, в django.utils.timezone соответственно. Если вы не знакомы с обработкой часовых поясов в Python, вы можете узнать больше в документации по часовым поясам.
Сохраните эти изменения и запустите новую интерактивную оболочку Python, выполнив 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 (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()
Дополнительную информацию об отношениях моделей см. в доступе к связанным объектам. Более подробную информацию о том, как использовать двойные подчёркивания для выполнения поиска по полям через API, см. в поиске по полям. Полные сведения о базе данных API см. в нашей справочной документации по API базы данных.
Представление административной панели Django
Философия
Генерация административных сайтов для ваших сотрудников или клиентов для добавления, изменения и удаления контента — это кропотливая работа, которая не требует большого творческого подхода. По этой причине Django полностью автоматизирует создание интерфейсов администраторов для моделей.
Django был написан в среде новостного агентства, с очень чётким разделением между «публикаторами контента» и «общедоступным» сайтом. Администраторы сайта используют систему для добавления новостных статей, событий, спортивных результатов и т. д., а этот контент отображается на общедоступном сайте. Django решает проблему создания единого интерфейса для администраторов сайта для редактирования контента.
Администратор не предназначен для использования посетителями сайта. Он для администраторов сайта.
Создание пользователя администратора
Сначала нам нужно создать пользователя, который может войти в административную панель. Запустите следующую команду:
$ python manage.py createsuperuser
...\> py manage.py createsuperuser
Введите желаемое имя пользователя и нажмите Enter.
Username: admin
Затем вам будет предложено ввести желаемый адрес электронной почты:
Email address: admin@example.com
Последний шаг — ввод пароля. Вам будет предложено дважды ввести пароль, второй раз — для подтверждения первого.
Password: ********** Password (again): ********* Superuser created successfully.
Запуск сервера разработки
Административная панель Django активирована по умолчанию. Давайте запустим сервер разработки и изучим её.
Если сервер не запущен, запустите его следующим образом:
$ python manage.py runserver
...\> py manage.py runserver
Теперь откройте веб-браузер и перейдите по адресу «/admin/» на вашем локальном домене — например, http://127.0.0.1:8000/admin/. Вы должны увидеть экран входа в административную панель:
Поскольку перевод включён по умолчанию, если вы установите LANGUAGE_CODE, экран входа в систему будет отображаться на заданном языке (если Django имеет соответствующие переводы).
Вход в админскую часть
Теперь попробуйте войти в систему с учетной записью суперпользователя, которую вы создали на предыдущем шаге. Вы должны увидеть главную страницу Django админского интерфейса:
django.contrib.auth, фреймворком аутентификации, поставляемым Django.Изменение настроек приложения poll в админке
Но где же наше приложение poll? Оно не отображается на главной странице админки.
Осталось сделать только одно: нужно сообщить админке, что объекты Question имеют интерфейс администрирования. Для этого откройте файл polls/admin.py, и измените его, как показано ниже:
polls/admin.pyfrom django.contrib import admin from .models import Question admin.site.register(Question)
Изучение бесплатных возможностей админки
Теперь, после регистрации Question, Django знает, что оно должно отображаться на главной странице админки:
Нажмите «Вопросы». Теперь вы находитесь на странице «список изменений» для вопросов. Эта страница отображает все вопросы в базе данных и позволяет выбрать один для изменения. Вот вопрос «Что нового?», который мы создали ранее:
Нажмите на вопрос «Что нового?» для его редактирования:
Следует обратить внимание на следующие моменты:
- Форма автоматически генерируется из модели
Question. - Разные типы полей модели (
DateTimeField,CharField) соответствуют соответствующему виджету ввода HTML. Каждый тип поля знает, как отображать себя в Django админке. - Каждое поле
DateTimeFieldполучает бесплатные JavaScript-сокращения. Даты получают сокращение «Сегодня» и всплывающее окно календаря, а время — сокращение «Сейчас» и удобное всплывающее окно, которое отображает часто вводимое время.
В нижней части страницы вам предлагаются несколько вариантов:
- Сохранить — сохраняет изменения и возвращает на страницу списка изменений для данного типа объекта.
- Сохра
Если значение «Дата публикации» не соответствует времени, когда вы создали вопрос в Уроке 1, это, вероятно, означает, что вы забыли установить правильное значение для параметра TIME_ZONE. Измените его, перезагрузите страницу и проверьте, что появилось правильное значение.
Измените «Дату публикации», нажав ярлыки «Сегодня» и «Сейчас». Затем нажмите «Сохранить и продолжить редактирование». Затем нажмите «История» в правом верхнем углу. Вы увидите страницу со списком всех изменений, внесённых в этот объект через админку Django, с отметкой времени и именем пользователя, который внес изменения:
Когда вы освоились с API моделей и ознакомились с админским сайтом, прочитайте третью часть этого руководства, чтобы узнать, как добавить больше представлений в наше приложение опросов.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.1/intro/tutorial02/