Создание первого приложения на Django, часть 2

Этот учебник начинается с момента, на которым мы остановились в Tutorial 1 остановился. Здесь мы настроим базу данных, создадим первую модель и быстро познакомимся с автоматически генерируемым сайтом администрирования Django.

Настройка базы данных

Теперь откройте mysite/settings.py. Это обычный модуль Python с переменными уровня модуля, представляющими настройки Django.

По умолчанию в конфигурации используется SQLite. Если вы новичок в базах данных или просто хотите попробовать Django, это самый простой выбор. SQLite включен в Python, поэтому вам не нужно устанавливать что-либо еще для поддержки вашей базы данных. Однако при запуске первого реального проекта вы можете использовать более масштабируемую базу данных, такую как PostgreSQL, чтобы избежать проблем с переключением баз данных в будущем.

Если вы хотите использовать другую базу данных, установите соответствующую bindings и измените следующие ключи в параметре DATABASES 'default', чтобы он соответствовал настройкам соединения с вашей базой данных:

  • ENGINE - 'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', или 'django.db.backends.oracle'. Другие бэкенды также доступны.
  • NAME - Название базы данных. Если вы используете SQLite, то БД будет просто файлом на компьютере. В этом случае NAME должна содержать полный абсолютный путь, включая имя файла. По умолчанию используется os.path.join(BASE_DIR, 'db.sqlite3'), и сохраняет файл в каталоге проекта.

Если вы не используете SQLite в качестве базы данных, необходимо добавить дополнительные настройки, такие как USER, PASSWORD и HOST. Для получения дополнительной информации смотрите справочную документацию для DATABASES.

Для баз данных, отличных от SQLite

Если вы используете базу данных отличную от SQLite, убедитесь, что создали базу данных к этому моменту. Это делается с помощью команды «CREATE DATABASE database_name;» в интерактивной консоли базы данных.

Также убедитесь, что пользователь базы данных, указанный в mysite/settings.py, имеет права на создание базы данных. Это позволяет автоматически создавать test database, которая понадобится в следующем уроке.

Если вы используете SQLite, вам не нужно ничего создавать заранее - файл базы данных будет создан автоматически, когда в этом возникнет необходимость.

Во время редактирования mysite/settings.py, установите TIME_ZONE в свой часовой пояс.

Также обратите внимание на параметр INSTALLED_APPS в верхней части файла. Он содержит имена всех приложений Django, которые активированы в этом экземпляре проекта Django. Приложения могут использоваться в нескольких проектах, и вы можете упаковывать и распространять их для использования другими разработчиками в своих проектах.

По умолчанию INSTALLED_APPS содержит следующие приложения, все из которых поставляются с Django:

Эти приложения включены по умолчанию для удобства для большинства базовых задач.

Некоторые из этих приложений используют хотя бы одну таблицу базы данных, поэтому нам необходимо создать таблицы в базе данных, прежде чем мы сможем их использовать. Для этого выполните следующую команду:

$ python manage.py migrate
...\> py manage.py migrate

