Написание вашего первого приложения 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. Question имеет вопрос и дату публикации. Choice имеет два поля: текст выбора и количество голосов. Каждый Choice связан с Question.
Эти концепции представлены классами 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. По умолчанию команда shell автоматически импортирует модели из вашей настройки INSTALLED_APPS.
После того, как вы находитесь в оболочке, изучите API базы данных:
# 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 ещё раз:
# 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 Admin
Философия
Генерация страниц администрирования для ваших сотрудников или клиентов, позволяющих добавлять, изменять и удалять контент — утомительная работа, которая не требует много творчества. По этой причине 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 admin:
Вы увидите несколько типов редактируемого контента: группы и пользователи. Они предоставляются django.contrib.auth, фреймворком аутентификации, поставляемым Django.
Сделать приложение опросов доступным для редактирования в админ-панели
Но где наше приложение опросов? Оно не отображается на главной странице администрирования.
Осталось совсем немного: нужно сообщить администрированию, что 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 admin, с отметкой времени и именем пользователя, внесшего изменение:
Когда вы будете чувствовать себя уверенно с API моделей и ознакомитесь со страницей администрирования, прочитайте часть 3 этого руководства, чтобы узнать, как добавить больше представлений в наше приложение опросов.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.2/intro/tutorial02/