Создаём своё первое приложение с Django, часть 1

Давайте учиться на примере.

Мы создадим проект, состоящий из простого приложения для голосования.

Проект будет состоять из двух частей:

  • Публичный сайт, который отображает пользователям голосования и позволяет им голосовать.

  • Интерфейс администратора, который позволяет вам добавлять, изменять и удалять голосования.

Мы предполагаем, что вы уже установили Django. Вы можете проверить это, запустив консоль Python и выполнив следующие команды:

python -c "import django; print(django.get_version())"

Если Django установлен, вы должны увидеть текущую версию. Иначе получите ошибку “No module named django”.

Учебник написан для Django 1.5 и Python 2.x. Если версия Django отличается, вы можете обратиться к документации, соответствующей версии Django, или обновить Django до последней версии. Если вы используете Python 3.x, ваш код может отличаться от приведенного в этом учебнике, и вы должны учитывать отличия Python 3.x.

Смотрите Как установить Django, чтобы узнать как удалить старую версию Django и установить новую.

Where to get help:

Если у вас возникают затруденения во время прохождения этого учебника, пожалуйста, оставьте сообщение на django-users или попросите помощи в чате #django on irc.freenode.net.

Создание проекта

Если вы используете Django первый раз, вам придется позаботиться о некоторых первоначальных настройках. А именно, сгенерировать основу проекта Django – настройки проекта, базы данных, приложений и др.

Используя командную строку, перейдите в каталог, где вы хотите хранить код, и выполните следующую команду:

django-admin.py startproject mysite

Это создаст каталог mysite в текущем каталоге. Если нет, смотрите Трудности с запуском django-admin.py.

Примечание

Вы не должны использовать в качестве названия проекта названия компонентов Python или Django. Это означает, что проект не может называться django (что конфликтует с Django) или test (конфликтует со стандартным пакетом Python).

Where should this code live?

Если вы раньше использовали PHP, то, наверное, привыкли размещать код проекта в корневом каталоге сайта на Web-сервере (например, /var/www). C Django вы не должны этого делать. Это плохая идея добавлять код проекта в корень Web-сервера, так как есть риск, что он будет доступен для просмотра. Не делайте этого в целях безопасности.

Разместите код в каталоге вне корневой директории сайта, например /home/mycode.

Давайте посмотрим, что было создано при помощи команды startproject:

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

Doesn’t match what you see?

Структура проекта по умолчанию была недавно изменена. Если вы видите “простую” структуру (без внутреннего каталога mysite/), возможно вы используете версию Django, которая не соответствует этому учебнику. Вам следует читать старую версию учебника или обновить Django.

Рассмотрим эти файлы:

  • Внешний каталог mysite/ – это просто контейнер для вашего проекта. Его название никак не используется Django и вы можете переименовать его во что угодно.

  • manage.py: Скрипт, который позволяет вам взаимодействовать с проектом Django. Подробности о manage.py читайте в разделе django-admin.py and manage.py.

  • Внутренний каталог mysite/ - это пакет Python вашего проекта. Его название – это название пакета Python, которое вы будете использовать для импорта чего-либо из проекта (например, mysite.urls).

  • mysite/__init__.py: Пустой файл, который указывает Python, что текущий каталог является пакетом Python. (Читайте о пакетах в официальной документации Python, если вы новичок в Python.)

  • mysite/settings.py: Настройки/конфигурация проекта. Раздел Django settings расскажет вам все о настройках проекта.

  • mysite/urls.py: Конфигурация URL-ов для вашего проекта Django. Это “содержание” всех Django-сайтов. Вы можете прочитать о конфигурации URL-ов в разделе Менеджер URL-ов.

  • mysite/wsgi.py: Точка входа для WSGI-совместимых веб-серверов. Подробности читайте в разделе Развёртывание с WSGI.

Сервер для разработки

Давайте проверим, что все заработало. Перейдите во внешний каталог mysite, если вы этого еще не сделали, и выполните команду python manage.py runserver. Вы увидите следующий вывод:

Validating models...