Команда migrate ищет настройки в INSTALLED_APPS и создает все необходимые таблицы базы данных в соответствии с настройками базы данных в вашем файле mysite/settings.py и миграциями базы данных, поставляемыми с приложением (мы рассмотрим их позже). Вы увидите сообщение для каждой применимой миграции. Если вам интересно, запустите клиент командной строки для вашей базы данных и введите dt` (PostgreSQL), SHOW TABLES; (MySQL), .schema (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.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. Каждая модель имеет несколько переменных класса, каждая из которых представляет поле базы данных в модели.

Каждое поле представлено экземпляром класса Field - например, CharField для символьных полей и DateTimeField для datetime. Это сообщает Django, какой тип данных содержит каждое поле.

Имя каждого экземпляра Field (например, question_text или pub_date) – это имя поля в машинно-удобном формате. Вы будете использовать это значение в своем коде Python, а ваша база данных будет использовать его в качестве имени столбца.

Вы можете использовать необязательный первый позиционный аргумент для Field для обозначения понятного человеку названия. Это используется в паре интроспективных частей Django, и дублируется как документация. Если это поле не указано, Django будет использовать машиночитаемое имя. В этом примере мы определили только удобочитаемое имя для Question.pub_date. Для всех других полей в этой модели, машиночитаемое имя поля будет достаточно в качестве его удобочитаемого имени.

Некоторые классы Field имеют обязательные аргументы. CharField, например, требует, чтобы вы передали ему аргумент max_length. Это используется не только в схеме базы данных, но и при проверке, как мы скоро увидим.

Field также может иметь различные необязательные аргументы. В этом случае мы установили для default значение voice в 0.

Наконец, обратите внимание, что связь определена с использованием ForeignKey. Это говорит Django, что каждый Choice связан с одним Question. Django поддерживает все общие отношения базы данных: многие-к-одному, многие-ко-многим и один-к-одному.

Активация моделей

Этот небольшой кусочек кода модели дает Django много информации. С его помощью Django может:

  • Создать схему базы данных (оператор CREATE TABLE) для этого приложения.
  • Создать API доступа к базе данных Python для доступа к объектам Question и Choice.

Но сначала нам нужно сообщить нашему проекту, что установлено приложение polls.

Философия

Приложения Django являются «подключаемыми»: вы можете использовать приложение в нескольких проектах и распространять приложения, потому что они не должны быть привязаны к конкретной установке Django.

Чтобы включить приложение в наш проект, нужно добавить ссылку на его класс конфигурации в настройке INSTALLED_APPS. Класс PlayersConfig находится в файле polls/apps.py, поэтому его путь - 'polls.apps.PollsConfig'. Отредактируйте файл mysite/settings.py и добавьте этот путь в параметр INSTALLED_APPS. Это будет выглядеть так:

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',
]

Теперь Django знает, как подключить приложение polls. Давайте запустим другую команду:

$ python manage.py makemigrations polls
...\> py manage.py makemigrations polls

Вы должны увидеть что-то похожее на следующее:

Migrations for 'polls':
  polls/migrations/0001_initial.py:
    - Create model Choice
    - Create model Question
    - Add field question to choice

Запустив makemigrations, вы сообщаете Django, что внесли некоторые изменения в свои модели (в данном случае вы сделали новые) и хотите, чтобы изменения были сохранены как * миграция*.

Миграции - это то, как Django хранит изменения в ваших моделях (и, следовательно, в вашей схеме базы данных) - это просто файлы на диске. Вы можете прочитать миграцию для своей новой модели, если хотите. Это файл polls/migrations/0001_initial.py. Не волнуйтесь, от вас не ожидают, что вы будете читать их каждый раз, когда Django их создает, но они предусматривают для редактирования человеком, если вы хотите вручную что-то изменить.

Есть команда, которая будет запускать миграции для вас и автоматически управлять схемой вашей базы данных - она называется migrate, и мы вскоре к ней подойдем - но сначала давайте посмотрим, какой SQL будет выполняться этой миграцией. Команда sqlmigrate принимает имена миграции и возвращает их SQL:

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

Вы должны увидеть нечто похожее на следующее (мы переформатировали его для удобства чтения):

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;

Обратите внимание на следующее:

  • Точный вывод будет зависеть от базы данных, которую вы используете. Пример выше создан для PostgreSQL.
  • Имена таблиц автоматически генерируются путем объединения названия приложения (polls) и названия модели в нижнем регистре - question и choice. (Вы можете переопределить это поведение.)
  • Первичные ключи (идентификаторы) добавляются автоматически. (Вы можете переопределить это тоже.)
  • По соглашению, Django добавляет "_id" к имени поля внешнего ключа. (Да, вы также можете переопределить это.)
  • Отношение внешнего ключа становится явным с помощью ограничения FOREIGN KEY. Не беспокойтесь о деталях DEFERRABLE; это просто говорит PostgreSQL не применять внешний ключ до конца транзакции.
  • Он адаптирован к используемой вами базе данных, поэтому обрабатываются специфичные для базы данных типы полей, такие как auto_increment (MySQL), serial (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=<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.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

Важно добавить методы __str__() в ваши модели, не только для вашего удобства при работе с интерактивным приглашением, но и потому, что представления объектов используются во всех автоматически сгенерированных страниц админки Django.

Давайте также добавим пользовательский метод к этой модели:

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)

Обратите внимание на добавление 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 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

Введите желаемое имя пользователя и нажмите ввод.

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/. Вы должны увидеть экран входа администратора:

Экран входа в систему администратора Django

Поскольку перевод включен по умолчанию, экран входа в систему может отображаться на вашем родном языке, в зависимости от настроек вашего браузера и наличия у Django перевода для этого языка.

Вход в админку

Теперь попробуйте войти в систему с учетной записью суперпользователя, которую вы создали на предыдущем шаге. Вы должны увидеть главную страницу админки Django:

Индексная страница администратора Django

Вы должны увидеть несколько типов редактируемого контента: группы и пользователи. Они предоставляются django.contrib.auth, инфраструктурой аутентификации, поставляемой Django.

Добавление своего приложения в админку

Но где наше приложение для голосования? Оно не отображается на главной странице админки.

Единственное, что нужно сделать - сообщить админке, что у объектов Question есть интерфейс администратора. Для этого откройте файл polls/admin.py и отредактируйте его следующим образом:

polls/admin.py
from django.contrib import admin

from .models import Question

admin.site.register(Question)

Изучите функциональность админки

Теперь, когда мы зарегистрировали Question, Django знает, что он должен отображаться на главной странице:

Индексная страница администратора Django, теперь с отображением опросов

Нажмите «Questions». Теперь вы находитесь на странице «Список изменений» для вопросов. На этой странице отображаются все вопросы в базе данных, и вы можете выбрать один из них, чтобы изменить его. Там есть вопрос «What’s up?», который мы создали ранее:

Опросы изменяют страницу списка

Нажмите «What’s up?» для его редактирования:

Форма редактирования для объекта вопроса

Что следует отметить здесь:

  • Форма автоматически генерируется из модели Question.
  • Различные типы полей модели (DateTimeField, CharField) соответствуют своему виджету ввода HTML. Каждый тип поля знает, как отобразить себя в админке Django.
  • Каждый DateTimeField получает ярлыки JavaScript. Даты получают ярлык «Сегодня» и всплывающее окно календаря, а время - ярлык «Сейчас» и удобное всплывающее окно, в котором перечислены часто вводимые времена.

В нижней части страницы вы найдете несколько опций:

  • Save - сохраняет изменения и возвращает на страницу списка изменений для этого типа объекта.
  • Сохранить и продолжить редактирование - сохраняет изменения и перезагружает страницу администратора для этого объекта.
  • Сохранить и добавить еще один - сохраняет изменения и загружает новую, пустую форму для этого типа объекта.
  • Удалить - отображает страницу подтверждения удаления.

Если значение «Date published» не соответствует времени, когда вы создали вопрос в Части 1, это, вероятно, означает, что вы забыли установить правильное значение для настройки TIME_ZONE. Измените его, перезагрузите страницу и убедитесь, что отображается правильное значение.

Измените «Дата публикации», нажав на ярлыки «Сегодня» и «Сейчас». Затем нажмите «Сохранить и продолжить редактирование». Затем нажмите «История» в правом верхнем углу. Вы увидите страницу со списком всех изменений, внесенных в этот объект через администратора Django, с отметкой времени и именем пользователя, который внес изменение:

Страница истории для объекта вопроса

Если вы знакомы с API моделей и ознакомились с сайтом администратора, прочитайте часть 3, чтобы узнать, как добавить больше отображений (функций) в наше приложение для опросов.

Вернуться на верх