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

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

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

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

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

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

Мы предполагаем что вы уже установили Django.Вы можете проверить это запустив консоль Python и выполнив import django. Если команда выполнилась без ошибок, Django установлен.

Where to get help:

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

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

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

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

django-admin.py startproject mysite

Это создаст каталог mysite в текущем каталоге.

Script name may differ in distribution packages

Если вы установили Django используя пакетный менеджер дистрибутива Linux (например, apt-get или yum), django-admin.py может называться django-admin и вам следует опустить .py из команд.

Mac OS X permissions

Если вы используете Mac OS X, вы можете получить ошибку “permission denied” при выполнении django-admin.py startproject. Это вызвано тем, что на Unix-системах, таких как OS X, файл должен быть отмечен как “исполняемый” чтобы использоваться как программа. Для этого откройте Terminal.app и перейдите (используя команду cd) в каталог, где установлена django-admin.py, и выполните команду sudo chmod +x django-admin.py.

Примечание

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

django-admin.py должен находиться в системном пути, если вы установили Django используя python setup.py. Если его там нет, вы можете поискать в site-packages/django/bin, где site-packages каталог в инсталяции Python. Рассмотрите возможность сделать символическую ссылку на django-admin.py из каталога, который точно есть в системном пути, например /usr/local/bin.

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, которое вы будете использовать для импорта чего-либо из проекта (например, import mysite.settings).

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

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

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

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

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

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

Validating models...
0 errors found.

Django version 1.4, 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 – хост на котором находится база данных. Оставьте пустым, если база данных находится на одном сервере с проектом (не используется для SQLite).

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

Примечание

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

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

При редактировании settings.py, добавьте значение вашей временной зоны в TIME_ZONE. Значение по-умолчанию - временной пояс Чикаго.

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

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

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

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

python manage.py syncdb

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

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

For the minimalists

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

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

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

Каждое приложение Django состоит из пакета Python, который находиться в путях Python и следует некоторым правилам. 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. Смысл в том, что бы определять модели в одном месте.

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

Эти понятия отображаются простыми классами 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 = models.CharField(max_length=200)
    votes = models.IntegerField()

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

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

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

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

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

Заметим, что связь между моделями определяется с помощью 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" 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 путь импорта для файла 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

Важно добавить метод __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 whose year is 2012.
>>> Poll.objects.get(pub_date__year=2012)
<Poll: What's up?>

>>> Poll.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Poll 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 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='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice='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 2012.
>>> Choice.objects.filter(poll__pub_date__year=2012)
[<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__startswith='Just hacking')
>>> c.delete()

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

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