0 errors found
March 30, 2016 - 15:50:53
Django version 1.5, using settings 'mysite.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Только что вы запустили сервер для разработки Django, простой Web-сервер написанный на Python. Мы включили его в Django, чтобы вы сразу могли приступить к разработке, без дополнительной настройки боевого веб-сервера – например, Apache – пока вам это действительно не понадобится.

Следует заметить: НИКОГДА НЕ используйте этот сервер на “живом” сайте. Он создан исключительно для разработки. (Мы умеем делать Web-фреймверки, не Web-сервера.)

Теперь, когда сервер запущен, перейдите на страницу http://127.0.0.1:8000/ в браузере. Вы увидите страницу с “Welcome to Django”. Работает!

Changing the port

По умолчанию, команда runserver запускает сервер для разработки на локальном IP используя порт 8000.

Если вы хотите изменить порт, укажите его как аргумент. Например, эта команда запускает сервер используя порт 8080:

python manage.py runserver 8080

Если вы хотите изменить IP, передайте его вместе со значением порта. Чтобы слушать все публичные IP (полезно, если вы хотите показать свою работу на других компьютерах), используйте:

python manage.py runserver 0.0.0.0:8000

Смотрите полное описание команды runserver.

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

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

  • ENGINE – Доступные значения: 'django.db.backends.postgresql_psycopg2', 'django.db.backends.mysql', 'django.db.backends.sqlite3' или 'django.db.backends.oracle'. Так же доступны другие бэкэнды.

  • NAME – Название базы данных. Если вы используете SQLite, база данных будет файлом на вашем компьютере; в таком случае NAME должна содержать полный путь, включая название этого файла. Если файл не существует, он будет автоматически создан при синхронизации базы данных.

    Когда указываете путь, используйте обратный слэш, даже на Windows (например, C:/homes/user/mysite/sqlite3.db).

  • USER – Имя пользователя базы данных (не используйтеся для SQLite).

  • PASSWORD – Пароль к базе данных (не используется для SQLite).

  • HOST – Имя хоста, на котором находится база данных. Оставьте пустую строку (или возможно 127.0.0.1), если сервер базы данных находится на том же хосте (не используется для SQLite). Смотрите детальное описание HOST.

Если вы новичок в базах данных, советуем вам использовать SQLite указав в ENGINE 'django.db.backends.sqlite3' и в NAME расположение файлы базы данных. SQLite включен в Python, так что вам не нужно устанавливать что либо еще.

Примечание

Если вы используете PostgreSQL или MySQL, убедитесь, что вы создали базу данных. Мы можете сделать это, выполнив запрос “CREATE DATABASE database_name;” в консоли базы данных.

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

При редактировании settings.py, добавьте значение вашей временной зоны в TIME_ZONE. Значение по умолчанию Central time zone в U.S. (Chicago).

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

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

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

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

python manage.py syncdb

Команда syncdb анализирует значение INSTALLED_APPS и создает все необходимые таблицы в базе данных, используя настройки базы данных из файла mysite/settings.py. Вы увидите сообщение о создании таблиц и предложение создать суперпользователя. Воспользуйтесь этим предложением.

Если вам интересно, запустите консольный клиент для вашей базы данных и выполните \dt (PostgreSQL), SHOW TABLES; (MySQL), или .schema (SQLite) чтобы увидеть какие таблицы создал Django.

For the minimalists

Как мы уже говорили ранее, приложения по умолчанию удобны для типовых проектов, но не для всех. Если вы не нуждаетесь в некоторых или во всех, закоментируйте или удалите соответствующие строки в INSTALLED_APPS перед запуском syncdb. Команда syncdb создаст таблицы только для приложений в INSTALLED_APPS.

Создание моделей

Теперь, после создания окружения (проекта), мы можем приступить к работе.

Каждое приложение Django состоит из пакета Python, находящегося в Python path, который следует некоторым соглашениям. Django содержит команду, которая создает структуру для нового приложения, что позволяет вам сосредоточиться на написании кода, а не на создании каталогов.

Projects vs. apps

Какая разница между приложением и проектом? Приложение – это Web-приложение, которое предоставляет определенный функционал – например, Web-блог, хранилище каких-то записей или простое приложение для голосования. Проект – это совокупность приложений и конфигурации сайта. Проект может содержать несколько приложений. Приложение может использоваться несколькими проектами.

