Начало – Django: пример создания приложения – часть 1: создание запуск проекта
Содержание
Создание моделей
Теперь, когда ваш проект настроен и работает – вы можете приступать непосредственно к разработке.
Django поставляется с утилитой, которая автоматически генерирует базовую структуру директорий приложений, позволяя вам сфокусироваться на написании кода, вместо того того что бы заниматься созданием каталогов.
Проект vs приложение
Какая разница между проектом (project) и приложением (application)? Приложение – это некоторая доступная из интернета часть, которая выполняет какой-то функционал, например – веблог-система, база данных публичных записей, или простая система опросов. Проект – это совокупность настроек и приложений веб-сайта. Проект может содержать несколько приложений, а приложение – может работать в нескольких проектах.
Ваше приложение может располагаться в любом каталоге, который находится в вашем Python path. В этом руководстве – мы создадим его в том же каталоге, где расположен файл manage.py
, так что его можно будет импортировать как модуль верхнего уровня, а не как подмодуль проекта mysite
.
Что бы создать приложение – убедитесь, что вы находитесь в одном каталоге с файлом manage.py
, и выполните такую команду:
$ ls -l total 8 -rwxrwxr-x 1 setevoy setevoy 249 May 6 16:41 manage.py drwxrwxr-x 2 setevoy setevoy 4096 May 6 18:44 mysite
$ python manage.py startapp polls
Она создаст директорию polls, которая будет выглядеть так:
$ tree -L 2 polls/ polls/ ├── admin.py ├── __init__.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py
Каталог polls
будет является корневой директорией вашего приложения polls
.
Первый шаг в создании приложения с базой данных – это описать ваши модели (models). Модель – это по сути схема базы данных, с некоторым дополнительными данными.
Философия
Модель – это основной источник информации о ваших данных. Она содержит необходимые поля, и описание поведения данных, которые хранятся в базе. Django следует философии DRY Principle. Цель состоит в том, что бы хранить модель ваших данных в одном месте, и автоматически получать данные из неё.
В нашем просто приложении-опросе мы создадим две модели – 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) 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
требуют обязательных аргументов. К примеру, CharField
требует указания max_length
.
Кроме того, Field
может иметь несколько опциональных аргументов. В данном случае – для votes
мы установили значение по умолчанию default=0
.
Обратите внимание, что мы установили связи через ForeignKey
. Это указывает Django, что каждый Choice
относится к одному Question
. Django поддерживает все распространённые связи баз данных: many-to-one, many-to-many и one-to-one.
Активация моделей
Этот небольшой объём кода даёт Djnago много информации. Используя его Django сможет:
- создать схему базы данных (операторы
CREATE TABLE
) для приложения; - создать Python API для доступа к базе данных и объектам
Question
иChoice
в ней.
Но для начала – мы должны указать нашему проекту, что приложение polls
установлено.
Философия
Приложения Django – подключаемые: вы можете использовать одно приложение в нескольких проектах, и вы можете распространять ваши приложения, так как они не привязаны к конкретной установке Django.
Отредактируйте файл mysite/settings.py
, и измените список INSTALLED_APPS
, добавив в него 'polls'
, что бы получить такой результат:
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls', )
Теперь – Django будет знать, что требуется включить приложение polls
. Давайте выполним ещё одну команду:
$ python manage.py makemigrations polls Migrations for 'polls': 0001_initial.py: - Create model Choice - Create model Question - Add field question to choice
Выполнив makemigrations
– вы указываете Django, что вы сделали какие-то изменения в ваших моделях (в данном случае – вы добавили новые), и вы хотите, что бы они были сохранены как миграции (migrations).
Migrations – это то, как Django сохраняет изменения в ваших моделях (и, соответственно, в базе данных), и являются просто файлами на диске. Вы можете прсомотреть файл миграции, который расположен в polls/migrations
и называется 0001_initial.py
– но вам не требуется делать это каждый раз.
Так же есть команда, которая запускает миграцию и настраивает вашу базу данных, она называется migrate
, и мы её очень скоро используем. А пока – давайте посмотрим, какие именно SQL-запросы будут выполнены при миграции. Команда sqlmigrate
принимает аргументом имя файла миграции, и возвращает её в виде SQL:
$ python manage.py sqlmigrate polls 0001 BEGIN; CREATE TABLE `polls_choice` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `votes` integer NOT NULL); CREATE TABLE `polls_question` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_text` varchar(200) NOT NULL, `pub_date` datetime NOT NULL); 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`); COMMIT;
Обратите внимание на следующее:
- вывод может отличаться в зависимости от типа базы данных, тут показан вывод для MySQL/MariaDB;
- имена таблиц генерируются из комбинации имени приложения (
polls
) и имени модели в нижнем регистре –question
иchoice
(это можно изменить); - Primary keys (IDs) добавляются автоматически (и это можно изменить);
- по умолчанию – Django добавляет “
_id
” к полюforeign key
(и это настраивается тоже); foreign key
отношения создаются явно с помощьюFOREIGN KEY
;- команда
sqlmigrate
не выполняет никаких действий в самой базе данных – она просто отображает то, что должно быть выполнено, и полезно для проверки перед внесением изменений в базу данных.
Вы так же можете выполните команду python manage.py check
, которая проверит потенциальные проблемы без создания файла миграции и без изменений в базе данных:
$ python manage.py check System check identified no issues (0 silenced).
Теперь – давайте выполним migrate
, что бы создать таблицы ваших моделей в базе данных:
$ python manage.py migrate Operations to perform: Synchronize unmigrated apps: staticfiles, messages Apply all migrations: admin, contenttypes, polls, auth, sessions Synchronizing apps without migrations: Creating tables... Running deferred SQL... Installing custom SQL... 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 documentation
для получения полной информации об утилите manage.py
.
Работа с API
Теперь – давайте переключимся в консоль Python и поработаем с Django API. Что бы вызывать консоль – выполните:
$ python manage.py shell
Мы используем эту команду вместо обычного вызова команды python
, так как manage.py
установит переменную окружения DJANGO_SETTINGS_MODULE
, которая укажет Django путь к импорту файла mysite/settings.py
и подключит все находящиеся в нём настройки.
Теперь, находясь в консоли, вы можете поработать с database API:
# импортируем классы моделей, которые мы только что написали >>> from polls.models import Question, Choice >>> # пока никаких вопросов в голосовании нет >>> Question.objects.all() [] >>> # создаём новый вопрос >>> # поддержка временных зон включена по умолчанию >>> # поэтому 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()) >>> # сохраняем новый объект в базу данных >>> q.save() >>> # теперь у него есть ID; вы можете заметить, что он будет 1L, в не 1 >>> # это зависит от типа сервера баз данных >>> q.id 1L >>> # получаем доступ к полям через атрибуты Python >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2015, 5, 7, 8, 9, 10, 364248, tzinfo=<UTC>) >>> # изменим значения атрибутов, и вызываем save() >>> q.question_text = "What's up?" >>> q.save() >>> # objects.all() отображает все вопросы в базе >>> Question.objects.all() [<Question: Question object>]
Минуту… <Question: Question object>
– уродливое и бесполезное отображение данных из объекта. Давайте исправим это, отредактировав модель Question
в файле polls/models.py
, и добавив метод __unicode__()
(__str__()
в Python 3) для моделей Question
и Choice
:
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __unicode__(self): # __str__ в Python 3 return self.question_text class Choice(models.Model): question = models.ForeignKey(Question) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __unicode__(self): # __str__ в Python 3 return self.choice_text
def __unicode__ тут – это обычный метод Python.
Давайте добавим ещё один, просто для примера:
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 из стандартной библиотеки Python, и timezone из django.utils.
Сохраните изменения, и запустите python manage.py shell
ещё раз:
>>> from polls.models import Question, Choice >>> Question.objects.all() [<Question: What's up?>] >>> # Django предоставляет так же API для поиска по базе >>> # основанный на ключевых словах >>> Question.objects.filter(id=1) [<Question: What's up?>] >>> Question.objects.filter(question_text__startswith='What') [<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) ... DoesNotExist: Question matching query does not exist. >>> # поиск по первичному ключу очень распространённое явление >>> # поэтому Djnago предоставляет сокращение для него >>> # следующий вызов аналогичен 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 к набору имеющихся choice >>> # и возвращает новый объект Choice >>> # Django создаёт набор для хранения ForeignKey связей с "другой стороной" >>> # (например - варианты ответа для вопроса) >>> # которые могут быть доступны через API >>> q = Question.objects.get(pk=1) >>> # пока никаких вариантов ответов нет >>> q.choice_set.all() [] >>> # создаём три ответа (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 имеют API доступ к связанным с ними объектам Questions >>> c.question <Question: What's up?> >>> # и наоборот - объекты Questions имеют доступ к объектам Choice >>> q.choice_set.all() [<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) [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] >>> # удалим один из вариантов ответа с помощью delete() >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()
Для получения детальной информации по связях моделей – смотрите Accessing related objects. Информация об использовании двойных подчёркиваний для выполнения поиска с помощью API – доступна на странице Field lookups. Полное описание работы с API – есть на странице Database API reference.
Продолжение – Django: пример создания приложения – часть 3: панель управления