Ваше приложение может находиться где угодно в путях Python. В этом учебнике, мы будем создавать приложение голосования возле файла manage.py, и оно может быть импортировано как независимый модуль, а не подмодуль mysite.

Создавая приложение, убедитесь, что вы находитесь в том же каталоге, что и файл manage.py, и выполните команду:

python manage.py startapp polls

Эта команда создаст каталог polls:

polls/
    __init__.py
    models.py
    tests.py
    views.py

Эти файлы являются частью приложения голосования.

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

Philosophy

Модель - это основной источник данных. Он содержит набор полей и поведение данных, которые вы храните. Django следует принципу DRY. Смысл в том, чтобы определять модели в одном месте.

В нашем простом приложении голосования, мы создадим две модели: Poll и Choice. Poll содержит вопрос и дату публикации. Choice содержит: текст ответа и количество голосов. Каждый объект Choice связан с объектом Poll.

Эти понятия отображаются простыми классами Python. Отредактируйте файл polls/models.py, чтобы он выглядел таким образом:

from django.db import models

class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Код очень простой. Каждая модель представлена классом, унаследованным от django.db.models.Model. Каждая модель содержит несколько атрибутов, каждый из которых отображает поле в таблице базы данных.

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

Названия каждого экземпляра Field (например, question или pub_date ) это название поля, в “машинном”(machine-friendly) формате. Вы будете использовать эти названия в коде, а база данных будет использовать их как названия колонок.

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

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

Field может принимать различные необязательные аргументы; в нашем примере мы указали default значение для votes` равное 0.

Заметим, что связь между моделями определяется с помощью ForeignKey. Это указывает Django, что каждый Choice связан с одним объектом Poll. Django поддерживает все основные типы связей: многие-к-одному, многие-ко-многим и один-к-одному.

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

Эта небольшая часть кода моделей предоставляет Django большое количество информации, которая позволяет Django:

  • Создать структуру базы данных (CREATE TABLE) для приложения.

  • Создать Python API для доступа к данным объектов Poll и Choice.

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

Philosophy

Приложения Django “подключаемые”: вы можете использовать приложение в нескольких проектах и вы можете распространять приложение, так как они не связаны с конкретным проектом Django.

Отредактируйте файл settings.py и измените настройку INSTALLED_APPS добавив строку 'polls'. В результате получим:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'polls',
)

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

python manage.py sql polls

Вы увидите приблизительно такое (SQL запросы CREATE TABLE для приложения голосования):

BEGIN;
CREATE TABLE "polls_poll" (
    "id" serial NOT NULL PRIMARY KEY,
    "question" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id") DEFERRABLE INITIALLY DEFERRED,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
COMMIT;

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

  • Полученные запросы зависят от базы данных, которую вы используете.

  • Названия таблиц созданы автоматически из названия приложения(polls) и названия модели в нижнем регистре – poll и choice. (Вы можете переопределить это.)

  • Первичные ключи (ID) добавлены автоматически. (Вы можете переопределить и это.)

  • Django добавляет "_id" к названию внешнего ключа. (Да, вы можете переопределить и это.)

  • Внешний ключ определяется явно через REFERENCES.

  • Учитываются особенности базы данных, которую вы используете. Специфические типы данных такие как auto_increment (MySQL), serial (PostgreSQL), или integer primary key (SQLite) будут использоваться автоматически. Тоже касается и экранирование названий, что позволяет использовать в названии кавычки – например, использование одинарных или двойных кавычек. Автор использует PostgreSQL, по этому в примерах используется синтаксис PostgreSQL.

  • Команда sql не выполняет SQL запросы в базе данных - она просто выводит их на экран, чтобы вы могли увидеть какой SQL создает Django. Если вы хотите, можете скопировать и выполнить этот SQL в консоли вашей базы данных. Однако, Django предоставляет более простой способ выполнять SQL в базе данных.

Если вам интересно, запустите следующие команды:

Изучение вывода этих команд может помочь вам понять что на самом деле происходит.

Теперь, выполните команду syncdb снова, чтобы создать таблицы для этих моделей в базе данных:

python manage.py syncdb

Команда syncdb выполняет все запросы из команды sqlall в вашей базе данных для всех приложений, которые находятся INSTALLED_APPS. Она создает все таблицы, загружает начальные данные и создает индексы для всех моделей всех приложений. Команда syncdb может выполняться любое количество раз и будут созданы только таблицы, которые еще не существуют.

О всех возможностях manage.py вы можете прочитать в разделе о django-admin.py.

Поиграемся с API

Теперь, давайте воспользуемся консолью Python и поиграем с API, которое предоставляет Django. Чтобы запустить консоль Python выполните:

python manage.py shell

Мы используем эту команду вместо просто “python”, потому что manage.py устанавливает переменную окружения DJANGO_SETTINGS_MODULE, которая указывает Django путь импорта для файла mysite/settings.py.

Bypassing manage.py

Если вы не хотите использовать manage.py, не проблема. Просто установите переменную окружения DJANGO_SETTINGS_MODULE в mysite.settings и выполните python из каталога, в котором находится файл manage.py (или убедить, что каталог находится в путях Python, и import mysite работает).

Полную информацию обо всем этом смотрите в разделе о django-admin.py.

Теперь, когда вы в консоли, исследуем API базы данных:

>>> from polls.models import Poll, Choice   # Import the model classes we just wrote.

# No polls are in the system yet.
>>> Poll.objects.all()
[]

# Create a new Poll.
# 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
>>> p = Poll(question="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> p.save()

# Now it has an ID. Note that this might say "1L" instead of "1", depending
# on which database you're using. That's no biggie; it just means your
# database backend prefers to return integers as Python long integer
# objects.
>>> p.id
1

# Access database columns via Python attributes.
>>> p.question
"What's new?"
>>> p.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> p.question = "What's up?"
>>> p.save()

# objects.all() displays all the polls in the database.
>>> Poll.objects.all()
[<Poll: Poll object>]

Одну минуту. <Poll: Poll object> – крайне непрактичное отображение объекта. Давайте исправим это отредактировав модель голосования (в файле polls/models.py) и добавив метод __unicode__() для моделей Poll и Choice:

class Poll(models.Model):
    # ...
    def __unicode__(self):
        return self.question

class Choice(models.Model):
    # ...
    def __unicode__(self):
        return self.choice_text

Важно добавить метод __unicode__() не только для красивого отображения в консоли, но так же и потому, что Django использует строковое представление объекта в интерфейсе администратора.

Why __unicode__() and not __str__()?

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

Модели Django содержат метод __str__(), который вызывает метод __unicode__() и конвертирует результат в UTF-8 байтовую строку. Это означает, что unicode(p) вернет строку Unicode, и str(p) вернет обычную строку, символы которой закодированы в UTF-8.

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

Заметим, это стандартные методы Python. Давайте добавим свой метод, просто для демонстрации:

import datetime
from django.utils import timezone
# ...
class Poll(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 Poll, Choice

# Make sure our __unicode__() addition worked.
>>> Poll.objects.all()
[<Poll: What's up?>]

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]

# Get the poll that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Poll.objects.get(pub_date__year=current_year)
<Poll: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Poll.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Poll matching query does not exist. Lookup parameters were {'id': 2}

# 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 Poll.objects.get(id=1).
>>> Poll.objects.get(pk=1)
<Poll: What's up?>

# Make sure our custom method worked.
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_recently()
True

# Give the Poll 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 poll's choices) which can be accessed via the API.
>>> p = Poll.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> p.choice_set.all()
[]

# Create three choices.
>>> p.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Poll objects.
>>> c.poll
<Poll: What's up?>

# And vice versa: Poll objects get access to Choice objects.
>>> p.choice_set.all()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
>>> p.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 poll whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(poll__pub_date__year=current_year)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

# Let's delete one of the choices. Use delete() for that.
>>> c = p.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

Подробности о работе со связанными объектами смотрите в соответствующем разделе. Подробности об использование синтаксиса двойного нижнего подчеркивания читайте в разделе о фильтрах полей. Полная информация об API для работы с базой данных содержится в соответствующем разделе.

Изучив API приступим ко второй части учебника, чтобы настроить интерфейс администратора Django.