Все, что нужно знать о Django.
Как организована документация¶
У Джанго много документации. Общий обзор того, как она организована, поможет вам узнать, где искать необходимое:
- Руководства. Пройдите серию шагов по созданию веб-приложения. Начните с них, если вы новичок в Django или разработке веб-приложений. Также посмотрите «Первые шаги» ниже.
- Руководства объясняют ключевые темы и концепции на достаточно высоком уровне и предоставляют полезную справочную информацию и пояснения.
- Справочные руководства содержат техническую информацию по API и другим аспектам работы Django. Они описывают, как всё работает и как это использовать, но предполагают, что у вас есть базовое понимание ключевых понятий.
- Практические руководства являются рецептами. Они проведут вас через шаги, связанные с решением ключевых проблем и вариантов использования Django. Они более продвинуты, чем учебные пособия, и предполагают, что вы знаете как Django работает.
Производительность и оптимизация¶
Существует множество методик и инструментов, которые могут помочь сделать ваш код более эффективным — более быстрым и использующим меньше системных ресурсов.
- Обзор производительности и оптимизации
Географический фреймворк¶
GeoDjango намеревается стать географическим веб-фреймворком мирового класса. Его цель — максимально упростить создание веб-приложений ГИС и использование возможностей пространственных данных.
Последнее обновление: 29.04.2023
Содержание руководства по созданию веб-приложений на языке Python с помощью фреймворка Django.
-
Глава 1. Введение в Django
-
Что такое Django
-
Установка и настройка Django
-
Создание первого проекта
-
Создание первого приложения
-
-
Глава 2. Представления и маршрутизация
-
Обработка запроса
-
Определение маршрутов и функции path и re_path
-
Получение данных запроса. HttpRequest
-
HttpResponse и отправка ответа
-
Параметры представлений
-
Вложенные маршруты и функция include
-
Параметры строки запроса
-
Переадресация и отправка статусных кодов
-
Отправка json
-
Отправка и получение кук
-
-
Глава 3. Шаблоны
-
Создание и использование шаблонов
-
Передача данных в шаблоны
-
Встроенные теги шаблонов
-
Фильтры шаблонов
-
Статические файлы
-
TemplateView
-
Конфигурация шаблонов
-
Расширение шаблонов и фильтр extends
-
Вложенные шаблоны и фильтр include
-
-
Глава 4. Работа с формами
-
Отправка форм
-
Определение форм Django
-
Типы полей формы
-
Настройка формы и ее полей
-
Валидация данных
-
Детальная настройка полей формы
-
Стилизация полей форм
-
-
Глава 5. Модели
-
Подключение к базе данных
-
Создание моделей
-
Типы полей моделей
-
QuerySet API
-
Создание и получение объектов модели
-
Редактирование и удаление объектов модели
-
Фильтрация
-
values и values_list и сортировка
-
Операции с множествами
-
Получение отдельных объектов и проверка их наличия
-
Агрегатные операции
-
Выполнение SQL-выражений
-
CRUD. Все базовые операции с моделями в веб-приложении
-
Отношение один ко многим (One to Many)
-
Практический пример связи один ко многим
-
Отношение многие ко многим (Many to Many)
-
Отношение многие ко многим (Many to Many)
-
Отношение один к одному (One to one)
-
Время на прочтение
14 мин
Количество просмотров 288K
Представляю вам перевод статей о Django с сайта effectivedjango.com. Наткнулся я на этот сайт во время изучения данного фреймворка. Информация размещенная на этом ресурсе показалась мне полезной, но так как нигде не нашел перевода на русский, решил сделать сие доброе дело сам. Этот цикл статей, как мне думается, будет полезен веб-разработчикам, которые делают только первые шаги в изучении Django.
Оглавление
- Введение
- Эффективный Django. Руководство
- Глава 1. Приступая к работе
- Глава 2. Используем модель
- Глава 3. Пишем представление
- Глава 4. Используем статические ресурсы
- Глава 5. Дополнительные базовые представления
- Глава 6. Основы форм
- Глава 7. Связанные модели
- Глава 8. Обработка аутентификации и авторизации
- Тестирование в Django
- Понимание Middleware’ов
- Базы данных и модели
- Представления-классы (CBV)
- Формы
Введение ⇧
Django — это популярный, мощный фреймворк на языке Python. Он имеет множество «батареек«, и позволяет сразу начать разработку. Однако вся эта мощь означает, что вы можете написать низкопробный код, который будет казаться рабочим. Так что же подразумевается под Эффективным Django? Под Эффективным Django будем понимать использование Django таким образом, чтобы написанный код был связным, тестируемым и масштабируемым. Что же каждое из этих слов значит?
«Связный» код — это код, который сосредоточен на выполнении одной вещи, только одной единственной вещи. Это значит, что когда вы пишете функцию или метод — написанный вами код должен делать что-то одно и делать это хорошо.
Это непосредственно относится к написанию тестируемого кода: код, который делает много вещей, достаточно часто является чересчур сложным для тестирования. Когда я ловлю себя на мысли: «Хорошо, этот кусок кода слишком сложен, чтобы писать для него тесты — это просто не стоит потраченных усилий» — вот сигнал к тому, чтобы вернутся назад и сосредоточиться на упрощении. Тестируемый код — такой код, который позволяет просто писать для него тесты; код, в котором легко найти проблемы.
И наконец, мы хотим писать масштабируемый код. Это означает не просто масштабировать его в терминах исполнения, но так же увеличивать в терминах команды и командного понимания. Хорошо протестированные приложения проще для понимания другими (и проще для изменения ими), что подразумевает большую возможность улучшить ваше приложение, путем добавления новых инженеров.
Моя цель — убедить вас в важности этих принципов, и предоставить примеры того, как следуя им, построить более стойкое Django-приложение. Я собираюсь последовательно пройти через процесс построения приложения для управления контактами, рассказывая про решения и стратегию тестирования, которые я использую.
Эти документы являются сочетанием заметок и примеров подготовленных для PyCon 2012, PyOhio 2012, и PyCon 2013, а также для web-разработки Eventbrite. Я все еще работаю над объединением их в один документ, но надеюсь вы найдете их полезными.
Примеры кода для этого руководства доступны на github’е. Отзывы, предложения и вопросы можете присылать на nathan@yergler.net.
Этот документ доступен на сайте, а также в форматах PDF и EPub.
Видео этого руководства с PyCon можно посмотреть на YouTube.
Глава 1. Приступая к работе ⇧
1.1. Ваша среда разработки
Когда ведется разговор о вашей среде разработки, существует три важных факта, которые необходимо иметь в виду: изоляция, предопределенность и сходство. Каждый из них важен и все они взаимодействуют друг с другом согласованно.
Изоляция
означает, что вы не сможете случайно воспользоватся инструментами или пакетами установленными вне вашего окружения. Это особенно важно, когда подобное происходит с чем-то, похожим на пакеты Python с расширениями написанными на C: если вы используете что-то установленное на системном уровне и не знаете об этом, то при развертывании или распространении своего кода вы можете обнаружить, что он работает не так как предполагалось. Инструменты наподобие virtualenv могут помочь создать нечто похожее на изолированную среду.
Ваша среда
предопределена
, если вы уверены в том, на какую версию ваших зависимостей вы полагаетесь и сможете ли вы наверняка воспроизвести системное окружение.
И на конец,
сходство
с производственной средой или средой сервера разработки означает, что везде установлена одна и та же операционная система (возможно даже одинаковый выпуск) и вы используете одинаковые инструменты как для конфигурирования вашей среды разработки, так и для конфигурирования вашей производственной среды. Это отнюдь не необходимость, но если вы строите большое и сложное программное обеспечение — сходство будет полезно для уверенности в том, что все проблемы, которые вы можете увидеть на «боевом» сервере, воспроизводимы в той среде, где вы ведете разработку. К тому же
сходство
ограничивает область исследования вашего кода.
1.1.1. Изоляция
- Мы хотим избежать использования неизвестных зависимостей или неизвестных версий.
- virtualenv предоставляет простой путь для работы над проектом, без использования системных site-packages.
1.1.2. Предопределенность
- Предопределенность означает управление зависимостями.
- Выберете один из инструментов и используйте как при разработке, так на «боевом» сервере:
- pip и специальные файлы зависимостей;
- buildout;
- install-requires в
setup.py
.
- Определите точные версии зависимостей.
Вы можете точно определить версии используя либо версию пакета на PyPI, либо же определенную ревизию (SHA в git, номер ревизии в Subversion и т. д.). Это гарантирует вам возможность получить в точности те же версии пакетов, которые вы используете при тестировании.
1.1.3. Сходство
- Работа в среде, похожей на ту, где вы будете разворачивать ваше приложение и пытаться обнаружить проблемы.
- Если вы разрабатываете что-то, требующее дополнительных сервисов — сходство становится еще более важным.
- Vagrant — это инструмент для управления виртуальными машинами, позволяющий вам легко создавать окружение отделенное от вашего повседневного окружения.
1.2. Настройка вашего окружения
1.2.1. Создание чистого рабочего пространства
Примечание переводчика:
Для начала создадим каталог (tutorial
), в котором будем работать:~$ mkdir tutorial ~$ cd tutorial ~/tutorial$ mkdir venv project
В каталоге
venv
будет находится наше виртуальное окружение, а в каталогеproject
— Django-проект
~/tutorial$ virtualenv --prompt="(venv:tutorial)" ./venv/
New python executable in ./venv/bin/python
Installing setuptools............done.
Installing pip...............done.
~/tutorial$ source ./venv/bin/activate
(venv:tutorial)~/tutorial$
1.2.2. Создание файла зависимостей
Создайте файл requirements.txt
в директории tutorial
с единственной строкой (зависимостью) в нем:
Django==1.6.7
Примечание переводчика:
В случае, если вы хотите использовать последнюю версию Django (1.7 — на момент написания перевода) — вместо строкиDjango==1.6.7
оставьте простоDjango
— pip установит последнюю доступную версию.
1.2.3. Установка зависимостей
А теперь мы можем использовать pip для установки зависимостей:
(venv:tutorial)~/tutorial$ pip install -U -r requirements.txt
Downloadping/unpacking Django==1.6.7 (from -r requirements.txt (line 1))
Downloading Django-1.6.7.tar.gz (6.6MB): 6.6MB downloaded
Running setup.py egg_info for package Django
warning: no previously-included files matching ’__pycache__’ found under directory ’*’
warning: no previously-included files matching ’*.py[co]’ found under directory ’*’
Installing collected packages: Django
Running setup.py install for Django
changing mode of build/scripts-2.7/django-admin.py from 644 to 755
warning: no previously-included files matching ’__pycache__’ found under directory ’*’
warning: no previously-included files matching ’*.py[co]’ found under directory ’*’
changing mode of /home/nathan/p/edt/bin/django-admin.py to 755
Successfully installed Django
Cleaning up...
1.3. Начало проекта Django
Когда здание находится в процессе постройки, строительные леса часто используются для поддержания структуры до того как строительство будет завершено. Строительные леса могут быть временными или они могут служить частью фундамента здания, но несмотря на это, они представляют некоторую поддержку когда вы только начинаете работу.
Django, как и многие web-фреймворки, представляет скаффолдинг для вашей разработки. Это происходит при помощи принятия решений и предоставления отправной точки для вашего кода, что позволяет вам сосредоточится на проблеме, которую вы пытаетесь решить, а не на том, как разобрать HTTP-запрос. Django предоставляет скаффолдинг как для работы с HTTP, так и для работы с файловой системой.
HTTP-скаффолдинг управляет, например, преобразованием HTTP-запроса в объект языка Python, а также предоставляет инструменты для более простого создания серверных ответов. Скаффолдинг файловой системы иной: это набор соглашений по организации вашего кода. Эти соглашения облегчают добавление новых инженеров в проект, так как инженеры (гипотетически) уже понимают как организован код. В терминах Django, проект — это конечный продукт, и он объединяет внутри себя одно или несколько приложений. В Django 1.4 было изменено то, как проекты и приложения размещаются на диске, что облегчило разъединение и повторное использование приложений в разных проектах.
1.3.1. Создание проекта
Django устанавливает в систему скрипт django-admin.py
для обработки задач скаффолдинга. Для создания файлов проекта используется задача startproject
. Мы определим имя проекта и имя директории, в которой хотим разместить проект. Так как, мы уже находимся в изолированной среде, можно просто написать:
Примечание переводчика:
Перейдем директорию~/tutorial/project/
и в дальнейшем будем работать только из этой директории (под$
далее будем подразумевать~/tutorial/project/$
):(venv:tutorial)~/tutorial/$ cd project
(venv:tutorial)$ django-admin.py startproject addressbook .
Созданный проект имеет следующую структуру
manage.py
./addressbook
__init__.py
settings.py
urls.py
wsgi.py
1.3.2. Скаффолдинг проекта
manage.py
— является ссылкой на скриптdjango-admin
, но с уже предустановленными переменными окружения, указывающими на ваш проект, как для чтения настроек оттуда, так и для управления им при необходимости;settings.py
— здесь находятся настройки вашего проекта. Файл уже содержит несколько разумных настроек, но база данных не указана;urls.py
— содержит URL’ы для маппирования (отображения) представлений: мы вскоре (в дальнейших главах) поговорим об этом подробнее;wsgi.py
— это WSGI обёртка для вашего приложения. Этот файл используется сервером разработки Django и возможно другими контейнерами, такими какmod_wsgi
,uwsgi
и др. на «боевом» сервере.
1.3.3. Создание приложения
(venv:tutorial)$ python ./manage.py startapp contacts
Созданное приложение имеет следующую структуру:
./contacts
__init__.py
models.py
tests.py
views.py
- Начиная с Django 1.4, приложения размещаются внутри пакета с проектом. Это замечательное улучшение, особенно когда приходит время разворачивать проект на «боевом» сервере;
models.py
будет содержать Django ORM-модели для вашего приложения;views.py
будет содержать код представлений;tests.py
будет содержать написанные вами модульные и интеграционные тесты.- Django 1.7:
admin.py
будет содержать модель для административного интерфейса. - Django 1.7:
migrations/
содержит файлы миграций
Примечание переводчика:
На текущий момент наша директория~/tutorial/
содержит файл зависимостей (requirements.txt
), директорию с виртуальным окружением (venv/
), один проект (project/addressbook
), одно приложение (project/contacts
) и имеет следующее содержание:~/tutorial/ requirements.txt venv/ ... project/ manage.py addressbook/ __init__.py settings.py urls.py wsgi.py contacts/ __init__.py models.py tests.py views.py
Глава 2. Используем модель ⇧
2.1. Конфигурирование базы данных
Django поддерживает «из коробки» MySQL, PostgreSQL, SQLite3 и Oracle. SQLite3 входит в состав Python начиная с версии 2.5, так что мы будем использовать его в нашем проекте (для простоты). Если вы хотите, к примеру, использовать MySQL, то нужно добавить mysql-python в ваш requirements.txt
.
Для того чтобы в качестве базы данных использовать SQLite, отредактируйте определение DATABASES
в файле addressbook/settings.py
. Файл settings.py содержит настройки Django для нашего проекта. В нем есть несколько настроек, которые вы обязаны указать — например DATABASES
— а так же другие, необязательные, настройки. Django устанавливает некоторые настройки сам, когда генерирует проект. Документация содержит полный список настроек. К тому же вы можете добавить свои собственные настройки, если это необходимо.
Для использования SQLite нам нужно указать движок (ENGINE
) и имя базы (NAME
). SQLite интерпертирует имя базы как имя файла для базы данных:
DATABASES = {
'defaults': {
'ENGINE': 'django.db.backends.sqlite3,' # ’postgresql_psycopg2’, ’mysql’, ’sqlite3’ or ’oracle'.
'NAME': os.path.join(BASE_DIR, 'address.db'),
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
Заметьте, что движок базы данных указан строкой, а не прямой ссылкой на объект Python. Это сделано по той причине, что файл настроек должен быть легко импортирован не вызывая сторонних эффектов. Вы должны избегать добавления вызовов import в этот файл.
Вам редко придется непосредственно импортировать файл настроек: Django импортирует его за вас, и делает настройки доступными как django.conf.settings
. Вы, как правило, импортируете настройки из django.conf
:
from django.conf import settings
2.2. Создание модели
Модели Django отображают (грубо говоря) таблицы базы данных, и предоставляют место для инкапсулирования бизнес-логики. Все модели являются наследниками базового класса Model и содержат поля определений. Давайте создадим простую модель Contacts
для нашего приложения в файле contacts/models.py
:
from django.db import models
class Contact(models.Model):
first_name = models.CharField(
max_length=255,
)
last_name = models.CharField(
max_length=255,
)
email = models.EmailField()
def __str__(self):
return ' '.join([
self.first_name,
self.last_name,
])
Django предоставляет набор полей для отображения типов данных и различных правил валидации. К примеру, EmailField
, который мы использовали, является отображением на колонку с типом CharField
, но добавляет валидацию данных.
После того, как вы создали модель, необходимо дополнить вашу базу данных новыми таблицами. Команда Django syncdb
смотрит установленные модели и создает (если нужно) таблицы для них:
Примечание переводчика:
Django предложит создать суперпользователя для андминки, которая включена в этой версии по умолчанию. Воспользуйтесь его предложением.
Примечание переводчика:
С версии Django 1.7 во фреймворк добавлена нативная поддержка миграций и командаsyncdb
объявлена устаревшей. Так что будьте так любезны, воспользуйтесь командойmigrate
вместоsyncdb
.
(venv:tutorial)$ python ./manage.py syncdb
Creating tables ...
Creating table django_admin_log
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'bjakushka'):
Email address:
Password:
Password (again):
Superuser created successfully.
Installing custom SQL ...
installing indexes ...
Installed 0 object(s) from 0 fixture(s)
(venv:tutorial)$
Примечание переводчика:
Если вы используете Django версии 1.7 и выше — вывод будет следующий:(venv:tutorial)$ python ./manage.py migrate Opperation to perform: Apply all migrations: admin, contenttypes, auth, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying sessions.0001_initial... OK (venv:tutorial)$
Однако нашей таблицы с контактами нигде не видно. Причина этого состоит в том, что нам нужно еще указать проекту использовать приложение.
Настройка INSTALLED_APPS
содержит список приложений, используемых в проекте. Этот список содержит в себе строки, которые отображают пакеты Python. Django будет импортировать каждый из указанных пакетов, а потом смотреть модуль models
. Давайте добавим наше приложение contacts
в настройки проекта (addressbook/settings.py
):
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'contacts',
)
После этого запустите syncdb
снова:
Примечание переводчика:
Для Django версии 1.7 и выше вам нужно будет запустить сначала командуmakemigrations
— для создания миграций на основе изменений в моделях, а после этого выполнить командуmigrate
— для того чтобы применить созданные миграции.
(venv:tutorial)$ python ./manage.py syncdb
Creating tables ...
Creating table contacts_contact
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
(venv:tutorial)$
Примечание переводчика:
Вывод для Django 1.7 и выше:(venv:tutorial)$ python ./manage.py makemigrations Migrations for 'contacts': 0001_initial.py: - Create model Contact (venv:tutorial)$ python ./manage.py migrate Opperation to perform: Apply all migrations: admin, contenttypes, sessions, auth, contacts Running migrations: Applying contacts.0001_initial... OK (venv:tutorial)$
Заметьте, что Django создает таблицу с именем contacts_contact
: по умолчанию Dj ango дает таблицам имена используя комбинацию имени приложения и имени модели. Вы можете изменить это с помощью опций модели Meta.
2.3. Взаимодействие с моделью
Теперь, когда модель синхронизирована с базой данных мы можем взаимодействовать с нею используя интерактивную оболочку:
(venv:tutorial)$ python ./manage.py shell
Python 2.7.3 (default, Mar 14 2014, 11:57:14)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from contacts.models import Contact
>>> Contact.objects.all()
[]
>>> Contact.objects.create(first_name='Nathan', last_name='Yergler')
<Contact: Nathan Yergler>
>>> Contact.objects.all()
[<Contact: Nathan Yergler>]
>>> nathan = Contact.objects.get(first_name='Nathan')
>>> nathan
<Contact: Nathan Yergler>
>>> print nathan
Nathan Yergler
>>> nathan.id
1
Здесь использовалось несколько новых штук. Во-первых, команда manage.py shell
запускает для нас интерактивную оболочку Python’а с правильно установленными путями для Django. Если вы попробуете запустить интерпретатор Python и просто импортировать ваше приложения, будет выброшено исключение, потому что Django не знает, какие настройки использовать, и не может отобразить экземпляры модели на базу данных.
Во-вторых, здесь использовалось свойство objects
нашей модели. Это менеджер модели. Так, если один экземпляр модели является аналогией для строки в базе, то менеджер модели — аналогией для таблицы. По умолчанию менеджер модели предоставляет функциональность запросов и может быть настроен. Когда мы вызываем all()
, filter()
или сам менеджер, возвращается объект QuerySet
. QuerySet
является итерируемым объектом и загружает данные из базы по необходимости.
И последнее — выше использовалось поле с именем id
, которое мы не определяли в нашей модели. Django добавляет это поле как первичный ключ для модели, но только в том случае если вы сами не определили какое поле будет первичным ключом.
2.4. Написание тестов
В нашей модели определен один метод, __str__
, так что настало время писать тесты. Метод __str__
будет использоваться всего лишь в нескольких местах, и, вполне возможно, полностью будет показан конечному пользователю. Для этого метода стоит написать тест, пока мы понимаем как он работает. Django создал файл tests.py
когда создавал приложение, так что мы добавим первый тест в этот файл, приложения contacts
.
from django.test import TestCase
from contacts.models import Contact
class ContactTests(TestCase):
"""Contact model tests."""
def test_str(self):
contact = Contact(first_name='John', last_name='Smith')
self.assertEquals(
str(contact),
'John Smith',
)
Вы можете запустить тесты для вашего приложения используя команду manage.py test
:
(venv:tutorial)$ python ./manage.py test
Если вы запустите это, то увидите что выполнилось около 420 тестов. Это удивляет, так как мы написали только один. Произошло это потому, что по умолчанию Django запускает тесты для всех установленных приложений. Когда вы добавляли приложение contacts
в наш проект, то могли увидеть, что там по умолчанию были добавлены несколько встроенных приложений Django. Дополнительные 419 тестов были взяты оттуда.
Примечание переводчика:
В нашем случае (при использовании версии Django 1.6.7) предыдущий абзац несколько устарел: запустится только один тест — тот который мы создали. Вывод команды будет такой как указано ниже.
Если же вы захотите запустить тесты для определенного приложения — укажите имя приложения в команде:
(venv:tutorial)$ python manage.py test contacts
Creating test database for alias ’default’...
.
----------------------------------------------------------------------
Ran 1 tests in 0.001s
OK
Destroying test database for alias ’default’...
(venv:tutorial)$
Еще одна интересная вещь на заметку, прежде чем двигаться дальше — первая и последняя строка вывода: Creating test database
и Destroying test database
. Некоторым тестам необходим доступ к базе данных, и поскольку мы не хотим мешать тестовые данные с «реальными» (по разным причинам, не последней из которых является предопределенность), Django услужливо создает тестовую базу для нас, прежде чем запускать тесты. По существу, создается новая база данных, и потом запускается syncdb
для нее. Если тестовый класс является потомком класса TestCase
(как у нас), Django так же сбросит данные в значения по умолчанию после запуска каждого теста, так что изменения в одном из тестов не затронут другие.
2.5. Резюме
- Модель определяет поля в таблице, и содержит бизнес-логику.
- Команда
syncdb
создает таблицы в вашей базе данных из моделей. В Django версии 1.7 и выше вместо командыsyncdb
необходимо использовать сначала командуmakemigrations
— для создания миграций, а после этого командуmigrate
— для внесение изменений в базу. - Менеджер модели позволяет вам оперировать коллекциями экземпляров: запросы, создание и т. д..
- Пишите модульные тесты для методов, которые вы добавили в модель.
- Команда управления
test
запускает модульные тесты на выполнение.
Примечание переводчика:
Для того чтобы протестировать наше, пока еще пустое, приложение нужно выполнить следующую команду:(venv:tutorial)$ python ./manage.py runserver 0.0.0.0:8080
Это запустит встроенный сервер, функционал которого любезно предоставляет нам Django. В параметрах после
runserver
указывается ip-адрес и порт, который будет слушаться работающим сервер. В нашем случае сервер будет принимать запросы от всех ip-адресов при обращении на 8080 порт.Я использую для разработки домашний сервер с внутренним IP 192.168.1.51. Так что для того что-бы увидеть результат работы сервера разработки в браузере я захожу по адресу http://192.168.1.51:8080/. Вы же должны подставить адрес своего сервера.
Как думаете, нужно ли продолжать перевод остальных глав? Оказался ли перевод для Вас полезен?
Буду рад конструктивной критике в комментариях.
Об ошибках и неточностях перевода прошу сообщать в личном сообщении.
Использованное в начале поста изображение создано как вариация изображения пользователя MaGIc2laNTern
admin
- admin.AdminSite.add_action()
- admin.AdminSite.disable_action()
- admin.ModelAdmin.get_actions()
- admin.action()
- admin.AdminSite
- admin.AdminSite.app_index_template
- admin.AdminSite.each_context()
- admin.AdminSite.empty_value_display
- admin.AdminSite.enable_nav_sidebar
- admin.AdminSite.final_catch_all_view
- admin.AdminSite.get_app_list()
- admin.AdminSite.has_permission()
- admin.AdminSite.index_template
- admin.AdminSite.index_title
- admin.AdminSite.login_form
- admin.AdminSite.login_template
- admin.AdminSite.logout_template
- admin.AdminSite.password_change_done_template
- admin.AdminSite.password_change_template
- admin.AdminSite.register()
- admin.AdminSite.site_header
- admin.AdminSite.site_title
- admin.AdminSite.site_url
- admin.AdminSite.unregister()
- admin.InlineModelAdmin
- admin.InlineModelAdmin.can_delete
- admin.InlineModelAdmin.classes
- admin.InlineModelAdmin.extra
- admin.InlineModelAdmin.fk_name
- admin.InlineModelAdmin.form
- admin.InlineModelAdmin.formset
- admin.InlineModelAdmin.get_extra()
- admin.InlineModelAdmin.get_formset()
- admin.InlineModelAdmin.get_max_num()
- admin.InlineModelAdmin.get_min_num()
- admin.InlineModelAdmin.has_add_permission()
- admin.InlineModelAdmin.has_change_permission()
- admin.InlineModelAdmin.has_delete_permission()
- admin.InlineModelAdmin.max_num
- admin.InlineModelAdmin.min_num
- admin.InlineModelAdmin.model
- admin.InlineModelAdmin.raw_id_fields
- admin.InlineModelAdmin.show_change_link
- admin.InlineModelAdmin.template
- admin.InlineModelAdmin.verbose_name
- admin.InlineModelAdmin.verbose_name_plural
- admin.ModelAdmin
- admin.ModelAdmin.actions
- admin.ModelAdmin.actions_on_bottom
- admin.ModelAdmin.actions_on_top
- admin.ModelAdmin.actions_selection_counter
- admin.ModelAdmin.add_form_template
- admin.ModelAdmin.add_view()
- admin.ModelAdmin.autocomplete_fields
- admin.ModelAdmin.change_form_template
- admin.ModelAdmin.change_list_template
- admin.ModelAdmin.change_view()
- admin.ModelAdmin.changelist_view()
- admin.ModelAdmin.date_hierarchy
- admin.ModelAdmin.delete_confirmation_template
- admin.ModelAdmin.delete_model()
- admin.ModelAdmin.delete_queryset()
- admin.ModelAdmin.delete_selected_confirmation_template
- admin.ModelAdmin.delete_view()
- admin.ModelAdmin.empty_value_display
- admin.ModelAdmin.exclude
- admin.ModelAdmin.fields
- admin.ModelAdmin.fieldsets
- admin.ModelAdmin.filter_horizontal
- admin.ModelAdmin.filter_vertical
- admin.ModelAdmin.form
- admin.ModelAdmin.formfield_for_choice_field()
- admin.ModelAdmin.formfield_for_foreignkey()
- admin.ModelAdmin.formfield_for_manytomany()
- admin.ModelAdmin.formfield_overrides
- admin.ModelAdmin.get_autocomplete_fields()
- admin.ModelAdmin.get_changeform_initial_data()
- admin.ModelAdmin.get_changelist()
- admin.ModelAdmin.get_changelist_form()
- admin.ModelAdmin.get_changelist_formset()
- admin.ModelAdmin.get_deleted_objects()
- admin.ModelAdmin.get_exclude()
- admin.ModelAdmin.get_fields()
- admin.ModelAdmin.get_fieldsets()
- admin.ModelAdmin.get_form()
- admin.ModelAdmin.get_formset_kwargs()
- admin.ModelAdmin.get_formsets_with_inlines()
- admin.ModelAdmin.get_inline_instances()
- admin.ModelAdmin.get_inlines()
- admin.ModelAdmin.get_list_display()
- admin.ModelAdmin.get_list_display_links()
- admin.ModelAdmin.get_list_filter()
- admin.ModelAdmin.get_list_select_related()
- admin.ModelAdmin.get_ordering()
- admin.ModelAdmin.get_paginator()
- admin.ModelAdmin.get_prepopulated_fields()
- admin.ModelAdmin.get_queryset()
- admin.ModelAdmin.get_readonly_fields()
- admin.ModelAdmin.get_search_fields()
- admin.ModelAdmin.get_search_results()
- admin.ModelAdmin.get_sortable_by()
- admin.ModelAdmin.get_urls()
- admin.ModelAdmin.has_add_permission()
- admin.ModelAdmin.has_change_permission()
- admin.ModelAdmin.has_delete_permission()
- admin.ModelAdmin.has_module_permission()
- admin.ModelAdmin.has_view_permission()
- admin.ModelAdmin.history_view()
- admin.ModelAdmin.inlines
- admin.ModelAdmin.list_display
- admin.ModelAdmin.list_display_links
- admin.ModelAdmin.list_editable
- admin.ModelAdmin.list_filter
- admin.ModelAdmin.list_max_show_all
- admin.ModelAdmin.list_per_page
- admin.ModelAdmin.list_select_related
- admin.ModelAdmin.lookup_allowed()
- admin.ModelAdmin.message_user()
- admin.ModelAdmin.object_history_template
- admin.ModelAdmin.ordering
- admin.ModelAdmin.paginator
- admin.ModelAdmin.popup_response_template
- admin.ModelAdmin.prepopulated_fields
- admin.ModelAdmin.preserve_filters
- admin.ModelAdmin.radio_fields
- admin.ModelAdmin.raw_id_fields
- admin.ModelAdmin.readonly_fields
- admin.ModelAdmin.response_add()
- admin.ModelAdmin.response_change()
- admin.ModelAdmin.response_delete()
- admin.ModelAdmin.save_as
- admin.ModelAdmin.save_as_continue
- admin.ModelAdmin.save_formset()
- admin.ModelAdmin.save_model()
- admin.ModelAdmin.save_on_top
- admin.ModelAdmin.save_related()
- admin.ModelAdmin.search_fields
- admin.ModelAdmin.search_help_text
- admin.ModelAdmin.show_full_result_count
- admin.ModelAdmin.sortable_by
- admin.ModelAdmin.view_on_site
- admin.StackedInline
- admin.TabularInline
- admin.apps.AdminConfig
- admin.apps.SimpleAdminConfig
- admin.apps.SimpleAdminConfig.default_site
- admin.autodiscover()
- admin.display()
- admin.models.LogEntry
- admin.models.LogEntry.action_flag
- admin.models.LogEntry.action_time
- admin.models.LogEntry.change_message
- admin.models.LogEntry.content_type
- admin.models.LogEntry.get_change_message()
- admin.models.LogEntry.get_edited_object()
- admin.models.LogEntry.object_id
- admin.models.LogEntry.object_repr
- admin.models.LogEntry.user
- admin.register()
- admin.views.decorators.staff_member_required()
API
- Applications
- Фреймворк проверки системы
- Base views
- Общие представления о дате
- Общий вид дисплея
- Общие представления при редактировании
- Встроенный API представлений на основе классов
- Смеси представлений на основе классов
- Date-based mixins
- Editing mixins
- Многочисленные объектные микшины
- Simple mixins
- Однообъектные смеси
- Clickjacking Protection
- Admin actions
- Генератор документации администратора Django
- Фильтры списков ModelAdmin
- Сайт администратора Django
- Настройки JavaScript у администратора
- django.contrib.auth
- Структура контент-типов
- Приложение для плоских страниц
- сайт администратора GeoDjango
- Команды управления GeoDjango
- API базы данных GeoDjango
- Deploying GeoDjango
- Geographic Feeds
- GeoDjango Forms API
- Функции географической базы данных
- GDAL API
- Геолокация с GeoIP2
- Ссылка на API GIS QuerySet
- GEOS API
- GeoDjango
- Установка геопространственных библиотек
- GeoDjango Installation
- Installing PostGIS
- Installing SpatiaLite
- Утилита импорта данных LayerMapping
- Measurement Objects
- API модели GeoDjango
- OGR Inspection
- GeoJSON Serializer
- Geographic Sitemaps
- Тестирование приложений GeoDjango
- GeoDjango Tutorial
- GeoDjango Utilities
- django.contrib.humanize
- contrib packages
- Фреймворк сообщений
- Специфические функции агрегации PostgreSQL
- Ограничения конкретной базы данных PostgreSQL
- Выражения запросов,специфичные для PostgreSQL
- Поля конкретной модели PostgreSQL
- Поля конкретной формы PostgreSQL и виджеты
- Функции конкретной базы данных PostgreSQL
- django.contrib.postgres
- Индексы конкретной модели PostgreSQL
- Специфический поиск по PostgreSQL
- Миграционные операции Базы данных
- Полнотекстовый поиск
- Validators
- Приложение перенаправления
- Карта сайта
- Структура «сайты»
- Приложение статических файлов
- Система синдицированной подачи корма
- Защита от подделок при перекрестных запросах
- Databases
- django-admin and manage.py
- Django Exceptions
- Объект «Файл
- File handling
- API хранения файлов
- Загруженные файлы и обработчики загрузки
- Формы API
- Form fields
- Formset Functions
- Forms
- Форма модели Функции
- API рендеринга формы
- Проверка формы и поля
- Widgets
- API Reference
- Logging
- Middleware
- Migration Operations
- Ссылка на класс модели
- Conditional Expressions
- Constraints reference
- Database Functions
- Query Expressions
- Ссылка на модель поля
- Models
- Ссылка на индекс модели
- Ссылка на образец модели
- Ссылка на API поиска
- Модель _API цель
- Опции Модели Мета
- Ссылка на QuerySet API
- Ссылка на связанные объекты
- Paginator
- Объекты запроса и ответа
- SchemaEditor
- Settings
- Signals
- TemplateResponse и SimpleTemplateResponse
- Язык шаблонов Django:для программистов Python
- Встроенные теги и фильтры шаблонов
- Templates
- Язык шаблонов Django
- Unicode data
- функции утилиты django.urls
- функции django.urls для использования в URLconfs
- Django Utils
- Validators
- Built-in Views
apps
- apps.AppConfig
- apps.AppConfig.default
- apps.AppConfig.default_auto_field
- apps.AppConfig.get_model()
- apps.AppConfig.get_models()
- apps.AppConfig.label
- apps.AppConfig.models_module
- apps.AppConfig.module
- apps.AppConfig.name
- apps.AppConfig.path
- apps.AppConfig.ready()
- apps.AppConfig.verbose_name
- apps.apps
- apps.apps.get_app_config()
- apps.apps.get_app_configs()
- apps.apps.get_model()
- apps.apps.is_installed()
- apps.apps.ready
Guides
- Asynchronous support
- Настройка аутентификации в Django
- Использование системы аутентификации Django
- Аутентификация пользователей в Django
- Управление паролями в Django
- кэш-фреймворк Django
- Фреймворк проверки системы
- Встроенные классовые общие представления
- Обработка форм с помощью представлений на основе классов
- Class-based views
- Введение в классовые представления
- Использование смесей с классовыми представлениями
- Обработка условного просмотра
- Aggregation
- Примеры использования API модельного соотношения
- Many-to-many relationships
- Many-to-one relationships
- One-to-one relationships
- Модели и базы данных
- Database instrumentation
- Managers
- Models
- Multiple databases
- Оптимизация доступа к базе данных
- Making queries
- Search
- Выполнение сырых SQL-запросов
- Tablespaces
- Database transactions
- Sending email
- External packages
- Managing files
- Formsets
- Работа с формами
- Форма Активы (класс носителя)
- Создание форм из моделей
- View decorators
- File Uploads
- Generic views
- Обработка HTTP-запросов
- Middleware
- Как использовать сеансы
- функции ярлыков Django
- URL dispatcher
- Writing views
- Format localization
- Интернационализация и локализация
- Time zones
- Translation
- Using Django
- Как установить Django
- Logging
- Migrations
- Pagination
- Производительность и оптимизация
- Безопасность в Джанго
- Сериализация объектов Django
- Django settings
- Signals
- Cryptographic signing
- Templates
- Расширенные темы тестирования
- Тестирование на Django
- Пишущий и выполняющий тесты
- Testing tools
auth
- auth.backends.AllowAllUsersModelBackend
- auth.backends.AllowAllUsersRemoteUserBackend
- auth.backends.BaseBackend
- auth.backends.BaseBackend.get_all_permissions()
- auth.backends.BaseBackend.get_group_permissions()
- auth.backends.BaseBackend.get_user_permissions()
- auth.backends.BaseBackend.has_perm()
- auth.backends.ModelBackend
- auth.backends.ModelBackend.authenticate()
- auth.backends.ModelBackend.get_all_permissions()
- auth.backends.ModelBackend.get_group_permissions()
- auth.backends.ModelBackend.get_user_permissions()
- auth.backends.ModelBackend.has_module_perms()
- auth.backends.ModelBackend.has_perm()
- auth.backends.ModelBackend.user_can_authenticate()
- auth.backends.ModelBackend.with_perm()
- auth.backends.RemoteUserBackend
- auth.backends.RemoteUserBackend.authenticate()
- auth.backends.RemoteUserBackend.clean_username()
- auth.backends.RemoteUserBackend.configure_user()
- auth.backends.RemoteUserBackend.create_unknown_user
- auth.backends.RemoteUserBackend.user_can_authenticate()
- auth.get_user()
- auth.models.AnonymousUser
- auth.models.Group
- auth.models.Group.name
- auth.models.Group.permissions
- auth.models.Permission
- auth.models.Permission.codename
- auth.models.Permission.content_type
- auth.models.Permission.name
- auth.models.User
- auth.models.User.check_password()
- auth.models.User.date_joined
- auth.models.User.email
- auth.models.User.email_user()
- auth.models.User.first_name
- auth.models.User.get_all_permissions()
- auth.models.User.get_full_name()
- auth.models.User.get_group_permissions()
- auth.models.User.get_short_name()
- auth.models.User.get_user_permissions()
- auth.models.User.get_username()
- auth.models.User.groups
- auth.models.User.has_module_perms()
- auth.models.User.has_perm()
- auth.models.User.has_perms()
- auth.models.User.has_usable_password()
- auth.models.User.is_active
- auth.models.User.is_anonymous
- auth.models.User.is_authenticated
- auth.models.User.is_staff
- auth.models.User.is_superuser
- auth.models.User.last_login
- auth.models.User.last_name
- auth.models.User.password
- auth.models.User.set_password()
- auth.models.User.set_unusable_password()
- auth.models.User.user_permissions
- auth.models.User.username
- auth.models.UserManager
- auth.models.UserManager.create_superuser()
- auth.models.UserManager.create_user()
- auth.models.UserManager.with_perm()
- auth.signals.user_logged_in
- auth.signals.user_logged_out
- auth.signals.user_login_failed
- auth.validators.ASCIIUsernameValidator
- auth.validators.UnicodeUsernameValidator
- auth.middleware.AuthenticationMiddleware
- auth.middleware.PersistentRemoteUserMiddleware
- auth.middleware.RemoteUserMiddleware
- auth.context_processors.auth()
- auth.get_user_model()
- auth.is_active
- auth.is_staff
- auth.models.AbstractBaseUser
- auth.models.AbstractBaseUser.check_password()
- auth.models.AbstractBaseUser.clean()
- auth.models.AbstractBaseUser.get_email_field_name()
- auth.models.AbstractBaseUser.get_session_auth_hash()
- auth.models.AbstractBaseUser.get_username()
- auth.models.AbstractBaseUser.has_usable_password()
- auth.models.AbstractBaseUser.is_anonymous
- auth.models.AbstractBaseUser.is_authenticated
- auth.models.AbstractBaseUser.normalize_username()
- auth.models.AbstractBaseUser.set_password()
- auth.models.AbstractBaseUser.set_unusable_password()
- auth.models.AbstractUser
- auth.models.AbstractUser.clean()
- auth.models.BaseUserManager
- auth.models.BaseUserManager.get_by_natural_key()
- auth.models.BaseUserManager.make_random_password()
- auth.models.BaseUserManager.normalize_email()
- auth.models.CustomUser
- auth.models.CustomUser.EMAIL_FIELD
- auth.models.CustomUser.REQUIRED_FIELDS
- auth.models.CustomUser.USERNAME_FIELD
- auth.models.CustomUser.get_full_name()
- auth.models.CustomUser.get_short_name()
- auth.models.CustomUser.is_active
- auth.models.CustomUserManager
- auth.models.CustomUserManager.create_superuser()
- auth.models.CustomUserManager.create_user()
- auth.models.PermissionsMixin
- auth.models.PermissionsMixin.get_all_permissions()
- auth.models.PermissionsMixin.get_group_permissions()
- auth.models.PermissionsMixin.get_user_permissions()
- auth.models.PermissionsMixin.has_module_perms()
- auth.models.PermissionsMixin.has_perm()
- auth.models.PermissionsMixin.has_perms()
- auth.models.PermissionsMixin.is_superuser
- auth.authenticate()
- auth.decorators.login_required()
- auth.decorators.permission_required()
- auth.decorators.user_passes_test()
- auth.forms.AdminPasswordChangeForm
- auth.forms.AuthenticationForm
- auth.forms.AuthenticationForm.confirm_login_allowed()
- auth.forms.PasswordChangeForm
- auth.forms.PasswordResetForm
- auth.forms.PasswordResetForm.send_mail()
- auth.forms.SetPasswordForm
- auth.forms.UserChangeForm
- auth.forms.UserCreationForm
- auth.login()
- auth.logout()
- auth.mixins.AccessMixin
- auth.mixins.AccessMixin.get_login_url()
- auth.mixins.AccessMixin.get_permission_denied_message()
- auth.mixins.AccessMixin.get_redirect_field_name()
- auth.mixins.AccessMixin.handle_no_permission()
- auth.mixins.AccessMixin.login_url
- auth.mixins.AccessMixin.permission_denied_message
- auth.mixins.AccessMixin.raise_exception
- auth.mixins.AccessMixin.redirect_field_name
- auth.mixins.LoginRequiredMixin
- auth.mixins.PermissionRequiredMixin
- auth.mixins.PermissionRequiredMixin.get_permission_required()
- auth.mixins.PermissionRequiredMixin.has_permission()
- auth.mixins.UserPassesTestMixin
- auth.mixins.UserPassesTestMixin.get_test_func()
- auth.mixins.UserPassesTestMixin.test_func()
- auth.update_session_auth_hash()
- auth.views.LoginView
- auth.views.LoginView.authentication_form
- auth.views.LoginView.extra_context
- auth.views.LoginView.get_default_redirect_url()
- auth.views.LoginView.next_page
- auth.views.LoginView.redirect_authenticated_user
- auth.views.LoginView.redirect_field_name
- auth.views.LoginView.success_url_allowed_hosts
- auth.views.LoginView.template_name
- auth.views.LogoutView
- auth.views.LogoutView.extra_context
- auth.views.LogoutView.next_page
- auth.views.LogoutView.redirect_field_name
- auth.views.LogoutView.success_url_allowed_hosts
- auth.views.LogoutView.template_name
- auth.views.PasswordChangeDoneView
- auth.views.PasswordChangeDoneView.extra_context
- auth.views.PasswordChangeDoneView.template_name
- auth.views.PasswordChangeView
- auth.views.PasswordChangeView.extra_context
- auth.views.PasswordChangeView.form_class
- auth.views.PasswordChangeView.success_url
- auth.views.PasswordChangeView.template_name
- auth.views.PasswordResetCompleteView
- auth.views.PasswordResetCompleteView.extra_context
- auth.views.PasswordResetCompleteView.template_name
- auth.views.PasswordResetConfirmView
- auth.views.PasswordResetConfirmView.extra_context
- auth.views.PasswordResetConfirmView.form_class
- auth.views.PasswordResetConfirmView.post_reset_login
- auth.views.PasswordResetConfirmView.post_reset_login_backend
- auth.views.PasswordResetConfirmView.reset_url_token
- auth.views.PasswordResetConfirmView.success_url
- auth.views.PasswordResetConfirmView.template_name
- auth.views.PasswordResetConfirmView.token_generator
- auth.views.PasswordResetDoneView
- auth.views.PasswordResetDoneView.extra_context
- auth.views.PasswordResetDoneView.template_name
- auth.views.PasswordResetView
- auth.views.PasswordResetView.email_template_name
- auth.views.PasswordResetView.extra_context
- auth.views.PasswordResetView.extra_email_context
- auth.views.PasswordResetView.form_class
- auth.views.PasswordResetView.from_email
- auth.views.PasswordResetView.html_email_template_name
- auth.views.PasswordResetView.subject_template_name
- auth.views.PasswordResetView.success_url
- auth.views.PasswordResetView.template_name
- auth.views.PasswordResetView.token_generator
- auth.views.logout_then_login()
- auth.views.redirect_to_login()
- auth.hashers.check_password()
- auth.hashers.is_password_usable()
- auth.hashers.make_password()
- auth.password_validation.CommonPasswordValidator
- auth.password_validation.MinimumLengthValidator
- auth.password_validation.NumericPasswordValidator
- auth.password_validation.UserAttributeSimilarityValidator
- auth.password_validation.get_password_validators()
- auth.password_validation.password_changed()
- auth.password_validation.password_validators_help_text_html()
- auth.password_validation.password_validators_help_texts()
- auth.password_validation.validate_password()
conf
- conf.urls.handler400
- conf.urls.handler403
- conf.urls.handler404
- conf.urls.handler500
- conf.urls.static.static()
- conf.urls.i18n.i18n_patterns()
- conf.settings.configure()
- conf.settings.configured
contenttypes
- contenttypes.admin.GenericInlineModelAdmin
- contenttypes.admin.GenericInlineModelAdmin.ct_field
- contenttypes.admin.GenericInlineModelAdmin.ct_fk_field
- contenttypes.admin.GenericStackedInline
- contenttypes.admin.GenericTabularInline
- contenttypes.fields.GenericForeignKey
- contenttypes.fields.GenericForeignKey.for_concrete_model
- contenttypes.fields.GenericRelation
- contenttypes.fields.GenericRelation.related_query_name
- contenttypes.forms.BaseGenericInlineFormSet
- contenttypes.forms.generic_inlineformset_factory()
- contenttypes.models.ContentType
- contenttypes.models.ContentType.app_label
- contenttypes.models.ContentType.get_object_for_this_type()
- contenttypes.models.ContentType.model
- contenttypes.models.ContentType.model_class()
- contenttypes.models.ContentType.name
- contenttypes.models.ContentTypeManager
- contenttypes.models.ContentTypeManager.clear_cache()
- contenttypes.models.ContentTypeManager.get_by_natural_key()
- contenttypes.models.ContentTypeManager.get_for_id()
- contenttypes.models.ContentTypeManager.get_for_model()
- contenttypes.models.ContentTypeManager.get_for_models()
core
- core.files.storage._open()
- core.files.storage._save()
- core.files.storage.get_alternative_name()
- core.files.storage.get_available_name()
- core.files.storage.get_valid_name()
- core.management.AppCommand
- core.management.AppCommand.handle_app_config()
- core.management.BaseCommand
- core.management.BaseCommand.add_arguments()
- core.management.BaseCommand.check()
- core.management.BaseCommand.create_parser()
- core.management.BaseCommand.execute()
- core.management.BaseCommand.get_version()
- core.management.BaseCommand.handle()
- core.management.BaseCommand.help
- core.management.BaseCommand.missing_args_message
- core.management.BaseCommand.output_transaction
- core.management.BaseCommand.requires_migrations_checks
- core.management.BaseCommand.requires_system_checks
- core.management.BaseCommand.style
- core.management.BaseCommand.suppressed_base_arguments
- core.management.LabelCommand
- core.management.LabelCommand.handle_label()
- core.management.LabelCommand.label
- core.checks.CheckMessage
- core.checks.Critical
- core.checks.Debug
- core.checks.Error
- core.checks.Info
- core.checks.Warning
- core.management.call_command()
- core.exceptions.NON_FIELD_ERRORS
- core.files.File
- core.files.File.__iter__()
- core.files.File.chunks()
- core.files.File.close()
- core.files.File.delete()
- core.files.File.file
- core.files.File.mode
- core.files.File.multiple_chunks()
- core.files.File.name
- core.files.File.open()
- core.files.File.save()
- core.files.File.size
- core.files.base.ContentFile
- core.files.images.ImageFile
- core.files.images.ImageFile.height
- core.files.images.ImageFile.width
- core.files.storage.DefaultStorage
- core.files.storage.FileSystemStorage
- core.files.storage.FileSystemStorage.base_url
- core.files.storage.FileSystemStorage.directory_permissions_mode
- core.files.storage.FileSystemStorage.file_permissions_mode
- core.files.storage.FileSystemStorage.get_created_time()
- core.files.storage.FileSystemStorage.location
- core.files.storage.Storage
- core.files.storage.Storage.delete()
- core.files.storage.Storage.exists()
- core.files.storage.Storage.generate_filename()
- core.files.storage.Storage.get_accessed_time()
- core.files.storage.Storage.get_alternative_name()
- core.files.storage.Storage.get_available_name()
- core.files.storage.Storage.get_created_time()
- core.files.storage.Storage.get_modified_time()
- core.files.storage.Storage.get_valid_name()
- core.files.storage.Storage.listdir()
- core.files.storage.Storage.open()
- core.files.storage.Storage.path()
- core.files.storage.Storage.save()
- core.files.storage.Storage.size()
- core.files.storage.Storage.url()
- core.files.storage.get_storage_class()
- core.files.uploadedfile.InMemoryUploadedFile
- core.files.uploadedfile.TemporaryUploadedFile
- core.files.uploadedfile.TemporaryUploadedFile.temporary_file_path()
- core.files.uploadedfile.UploadedFile
- core.files.uploadedfile.UploadedFile.charset
- core.files.uploadedfile.UploadedFile.chunks()
- core.files.uploadedfile.UploadedFile.content_type
- core.files.uploadedfile.UploadedFile.content_type_extra
- core.files.uploadedfile.UploadedFile.multiple_chunks()
- core.files.uploadedfile.UploadedFile.name
- core.files.uploadedfile.UploadedFile.read()
- core.files.uploadedfile.UploadedFile.size
- core.files.uploadhandler.FileUploadHandler
- core.files.uploadhandler.FileUploadHandler.chunk_size
- core.files.uploadhandler.FileUploadHandler.file_complete()
- core.files.uploadhandler.FileUploadHandler.handle_raw_input()
- core.files.uploadhandler.FileUploadHandler.new_file()
- core.files.uploadhandler.FileUploadHandler.receive_data_chunk()
- core.files.uploadhandler.FileUploadHandler.upload_complete()
- core.files.uploadhandler.FileUploadHandler.upload_interrupted()
- core.files.uploadhandler.MemoryFileUploadHandler
- core.files.uploadhandler.TemporaryFileUploadHandler
- core.paginator.Page
- core.paginator.Page.end_index()
- core.paginator.Page.has_next()
- core.paginator.Page.has_other_pages()
- core.paginator.Page.has_previous()
- core.paginator.Page.next_page_number()
- core.paginator.Page.number
- core.paginator.Page.object_list
- core.paginator.Page.paginator
- core.paginator.Page.previous_page_number()
- core.paginator.Page.start_index()
- core.paginator.Paginator
- core.paginator.Paginator.ELLIPSIS
- core.paginator.Paginator.allow_empty_first_page
- core.paginator.Paginator.count
- core.paginator.Paginator.get_elided_page_range()
- core.paginator.Paginator.get_page()
- core.paginator.Paginator.num_pages
- core.paginator.Paginator.object_list
- core.paginator.Paginator.orphans
- core.paginator.Paginator.page()
- core.paginator.Paginator.page_range
- core.paginator.Paginator.per_page
- core.signals.got_request_exception
- core.signals.request_finished
- core.signals.request_started
- core.validators.DecimalValidator
- core.validators.EmailValidator
- core.validators.EmailValidator.allowlist
- core.validators.EmailValidator.code
- core.validators.EmailValidator.message
- core.validators.FileExtensionValidator
- core.validators.MaxLengthValidator
- core.validators.MaxValueValidator
- core.validators.MinLengthValidator
- core.validators.MinValueValidator
- core.validators.ProhibitNullCharactersValidator
- core.validators.ProhibitNullCharactersValidator.code
- core.validators.ProhibitNullCharactersValidator.message
- core.validators.RegexValidator
- core.validators.RegexValidator.code
- core.validators.RegexValidator.flags
- core.validators.RegexValidator.inverse_match
- core.validators.RegexValidator.message
- core.validators.RegexValidator.regex
- core.validators.StepValueValidator
- core.validators.URLValidator
- core.validators.URLValidator.schemes
- core.validators.int_list_validator()
- core.validators.validate_comma_separated_integer_list
- core.validators.validate_email
- core.validators.validate_image_file_extension
- core.validators.validate_ipv46_address
- core.validators.validate_ipv4_address
- core.validators.validate_ipv6_address
- core.validators.validate_slug
- core.validators.validate_unicode_slug
- core.cache.cache
- core.cache.caches
- core.cache.utils.make_template_fragment_key()
- core.caches.cache.add()
- core.caches.cache.clear()
- core.caches.cache.close()
- core.caches.cache.decr()
- core.caches.cache.delete()
- core.caches.cache.delete_many()
- core.caches.cache.get()
- core.caches.cache.get_many()
- core.caches.cache.get_or_set()
- core.caches.cache.incr()
- core.caches.cache.set()
- core.caches.cache.set_many()
- core.caches.cache.touch()
- core.checks.register()
- core.mail.EmailMessage
- core.mail.backends.smtp.EmailBackend
- core.mail.get_connection()
- core.mail.mail_admins()
- core.mail.mail_managers()
- core.mail.send_mail()
- core.mail.send_mass_mail()
- core.serializers.get_serializer()
- core.serializers.json.DjangoJSONEncoder
- core.signing.Signer
- core.signing.TimestampSigner
- core.signing.TimestampSigner.sign()
- core.signing.TimestampSigner.sign_object()
- core.signing.TimestampSigner.unsign()
- core.signing.TimestampSigner.unsign_object()
- core.signing.dumps()
- core.signing.loads()
- core.mail.django.core.mail.outbox
backends
- db.backends.base.schema.BaseDatabaseSchemaEditor
- db.backends.base.schema.BaseDatabaseSchemaEditor.add_constraint()
- db.backends.base.schema.BaseDatabaseSchemaEditor.add_field()
- db.backends.base.schema.BaseDatabaseSchemaEditor.add_index()
- db.backends.base.schema.BaseDatabaseSchemaEditor.alter_db_table()
- db.backends.base.schema.BaseDatabaseSchemaEditor.alter_db_tablespace()
- db.backends.base.schema.BaseDatabaseSchemaEditor.alter_field()
- db.backends.base.schema.BaseDatabaseSchemaEditor.alter_index_together()
- db.backends.base.schema.BaseDatabaseSchemaEditor.alter_unique_together()
- db.backends.base.schema.BaseDatabaseSchemaEditor.create_model()
- db.backends.base.schema.BaseDatabaseSchemaEditor.delete_model()
- db.backends.base.schema.BaseDatabaseSchemaEditor.execute()
- db.backends.base.schema.BaseDatabaseSchemaEditor.remove_constraint()
- db.backends.base.schema.BaseDatabaseSchemaEditor.remove_field()
- db.backends.base.schema.BaseDatabaseSchemaEditor.remove_index()
- db.backends.base.schema.BaseDatabaseSchemaEditor.rename_index()
- db.backends.base.schema.SchemaEditor.connection
- db.backends.signals.connection_created
- db.backends.base.DatabaseWrapper.execute_wrapper()
migrations
- db.migrations.operations.AddConstraint
- db.migrations.operations.AddField
- db.migrations.operations.AddIndex
- db.migrations.operations.AlterField
- db.migrations.operations.AlterIndexTogether
- db.migrations.operations.AlterModelManagers
- db.migrations.operations.AlterModelOptions
- db.migrations.operations.AlterModelTable
- db.migrations.operations.AlterOrderWithRespectTo
- db.migrations.operations.AlterUniqueTogether
- db.migrations.operations.CreateModel
- db.migrations.operations.DeleteModel
- db.migrations.operations.RemoveConstraint
- db.migrations.operations.RemoveField
- db.migrations.operations.RemoveIndex
- db.migrations.operations.RenameField
- db.migrations.operations.RenameIndex
- db.migrations.operations.RenameModel
- db.migrations.operations.RunPython
- db.migrations.operations.RunPython.noop()
- db.migrations.operations.RunSQL
- db.migrations.operations.RunSQL.noop
- db.migrations.operations.SeparateDatabaseAndState
- db.migrations.Migration.initial
models
- db.models.Model.objects
- db.models.expressions.Case
- db.models.expressions.When
- db.models.BaseConstraint
- db.models.BaseConstraint.name
- db.models.BaseConstraint.validate()
- db.models.BaseConstraint.violation_error_message
- db.models.CheckConstraint
- db.models.CheckConstraint.check
- db.models.UniqueConstraint
- db.models.UniqueConstraint.condition
- db.models.UniqueConstraint.deferrable
- db.models.UniqueConstraint.expressions
- db.models.UniqueConstraint.fields
- db.models.UniqueConstraint.include
- db.models.UniqueConstraint.opclasses
- db.models.UniqueConstraint.violation_error_message
- db.models.functions.ACos
- db.models.functions.ASin
- db.models.functions.ATan
- db.models.functions.ATan2
- db.models.functions.Abs
- db.models.functions.Cast
- db.models.functions.Ceil
- db.models.functions.Chr
- db.models.functions.Coalesce
- db.models.functions.Collate
- db.models.functions.Concat
- db.models.functions.Cos
- db.models.functions.Cot
- db.models.functions.CumeDist
- db.models.functions.Degrees
- db.models.functions.DenseRank
- db.models.functions.Exp
- db.models.functions.Extract
- db.models.functions.ExtractDay
- db.models.functions.ExtractHour
- db.models.functions.ExtractIsoWeekDay
- db.models.functions.ExtractIsoYear
- db.models.functions.ExtractMinute
- db.models.functions.ExtractMonth
- db.models.functions.ExtractQuarter
- db.models.functions.ExtractSecond
- db.models.functions.ExtractWeek
- db.models.functions.ExtractWeekDay
- db.models.functions.ExtractYear
- db.models.functions.FirstValue
- db.models.functions.Floor
- db.models.functions.Greatest
- db.models.functions.JSONObject
- db.models.functions.LPad
- db.models.functions.LTrim
- db.models.functions.Lag
- db.models.functions.LastValue
- db.models.functions.Lead
- db.models.functions.Least
- db.models.functions.Left
- db.models.functions.Length
- db.models.functions.Ln
- db.models.functions.Log
- db.models.functions.Lower
- db.models.functions.MD5
- db.models.functions.Mod
- db.models.functions.Now
- db.models.functions.NthValue
- db.models.functions.Ntile
- db.models.functions.NullIf
- db.models.functions.Ord
- db.models.functions.PercentRank
- db.models.functions.Pi
- db.models.functions.Power
- db.models.functions.RPad
- db.models.functions.RTrim
- db.models.functions.Radians
- db.models.functions.Random
- db.models.functions.Rank
- db.models.functions.Repeat
- db.models.functions.Replace
- db.models.functions.Reverse
- db.models.functions.Right
- db.models.functions.Round
- db.models.functions.RowNumber
- db.models.functions.SHA1
- db.models.functions.SHA224
- db.models.functions.SHA256
- db.models.functions.SHA384
- db.models.functions.SHA512
- db.models.functions.Sign
- db.models.functions.Sin
- db.models.functions.Sqrt
- db.models.functions.StrIndex
- db.models.functions.Substr
- db.models.functions.Tan
- db.models.functions.Trim
- db.models.functions.Trunc
- db.models.functions.TruncDate
- db.models.functions.TruncDay
- db.models.functions.TruncHour
- db.models.functions.TruncMinute
- db.models.functions.TruncMonth
- db.models.functions.TruncQuarter
- db.models.functions.TruncSecond
- db.models.functions.TruncTime
- db.models.functions.TruncWeek
- db.models.functions.TruncYear
- db.models.functions.Upper
- db.models.Aggregate
- db.models.Aggregate.allow_distinct
- db.models.Aggregate.empty_result_set_value
- db.models.Aggregate.function
- db.models.Aggregate.template
- db.models.Aggregate.window_compatible
- db.models.Exists
- db.models.Expression
- db.models.Expression.asc()
- db.models.Expression.contains_aggregate
- db.models.Expression.contains_over_clause
- db.models.Expression.convert_value()
- db.models.Expression.desc()
- db.models.Expression.empty_result_set_value
- db.models.Expression.filterable
- db.models.Expression.get_group_by_cols()
- db.models.Expression.get_source_expressions()
- db.models.Expression.relabeled_clone()
- db.models.Expression.resolve_expression()
- db.models.Expression.reverse_ordering()
- db.models.Expression.set_source_expressions()
- db.models.Expression.window_compatible
- db.models.ExpressionWrapper
- db.models.F
- db.models.Func
- db.models.Func.arg_joiner
- db.models.Func.arity
- db.models.Func.as_sql()
- db.models.Func.function
- db.models.Func.template
- db.models.OuterRef
- db.models.Subquery
- db.models.Value
- db.models.expressions.RawSQL
- db.models.expressions.RowRange
- db.models.expressions.RowRange.frame_type
- db.models.expressions.ValueRange
- db.models.expressions.ValueRange.frame_type
- db.models.expressions.Window
- db.models.expressions.Window.filterable
- db.models.expressions.Window.template
- db.models.AutoField
- db.models.BigAutoField
- db.models.BigIntegerField
- db.models.BinaryField
- db.models.BinaryField.max_length
- db.models.BooleanField
- db.models.CASCADE
- db.models.CharField
- db.models.CharField.db_collation
- db.models.CharField.max_length
- db.models.DO_NOTHING
- db.models.DateField
- db.models.DateField.auto_now
- db.models.DateField.auto_now_add
- db.models.DateTimeField
- db.models.DecimalField
- db.models.DecimalField.decimal_places
- db.models.DecimalField.max_digits
- db.models.DurationField
- db.models.EmailField
- db.models.Field
- db.models.Field.auto_created
- db.models.Field.blank
- db.models.Field.choices
- db.models.Field.concrete
- db.models.Field.db_column
- db.models.Field.db_index
- db.models.Field.db_tablespace
- db.models.Field.db_type()
- db.models.Field.deconstruct()
- db.models.Field.default
- db.models.Field.description
- db.models.Field.descriptor_class
- db.models.Field.editable
- db.models.Field.error_messages
- db.models.Field.formfield()
- db.models.Field.from_db_value()
- db.models.Field.get_db_prep_save()
- db.models.Field.get_db_prep_value()
- db.models.Field.get_internal_type()
- db.models.Field.get_prep_value()
- db.models.Field.help_text
- db.models.Field.hidden
- db.models.Field.is_relation
- db.models.Field.many_to_many
- db.models.Field.many_to_one
- db.models.Field.model
- db.models.Field.null
- db.models.Field.one_to_many
- db.models.Field.one_to_one
- db.models.Field.pre_save()
- db.models.Field.primary_key
- db.models.Field.rel_db_type()
- db.models.Field.related_model
- db.models.Field.to_python()
- db.models.Field.unique
- db.models.Field.unique_for_date
- db.models.Field.unique_for_month
- db.models.Field.unique_for_year
- db.models.Field.validators
- db.models.Field.value_from_object()
- db.models.Field.value_to_string()
- db.models.Field.verbose_name
- db.models.FileField
- db.models.FileField.storage
- db.models.FileField.upload_to
- db.models.FilePathField
- db.models.FilePathField.allow_files
- db.models.FilePathField.allow_folders
- db.models.FilePathField.match
- db.models.FilePathField.path
- db.models.FilePathField.recursive
- db.models.FloatField
- db.models.ForeignKey
- db.models.ForeignKey.db_constraint
- db.models.ForeignKey.limit_choices_to
- db.models.ForeignKey.on_delete
- db.models.ForeignKey.related_name
- db.models.ForeignKey.related_query_name
- db.models.ForeignKey.swappable
- db.models.ForeignKey.to_field
- db.models.GenericIPAddressField
- db.models.GenericIPAddressField.protocol
- db.models.GenericIPAddressField.unpack_ipv4
- db.models.ImageField
- db.models.ImageField.height_field
- db.models.ImageField.width_field
- db.models.IntegerField
- db.models.JSONField
- db.models.JSONField.decoder
- db.models.JSONField.encoder
- db.models.ManyToManyField
- db.models.ManyToManyField.db_constraint
- db.models.ManyToManyField.db_table
- db.models.ManyToManyField.limit_choices_to
- db.models.ManyToManyField.related_name
- db.models.ManyToManyField.related_query_name
- db.models.ManyToManyField.swappable
- db.models.ManyToManyField.symmetrical
- db.models.ManyToManyField.through
- db.models.ManyToManyField.through_fields
- db.models.OneToOneField
- db.models.OneToOneField.parent_link
- db.models.PROTECT
- db.models.PositiveBigIntegerField
- db.models.PositiveIntegerField
- db.models.PositiveSmallIntegerField
- db.models.RESTRICT
- db.models.SET()
- db.models.SET_DEFAULT
- db.models.SET_NULL
- db.models.SlugField
- db.models.SlugField.allow_unicode
- db.models.SmallAutoField
- db.models.SmallIntegerField
- db.models.TextField
- db.models.TextField.db_collation
- db.models.TimeField
- db.models.URLField
- db.models.UUIDField
- db.models.fields.files.FieldFile
- db.models.fields.files.FieldFile.close()
- db.models.fields.files.FieldFile.delete()
- db.models.fields.files.FieldFile.name
- db.models.fields.files.FieldFile.open()
- db.models.fields.files.FieldFile.path
- db.models.fields.files.FieldFile.save()
- db.models.fields.files.FieldFile.size
- db.models.fields.files.FieldFile.url
- db.models.Index
- db.models.Index.condition
- db.models.Index.db_tablespace
- db.models.Index.expressions
- db.models.Index.fields
- db.models.Index.include
- db.models.Index.name
- db.models.Index.opclasses
- db.models.Model
- db.models.Model.__eq__()
- db.models.Model.__hash__()
- db.models.Model.__str__()
- db.models.Model._state
- db.models.Model.clean()
- db.models.Model.clean_fields()
- db.models.Model.delete()
- db.models.Model.from_db()
- db.models.Model.full_clean()
- db.models.Model.get_FOO_display()
- db.models.Model.get_absolute_url()
- db.models.Model.get_deferred_fields()
- db.models.Model.get_next_by_FOO()
- db.models.Model.get_previous_by_FOO()
- db.models.Model.pk
- db.models.Model.refresh_from_db()
- db.models.Model.save()
- db.models.Model.validate_constraints()
- db.models.Model.validate_unique()
- db.models.Lookup
- db.models.Lookup.lhs
- db.models.Lookup.lookup_name
- db.models.Lookup.process_lhs()
- db.models.Lookup.process_rhs()
- db.models.Lookup.rhs
- db.models.Transform
- db.models.Transform.bilateral
- db.models.Transform.lhs
- db.models.Transform.lookup_name
- db.models.Transform.output_field
- db.models.as_sql()
- db.models.as_vendorname()
- db.models.get_lookup()
- db.models.get_transform()
- db.models.lookups.RegisterLookupMixin
- db.models.lookups.RegisterLookupMixin.get_lookup()
- db.models.lookups.RegisterLookupMixin.get_lookups()
- db.models.lookups.RegisterLookupMixin.get_transform()
- db.models.lookups.RegisterLookupMixin.register_lookup()
- db.models.output_field
- db.models.options.Options
- db.models.options.Options.get_field()
- db.models.options.Options.get_fields()
- db.models.Options.abstract
- db.models.Options.app_label
- db.models.Options.base_manager_name
- db.models.Options.constraints
- db.models.Options.db_table
- db.models.Options.db_tablespace
- db.models.Options.default_manager_name
- db.models.Options.default_permissions
- db.models.Options.default_related_name
- db.models.Options.get_latest_by
- db.models.Options.index_together
- db.models.Options.indexes
- db.models.Options.label
- db.models.Options.label_lower
- db.models.Options.managed
- db.models.Options.order_with_respect_to
- db.models.Options.ordering
- db.models.Options.permissions
- db.models.Options.proxy
- db.models.Options.required_db_features
- db.models.Options.required_db_vendor
- db.models.Options.select_on_save
- db.models.Options.unique_together
- db.models.Options.verbose_name
- db.models.Options.verbose_name_plural
- db.models.Avg
- db.models.Avg.distinct
- db.models.Count
- db.models.Count.distinct
- db.models.FilteredRelation
- db.models.FilteredRelation.condition
- db.models.FilteredRelation.relation_name
- db.models.Max
- db.models.Min
- db.models.Prefetch
- db.models.Q
- db.models.StdDev
- db.models.StdDev.sample
- db.models.Sum
- db.models.Sum.distinct
- db.models.Variance
- db.models.Variance.sample
- db.models.prefetch_related_objects()
- db.models.query.QuerySet
- db.models.query.QuerySet.aaggregate()
- db.models.query.QuerySet.abulk_create()
- db.models.query.QuerySet.abulk_update()
- db.models.query.QuerySet.acontains()
- db.models.query.QuerySet.acount()
- db.models.query.QuerySet.acreate()
- db.models.query.QuerySet.adelete()
- db.models.query.QuerySet.aearliest()
- db.models.query.QuerySet.aexists()
- db.models.query.QuerySet.aexplain()
- db.models.query.QuerySet.afirst()
- db.models.query.QuerySet.aget()
- db.models.query.QuerySet.aget_or_create()
- db.models.query.QuerySet.aggregate()
- db.models.query.QuerySet.ain_bulk()
- db.models.query.QuerySet.aiterator()
- db.models.query.QuerySet.alast()
- db.models.query.QuerySet.alatest()
- db.models.query.QuerySet.alias()
- db.models.query.QuerySet.all()
- db.models.query.QuerySet.annotate()
- db.models.query.QuerySet.as_manager()
- db.models.query.QuerySet.aupdate()
- db.models.query.QuerySet.aupdate_or_create()
- db.models.query.QuerySet.bulk_create()
- db.models.query.QuerySet.bulk_update()
- db.models.query.QuerySet.contains()
- db.models.query.QuerySet.count()
- db.models.query.QuerySet.create()
- db.models.query.QuerySet.dates()
- db.models.query.QuerySet.datetimes()
- db.models.query.QuerySet.db
- db.models.query.QuerySet.defer()
- db.models.query.QuerySet.delete()
- db.models.query.QuerySet.difference()
- db.models.query.QuerySet.distinct()
- db.models.query.QuerySet.earliest()
- db.models.query.QuerySet.exclude()
- db.models.query.QuerySet.exists()
- db.models.query.QuerySet.explain()
- db.models.query.QuerySet.extra()
- db.models.query.QuerySet.filter()
- db.models.query.QuerySet.first()
- db.models.query.QuerySet.get()
- db.models.query.QuerySet.get_or_create()
- db.models.query.QuerySet.in_bulk()
- db.models.query.QuerySet.intersection()
- db.models.query.QuerySet.iterator()
- db.models.query.QuerySet.last()
- db.models.query.QuerySet.latest()
- db.models.query.QuerySet.none()
- db.models.query.QuerySet.only()
- db.models.query.QuerySet.order_by()
- db.models.query.QuerySet.ordered
- db.models.query.QuerySet.prefetch_related()
- db.models.query.QuerySet.raw()
- db.models.query.QuerySet.reverse()
- db.models.query.QuerySet.select_for_update()
- db.models.query.QuerySet.select_related()
- db.models.query.QuerySet.union()
- db.models.query.QuerySet.update()
- db.models.query.QuerySet.update_or_create()
- db.models.query.QuerySet.using()
- db.models.query.QuerySet.values()
- db.models.query.QuerySet.values_list()
- db.models.fields.related.RelatedManager
- db.models.fields.related.RelatedManager.add()
- db.models.fields.related.RelatedManager.clear()
- db.models.fields.related.RelatedManager.create()
- db.models.fields.related.RelatedManager.remove()
- db.models.fields.related.RelatedManager.set()
- db.models.signals.class_prepared
- db.models.signals.m2m_changed
- db.models.signals.post_delete
- db.models.signals.post_init
- db.models.signals.post_migrate
- db.models.signals.post_save
- db.models.signals.pre_delete
- db.models.signals.pre_init
- db.models.signals.pre_migrate
- db.models.signals.pre_save
- db.models.Manager
- db.models.Model._base_manager
- db.models.Model._default_manager
- db.models.from_queryset()
- db.models.CursorWrapper.callproc()
- db.models.Manager.raw()
transaction
- db.transaction.atomic()
- db.transaction.clean_savepoints()
- db.transaction.commit()
- db.transaction.get_autocommit()
- db.transaction.get_rollback()
- db.transaction.non_atomic_requests()
- db.transaction.on_commit()
- db.transaction.rollback()
- db.transaction.savepoint()
- db.transaction.savepoint_commit()
- db.transaction.savepoint_rollback()
- db.transaction.set_autocommit()
- db.transaction.set_rollback()
dispatch
- dispatch.Signal
- dispatch.Signal.connect()
- dispatch.Signal.disconnect()
- dispatch.Signal.send()
- dispatch.Signal.send_robust()
- dispatch.receiver()
flatpages
- flatpages.middleware.FlatpageFallbackMiddleware
- flatpages.models.FlatPage
- flatpages.sitemaps.FlatPageSitemap
forms
- forms.BoundField
- forms.BoundField.as_hidden()
- forms.BoundField.as_widget()
- forms.BoundField.auto_id
- forms.BoundField.css_classes()
- forms.BoundField.data
- forms.BoundField.errors
- forms.BoundField.field
- forms.BoundField.form
- forms.BoundField.help_text
- forms.BoundField.html_name
- forms.BoundField.id_for_label
- forms.BoundField.initial
- forms.BoundField.is_hidden
- forms.BoundField.label
- forms.BoundField.label_tag()
- forms.BoundField.legend_tag()
- forms.BoundField.name
- forms.BoundField.use_fieldset
- forms.BoundField.value()
- forms.BoundField.widget_type
- forms.ErrorList
- forms.ErrorList.as_text()
- forms.ErrorList.as_ul()
- forms.ErrorList.error_class
- forms.ErrorList.get_context()
- forms.ErrorList.render()
- forms.ErrorList.renderer
- forms.ErrorList.template_name
- forms.ErrorList.template_name_text
- forms.ErrorList.template_name_ul
- forms.Field.get_bound_field()
- forms.Form
- forms.Form.add_error()
- forms.Form.as_div()
- forms.Form.as_p()
- forms.Form.as_table()
- forms.Form.as_ul()
- forms.Form.auto_id
- forms.Form.changed_data
- forms.Form.clean()
- forms.Form.cleaned_data
- forms.Form.default_renderer
- forms.Form.error_css_class
- forms.Form.errors
- forms.Form.errors.as_data()
- forms.Form.errors.as_json()
- forms.Form.errors.get_json_data()
- forms.Form.field_order
- forms.Form.fields
- forms.Form.get_context()
- forms.Form.get_initial_for_field()
- forms.Form.has_changed()
- forms.Form.has_error()
- forms.Form.initial
- forms.Form.is_bound
- forms.Form.is_multipart()
- forms.Form.is_valid()
- forms.Form.label_suffix
- forms.Form.non_field_errors()
- forms.Form.order_fields()
- forms.Form.prefix
- forms.Form.render()
- forms.Form.required_css_class
- forms.Form.template_name
- forms.Form.template_name_div
- forms.Form.template_name_label
- forms.Form.template_name_p
- forms.Form.template_name_table
- forms.Form.template_name_ul
- forms.Form.use_required_attribute
- forms.BooleanField
- forms.CharField
- forms.CharField.empty_value
- forms.CharField.max_length
- forms.CharField.min_length
- forms.CharField.strip
- forms.ChoiceField
- forms.ChoiceField.choices
- forms.ComboField
- forms.ComboField.fields
- forms.DateField
- forms.DateField.input_formats
- forms.DateTimeField
- forms.DateTimeField.input_formats
- forms.DecimalField
- forms.DecimalField.decimal_places
- forms.DecimalField.max_digits
- forms.DecimalField.max_value
- forms.DecimalField.min_value
- forms.DecimalField.step_size
- forms.DurationField
- forms.EmailField
- forms.Field
- forms.Field.clean()
- forms.Field.disabled
- forms.Field.error_messages
- forms.Field.has_changed()
- forms.Field.help_text
- forms.Field.initial
- forms.Field.label
- forms.Field.label_suffix
- forms.Field.localize
- forms.Field.required
- forms.Field.validators
- forms.Field.widget
- forms.FileField
- forms.FilePathField
- forms.FilePathField.allow_files
- forms.FilePathField.allow_folders
- forms.FilePathField.match
- forms.FilePathField.path
- forms.FilePathField.recursive
- forms.FloatField
- forms.FloatField.max_value
- forms.FloatField.min_value
- forms.FloatField.step_size
- forms.GenericIPAddressField
- forms.GenericIPAddressField.protocol
- forms.GenericIPAddressField.unpack_ipv4
- forms.ImageField
- forms.IntegerField
- forms.IntegerField.max_value
- forms.IntegerField.min_value
- forms.IntegerField.step_size
- forms.JSONField
- forms.JSONField.decoder
- forms.JSONField.encoder
- forms.ModelChoiceField
- forms.ModelChoiceField.blank
- forms.ModelChoiceField.empty_label
- forms.ModelChoiceField.iterator
- forms.ModelChoiceField.queryset
- forms.ModelChoiceField.to_field_name
- forms.ModelChoiceIterator
- forms.ModelChoiceIterator.__iter__()
- forms.ModelChoiceIterator.field
- forms.ModelChoiceIteratorValue
- forms.ModelChoiceIteratorValue.__str__()
- forms.ModelChoiceIteratorValue.instance
- forms.ModelChoiceIteratorValue.value
- forms.ModelMultipleChoiceField
- forms.ModelMultipleChoiceField.iterator
- forms.ModelMultipleChoiceField.queryset
- forms.ModelMultipleChoiceField.to_field_name
- forms.MultiValueField
- forms.MultiValueField.compress()
- forms.MultiValueField.fields
- forms.MultiValueField.require_all_fields
- forms.MultiValueField.widget
- forms.MultipleChoiceField
- forms.NullBooleanField
- forms.RegexField
- forms.RegexField.regex
- forms.RegexField.strip
- forms.SlugField
- forms.SlugField.allow_unicode
- forms.SlugField.empty_value
- forms.SplitDateTimeField
- forms.SplitDateTimeField.input_date_formats
- forms.SplitDateTimeField.input_time_formats
- forms.TimeField
- forms.TimeField.input_formats
- forms.TypedChoiceField
- forms.TypedChoiceField.coerce
- forms.TypedChoiceField.empty_value
- forms.TypedMultipleChoiceField
- forms.URLField
- forms.UUIDField
- forms.formsets.formset_factory()
- forms.models.inlineformset_factory()
- forms.models.modelform_factory()
- forms.models.modelformset_factory()
- forms.renderers.BaseRenderer
- forms.renderers.BaseRenderer.form_template_name
- forms.renderers.BaseRenderer.formset_template_name
- forms.renderers.BaseRenderer.get_template()
- forms.renderers.BaseRenderer.render()
- forms.renderers.DjangoDivFormRenderer
- forms.renderers.DjangoTemplates
- forms.renderers.Jinja2
- forms.renderers.Jinja2DivFormRenderer
- forms.renderers.TemplatesSetting
- forms.CheckboxInput
- forms.CheckboxInput.check_test
- forms.CheckboxSelectMultiple
- forms.ClearableFileInput
- forms.DateInput
- forms.DateInput.format
- forms.DateTimeInput
- forms.DateTimeInput.format
- forms.EmailInput
- forms.FileInput
- forms.HiddenInput
- forms.MultiWidget
- forms.MultiWidget.decompress()
- forms.MultiWidget.get_context()
- forms.MultiWidget.widgets
- forms.MultipleHiddenInput
- forms.NullBooleanSelect
- forms.NumberInput
- forms.PasswordInput
- forms.PasswordInput.render_value
- forms.RadioSelect
- forms.Select
- forms.Select.choices
- forms.SelectDateWidget
- forms.SelectDateWidget.empty_label
- forms.SelectDateWidget.months
- forms.SelectDateWidget.years
- forms.SelectMultiple
- forms.SplitDateTimeWidget
- forms.SplitDateTimeWidget.date_attrs
- forms.SplitDateTimeWidget.date_format
- forms.SplitDateTimeWidget.time_attrs
- forms.SplitDateTimeWidget.time_format
- forms.SplitHiddenDateTimeWidget
- forms.TextInput
- forms.Textarea
- forms.TimeInput
- forms.TimeInput.format
- forms.URLInput
- forms.Widget
- forms.Widget.attrs
- forms.Widget.format_value()
- forms.Widget.get_context()
- forms.Widget.id_for_label()
- forms.Widget.render()
- forms.Widget.supports_microseconds
- forms.Widget.use_fieldset
- forms.Widget.use_required_attribute()
- forms.Widget.value_from_datadict()
- forms.Widget.value_omitted_from_data()
- forms.formsets.BaseFormSet
- forms.formsets.BaseFormSet.as_p()
- forms.formsets.BaseFormSet.as_table()
- forms.formsets.BaseFormSet.as_ul()
- forms.formsets.BaseFormSet.can_delete
- forms.formsets.BaseFormSet.can_delete_extra
- forms.formsets.BaseFormSet.can_order
- forms.formsets.BaseFormSet.deletion_widget
- forms.formsets.BaseFormSet.get_context()
- forms.formsets.BaseFormSet.get_deletion_widget()
- forms.formsets.BaseFormSet.get_ordering_widget()
- forms.formsets.BaseFormSet.ordering_widget
- forms.formsets.BaseFormSet.render()
- forms.formsets.BaseFormSet.renderer
- forms.formsets.BaseFormSet.template_name
- forms.formsets.BaseFormSet.template_name_div
- forms.formsets.BaseFormSet.template_name_p
- forms.formsets.BaseFormSet.template_name_table
- forms.formsets.BaseFormSet.template_name_ul
- forms.formsets.BaseFormSet.total_error_count()
- forms.ModelForm
- forms.models.BaseInlineFormSet
- forms.models.BaseModelFormSet
- forms.models.BaseModelFormSet.changed_objects
- forms.models.BaseModelFormSet.deleted_objects
- forms.models.BaseModelFormSet.new_objects
gis
- gis.admin.GISModelAdmin
- gis.admin.GISModelAdmin.gis_widget
- gis.admin.GISModelAdmin.gis_widget_kwargs
- gis.admin.GeoModelAdmin
- gis.admin.GeoModelAdmin.default_lat
- gis.admin.GeoModelAdmin.default_lon
- gis.admin.GeoModelAdmin.default_zoom
- gis.admin.GeoModelAdmin.extra_js
- gis.admin.GeoModelAdmin.map_height
- gis.admin.GeoModelAdmin.map_template
- gis.admin.GeoModelAdmin.map_width
- gis.admin.GeoModelAdmin.modifiable
- gis.admin.GeoModelAdmin.openlayers_url
- gis.admin.OSMGeoAdmin
- gis.feeds.Feed
- gis.feeds.Feed.geometry()
- gis.feeds.Feed.item_geometry()
- gis.feeds.GeoAtom1Feed
- gis.feeds.GeoRSSFeed
- gis.feeds.W3CGeoFeed
- gis.forms.Field.geom_type
- gis.forms.Field.srid
- gis.forms.GeometryCollectionField
- gis.forms.GeometryField
- gis.forms.LineStringField
- gis.forms.MultiLineStringField
- gis.forms.MultiPointField
- gis.forms.MultiPolygonField
- gis.forms.PointField
- gis.forms.PolygonField
- gis.forms.widgets.BaseGeometryWidget
- gis.forms.widgets.BaseGeometryWidget.display_raw
- gis.forms.widgets.BaseGeometryWidget.geom_type
- gis.forms.widgets.BaseGeometryWidget.map_height
- gis.forms.widgets.BaseGeometryWidget.map_srid
- gis.forms.widgets.BaseGeometryWidget.map_width
- gis.forms.widgets.BaseGeometryWidget.supports_3d
- gis.forms.widgets.BaseGeometryWidget.template_name
- gis.forms.widgets.OSMWidget
- gis.forms.widgets.OSMWidget.default_lat
- gis.forms.widgets.OSMWidget.default_lon
- gis.forms.widgets.OSMWidget.default_zoom
- gis.forms.widgets.OSMWidget.template_name
- gis.forms.widgets.OpenLayersWidget
- gis.db.models.functions.Area
- gis.db.models.functions.AsGML
- gis.db.models.functions.AsGeoJSON
- gis.db.models.functions.AsKML
- gis.db.models.functions.AsSVG
- gis.db.models.functions.AsWKB
- gis.db.models.functions.AsWKT
- gis.db.models.functions.Azimuth
- gis.db.models.functions.BoundingCircle
- gis.db.models.functions.Centroid
- gis.db.models.functions.Difference
- gis.db.models.functions.Distance
- gis.db.models.functions.Envelope
- gis.db.models.functions.ForcePolygonCW
- gis.db.models.functions.GeoHash
- gis.db.models.functions.GeometryDistance
- gis.db.models.functions.Intersection
- gis.db.models.functions.IsValid
- gis.db.models.functions.Length
- gis.db.models.functions.LineLocatePoint
- gis.db.models.functions.MakeValid
- gis.db.models.functions.MemSize
- gis.db.models.functions.NumGeometries
- gis.db.models.functions.NumPoints
- gis.db.models.functions.Perimeter
- gis.db.models.functions.PointOnSurface
- gis.db.models.functions.Reverse
- gis.db.models.functions.Scale
- gis.db.models.functions.SnapToGrid
- gis.db.models.functions.SymDifference
- gis.db.models.functions.Transform
- gis.db.models.functions.Translate
- gis.db.models.functions.Union
- gis.gdal.CoordTransform
- gis.gdal.DataSource
- gis.gdal.DataSource.layer_count
- gis.gdal.DataSource.name
- gis.gdal.Driver
- gis.gdal.Driver.driver_count
- gis.gdal.Envelope
- gis.gdal.Envelope.expand_to_include()
- gis.gdal.Envelope.ll
- gis.gdal.Envelope.max_x
- gis.gdal.Envelope.max_y
- gis.gdal.Envelope.min_x
- gis.gdal.Envelope.min_y
- gis.gdal.Envelope.tuple
- gis.gdal.Envelope.ur
- gis.gdal.Envelope.wkt
- gis.gdal.Feature
- gis.gdal.Feature.fid
- gis.gdal.Feature.fields
- gis.gdal.Feature.geom
- gis.gdal.Feature.geom_type
- gis.gdal.Feature.get
- gis.gdal.Feature.index
- gis.gdal.Feature.layer_name
- gis.gdal.Feature.num_fields
- gis.gdal.Field
- gis.gdal.Field.as_datetime()
- gis.gdal.Field.as_double()
- gis.gdal.Field.as_int()
- gis.gdal.Field.as_string()
- gis.gdal.Field.name
- gis.gdal.Field.precision
- gis.gdal.Field.type
- gis.gdal.Field.type_name
- gis.gdal.Field.value
- gis.gdal.Field.width
- gis.gdal.GDALBand
- gis.gdal.GDALBand.color_interp()
- gis.gdal.GDALBand.data()
- gis.gdal.GDALBand.datatype()
- gis.gdal.GDALBand.description
- gis.gdal.GDALBand.height
- gis.gdal.GDALBand.max
- gis.gdal.GDALBand.mean
- gis.gdal.GDALBand.metadata
- gis.gdal.GDALBand.min
- gis.gdal.GDALBand.nodata_value
- gis.gdal.GDALBand.pixel_count
- gis.gdal.GDALBand.statistics()
- gis.gdal.GDALBand.std
- gis.gdal.GDALBand.width
- gis.gdal.GDALRaster
- gis.gdal.GDALRaster.bands
- gis.gdal.GDALRaster.driver
- gis.gdal.GDALRaster.extent
- gis.gdal.GDALRaster.geotransform
- gis.gdal.GDALRaster.height
- gis.gdal.GDALRaster.info
- gis.gdal.GDALRaster.is_vsi_based
- gis.gdal.GDALRaster.metadata
- gis.gdal.GDALRaster.name
- gis.gdal.GDALRaster.origin
- gis.gdal.GDALRaster.scale
- gis.gdal.GDALRaster.skew
- gis.gdal.GDALRaster.srid
- gis.gdal.GDALRaster.srs
- gis.gdal.GDALRaster.transform()
- gis.gdal.GDALRaster.vsi_buffer
- gis.gdal.GDALRaster.warp()
- gis.gdal.GDALRaster.width
- gis.gdal.GeometryCollection
- gis.gdal.GeometryCollection.add()
- gis.gdal.Layer
- gis.gdal.Layer.extent
- gis.gdal.Layer.field_precisions
- gis.gdal.Layer.field_widths
- gis.gdal.Layer.fields
- gis.gdal.Layer.geom_type
- gis.gdal.Layer.get_fields()
- gis.gdal.Layer.get_geoms()
- gis.gdal.Layer.name
- gis.gdal.Layer.num_feat
- gis.gdal.Layer.num_fields
- gis.gdal.Layer.spatial_filter
- gis.gdal.Layer.srs
- gis.gdal.Layer.test_capability()
- gis.gdal.LineString
- gis.gdal.LineString.x
- gis.gdal.LineString.y
- gis.gdal.LineString.z
- gis.gdal.OGRGeomType
- gis.gdal.OGRGeomType.django
- gis.gdal.OGRGeomType.name
- gis.gdal.OGRGeomType.num
- gis.gdal.OGRGeometry
- gis.gdal.OGRGeometry.__getitem__()
- gis.gdal.OGRGeometry.__iter__()
- gis.gdal.OGRGeometry.__len__()
- gis.gdal.OGRGeometry.area
- gis.gdal.OGRGeometry.boundary()
- gis.gdal.OGRGeometry.clone()
- gis.gdal.OGRGeometry.close_rings()
- gis.gdal.OGRGeometry.contains()
- gis.gdal.OGRGeometry.convex_hull
- gis.gdal.OGRGeometry.coord_dim
- gis.gdal.OGRGeometry.coords
- gis.gdal.OGRGeometry.crosses()
- gis.gdal.OGRGeometry.difference()
- gis.gdal.OGRGeometry.dimension
- gis.gdal.OGRGeometry.disjoint()
- gis.gdal.OGRGeometry.envelope
- gis.gdal.OGRGeometry.equals()
- gis.gdal.OGRGeometry.ewkt
- gis.gdal.OGRGeometry.extent
- gis.gdal.OGRGeometry.from_bbox()
- gis.gdal.OGRGeometry.from_gml()
- gis.gdal.OGRGeometry.geom_count
- gis.gdal.OGRGeometry.geom_name
- gis.gdal.OGRGeometry.geom_type
- gis.gdal.OGRGeometry.geos
- gis.gdal.OGRGeometry.gml
- gis.gdal.OGRGeometry.hex
- gis.gdal.OGRGeometry.intersection()
- gis.gdal.OGRGeometry.intersects()
- gis.gdal.OGRGeometry.json
- gis.gdal.OGRGeometry.kml
- gis.gdal.OGRGeometry.num_coords
- gis.gdal.OGRGeometry.num_points
- gis.gdal.OGRGeometry.overlaps()
- gis.gdal.OGRGeometry.point_count
- gis.gdal.OGRGeometry.srid
- gis.gdal.OGRGeometry.srs
- gis.gdal.OGRGeometry.sym_difference()
- gis.gdal.OGRGeometry.touches()
- gis.gdal.OGRGeometry.transform()
- gis.gdal.OGRGeometry.tuple
- gis.gdal.OGRGeometry.union()
- gis.gdal.OGRGeometry.within()
- gis.gdal.OGRGeometry.wkb
- gis.gdal.OGRGeometry.wkb_size
- gis.gdal.OGRGeometry.wkt
- gis.gdal.Point
- gis.gdal.Point.x
- gis.gdal.Point.y
- gis.gdal.Point.z
- gis.gdal.Polygon
- gis.gdal.Polygon.centroid
- gis.gdal.Polygon.exterior_ring
- gis.gdal.Polygon.shell
- gis.gdal.SpatialReference
- gis.gdal.SpatialReference.__getitem__()
- gis.gdal.SpatialReference.angular_name
- gis.gdal.SpatialReference.angular_units
- gis.gdal.SpatialReference.attr_value()
- gis.gdal.SpatialReference.auth_code()
- gis.gdal.SpatialReference.auth_name()
- gis.gdal.SpatialReference.clone()
- gis.gdal.SpatialReference.ellipsoid
- gis.gdal.SpatialReference.from_esri()
- gis.gdal.SpatialReference.geographic
- gis.gdal.SpatialReference.identify_epsg()
- gis.gdal.SpatialReference.import_epsg()
- gis.gdal.SpatialReference.import_proj()
- gis.gdal.SpatialReference.import_user_input()
- gis.gdal.SpatialReference.import_wkt()
- gis.gdal.SpatialReference.import_xml()
- gis.gdal.SpatialReference.inverse_flattening
- gis.gdal.SpatialReference.linear_name
- gis.gdal.SpatialReference.linear_units
- gis.gdal.SpatialReference.local
- gis.gdal.SpatialReference.name
- gis.gdal.SpatialReference.pretty_wkt
- gis.gdal.SpatialReference.proj
- gis.gdal.SpatialReference.proj4
- gis.gdal.SpatialReference.projected
- gis.gdal.SpatialReference.semi_major
- gis.gdal.SpatialReference.semi_minor
- gis.gdal.SpatialReference.srid
- gis.gdal.SpatialReference.to_esri()
- gis.gdal.SpatialReference.units
- gis.gdal.SpatialReference.validate()
- gis.gdal.SpatialReference.wkt
- gis.gdal.SpatialReference.xml
- gis.geoip2.GeoIP2
- gis.geoip2.GeoIP2.city()
- gis.geoip2.GeoIP2.coords()
- gis.geoip2.GeoIP2.country()
- gis.geoip2.GeoIP2.country_code()
- gis.geoip2.GeoIP2.country_name()
- gis.geoip2.GeoIP2.geos()
- gis.geoip2.GeoIP2.lat_lon()
- gis.geoip2.GeoIP2.lon_lat()
- gis.geoip2.GeoIP2.open()
- gis.db.models.Collect
- gis.db.models.Extent
- gis.db.models.Extent3D
- gis.db.models.MakeLine
- gis.db.models.Union
- gis.geos.GEOSGeometry
- gis.geos.GEOSGeometry.area
- gis.geos.GEOSGeometry.boundary
- gis.geos.GEOSGeometry.buffer()
- gis.geos.GEOSGeometry.buffer_with_style()
- gis.geos.GEOSGeometry.centroid
- gis.geos.GEOSGeometry.clone()
- gis.geos.GEOSGeometry.contains()
- gis.geos.GEOSGeometry.convex_hull
- gis.geos.GEOSGeometry.coords
- gis.geos.GEOSGeometry.covers()
- gis.geos.GEOSGeometry.crosses()
- gis.geos.GEOSGeometry.difference()
- gis.geos.GEOSGeometry.dims
- gis.geos.GEOSGeometry.disjoint()
- gis.geos.GEOSGeometry.distance()
- gis.geos.GEOSGeometry.empty
- gis.geos.GEOSGeometry.envelope
- gis.geos.GEOSGeometry.equals()
- gis.geos.GEOSGeometry.equals_exact()
- gis.geos.GEOSGeometry.ewkb
- gis.geos.GEOSGeometry.ewkt
- gis.geos.GEOSGeometry.extent
- gis.geos.GEOSGeometry.from_gml()
- gis.geos.GEOSGeometry.geojson
- gis.geos.GEOSGeometry.geom_type
- gis.geos.GEOSGeometry.geom_typeid
- gis.geos.GEOSGeometry.hasz
- gis.geos.GEOSGeometry.hex
- gis.geos.GEOSGeometry.hexewkb
- gis.geos.GEOSGeometry.interpolate()
- gis.geos.GEOSGeometry.interpolate_normalized()
- gis.geos.GEOSGeometry.intersection()
- gis.geos.GEOSGeometry.intersects()
- gis.geos.GEOSGeometry.json
- gis.geos.GEOSGeometry.kml
- gis.geos.GEOSGeometry.length
- gis.geos.GEOSGeometry.make_valid()
- gis.geos.GEOSGeometry.normalize()
- gis.geos.GEOSGeometry.num_coords
- gis.geos.GEOSGeometry.num_geom
- gis.geos.GEOSGeometry.ogr
- gis.geos.GEOSGeometry.overlaps()
- gis.geos.GEOSGeometry.point_on_surface
- gis.geos.GEOSGeometry.prepared
- gis.geos.GEOSGeometry.project()
- gis.geos.GEOSGeometry.project_normalized()
- gis.geos.GEOSGeometry.relate()
- gis.geos.GEOSGeometry.relate_pattern()
- gis.geos.GEOSGeometry.ring
- gis.geos.GEOSGeometry.simple
- gis.geos.GEOSGeometry.simplify()
- gis.geos.GEOSGeometry.srid
- gis.geos.GEOSGeometry.srs
- gis.geos.GEOSGeometry.sym_difference()
- gis.geos.GEOSGeometry.touches()
- gis.geos.GEOSGeometry.transform()
- gis.geos.GEOSGeometry.unary_union
- gis.geos.GEOSGeometry.union()
- gis.geos.GEOSGeometry.valid
- gis.geos.GEOSGeometry.valid_reason
- gis.geos.GEOSGeometry.within()
- gis.geos.GEOSGeometry.wkb
- gis.geos.GEOSGeometry.wkt
- gis.geos.GeometryCollection
- gis.geos.LineString
- gis.geos.LineString.closed
- gis.geos.LinearRing
- gis.geos.LinearRing.is_counterclockwise
- gis.geos.MultiLineString
- gis.geos.MultiLineString.closed
- gis.geos.MultiLineString.merged
- gis.geos.MultiPoint
- gis.geos.MultiPolygon
- gis.geos.Point
- gis.geos.Polygon
- gis.geos.Polygon.from_bbox()
- gis.geos.Polygon.num_interior_rings
- gis.geos.PreparedGeometry
- gis.geos.PreparedGeometry.contains()
- gis.geos.PreparedGeometry.contains_properly()
- gis.geos.PreparedGeometry.covers()
- gis.geos.PreparedGeometry.crosses()
- gis.geos.PreparedGeometry.disjoint()
- gis.geos.PreparedGeometry.intersects()
- gis.geos.PreparedGeometry.overlaps()
- gis.geos.PreparedGeometry.touches()
- gis.geos.PreparedGeometry.within()
- gis.geos.WKBReader
- gis.geos.WKBWriter
- gis.geos.WKBWriter.byteorder
- gis.geos.WKBWriter.outdim
- gis.geos.WKBWriter.srid
- gis.geos.WKBWriter.write()
- gis.geos.WKBWriter.write_hex()
- gis.geos.WKTReader
- gis.geos.WKTWriter
- gis.geos.WKTWriter.outdim
- gis.geos.WKTWriter.precision
- gis.geos.WKTWriter.trim
- gis.geos.WKTWriter.write()
- gis.geos.fromfile()
- gis.geos.fromstr()
- gis.utils.LayerMapping
- gis.utils.LayerMapping.save()
- gis.measure.A
- gis.measure.Area
- gis.measure.Area.__getattr__()
- gis.measure.Area.unit_attname()
- gis.measure.D
- gis.measure.Distance
- gis.measure.Distance.__getattr__()
- gis.measure.Distance.unit_attname()
- gis.db.models.BaseSpatialField.spatial_index
- gis.db.models.BaseSpatialField.srid
- gis.db.models.GeometryCollectionField
- gis.db.models.GeometryField
- gis.db.models.GeometryField.dim
- gis.db.models.GeometryField.geography
- gis.db.models.LineStringField
- gis.db.models.MultiLineStringField
- gis.db.models.MultiPointField
- gis.db.models.MultiPolygonField
- gis.db.models.PointField
- gis.db.models.PolygonField
- gis.db.models.RasterField
- gis.utils.mapping()
How-tos
- Как аутентифицироваться с помощью REMOTE_USER
- Как использовать защиту от CSRF в Django
- Как написать пользовательский класс хранения
- Как писать пользовательские поисковые запросы
- Как создавать пользовательские команды django-admin
- Как создать пользовательские поля модели
- Как реализовать бэкенд пользовательского шаблона
- Как создавать пользовательские теги и фильтры шаблонов
- Как использовать Django с Дафни.
- Как использовать Django с Hypercorn
- Как развернуть с помощью ASGI
- Как использовать Django с Uvicorn.
- Deployment checklist
- Как развернуть Django
- Как аутентифицироваться в базе данных пользователей Django из Apache
- Как использовать «Джанго» с «Гуникорн».
- Как развернуть с WSGI
- Как использовать Django с Apache и mod_wsgi
- Как использовать Django с uWSGI
- Как управлять отчетами об ошибках
- “How-to” guides
- Как предоставить исходные данные для моделей
- Как интегрировать Django с унаследованной базой данных
- Как настроить и использовать протоколирование
- Как создать выходной файл CSV
- Как создавать файлы PDF
- Как переопределить шаблоны
- Как развернуть статические файлы
- Как управлять статическими файлами (например,изображениями,JavaScript,CSS)
- Как обновить Django до более новой версии
- Как установить Django на Windows
- Как создавать миграции баз данных
http
- http.FileResponse
- http.FileResponse.set_headers()
- http.HttpRequest
- http.HttpRequest.COOKIES
- http.HttpRequest.FILES
- http.HttpRequest.GET
- http.HttpRequest.META
- http.HttpRequest.POST
- http.HttpRequest.__iter__()
- http.HttpRequest.accepts()
- http.HttpRequest.body
- http.HttpRequest.build_absolute_uri()
- http.HttpRequest.content_params
- http.HttpRequest.content_type
- http.HttpRequest.current_app
- http.HttpRequest.encoding
- http.HttpRequest.exception_reporter_class
- http.HttpRequest.exception_reporter_filter
- http.HttpRequest.get_full_path()
- http.HttpRequest.get_full_path_info()
- http.HttpRequest.get_host()
- http.HttpRequest.get_port()
- http.HttpRequest.get_signed_cookie()
- http.HttpRequest.headers
- http.HttpRequest.is_secure()
- http.HttpRequest.method
- http.HttpRequest.path
- http.HttpRequest.path_info
- http.HttpRequest.read()
- http.HttpRequest.readline()
- http.HttpRequest.readlines()
- http.HttpRequest.resolver_match
- http.HttpRequest.scheme
- http.HttpRequest.session
- http.HttpRequest.site
- http.HttpRequest.urlconf
- http.HttpRequest.user
- http.HttpResponse
- http.HttpResponse.__delitem__()
- http.HttpResponse.__getitem__()
- http.HttpResponse.__init__()
- http.HttpResponse.__setitem__()
- http.HttpResponse.charset
- http.HttpResponse.close()
- http.HttpResponse.closed
- http.HttpResponse.content
- http.HttpResponse.delete_cookie()
- http.HttpResponse.flush()
- http.HttpResponse.get()
- http.HttpResponse.getvalue()
- http.HttpResponse.has_header()
- http.HttpResponse.headers
- http.HttpResponse.items()
- http.HttpResponse.readable()
- http.HttpResponse.reason_phrase
- http.HttpResponse.seekable()
- http.HttpResponse.set_cookie()
- http.HttpResponse.set_signed_cookie()
- http.HttpResponse.setdefault()
- http.HttpResponse.status_code
- http.HttpResponse.streaming
- http.HttpResponse.tell()
- http.HttpResponse.writable()
- http.HttpResponse.write()
- http.HttpResponse.writelines()
- http.HttpResponseBadRequest
- http.HttpResponseBase
- http.HttpResponseForbidden
- http.HttpResponseGone
- http.HttpResponseNotAllowed
- http.HttpResponseNotFound
- http.HttpResponseNotModified
- http.HttpResponsePermanentRedirect
- http.HttpResponseRedirect
- http.HttpResponseRedirect.url
- http.HttpResponseServerError
- http.JsonResponse
- http.QueryDict
- http.QueryDict.__contains__()
- http.QueryDict.__getitem__()
- http.QueryDict.__init__()
- http.QueryDict.__setitem__()
- http.QueryDict.appendlist()
- http.QueryDict.copy()
- http.QueryDict.dict()
- http.QueryDict.fromkeys()
- http.QueryDict.get()
- http.QueryDict.getlist()
- http.QueryDict.items()
- http.QueryDict.lists()
- http.QueryDict.pop()
- http.QueryDict.popitem()
- http.QueryDict.setdefault()
- http.QueryDict.setlist()
- http.QueryDict.setlistdefault()
- http.QueryDict.update()
- http.QueryDict.urlencode()
- http.QueryDict.values()
- http.StreamingHttpResponse
- http.StreamingHttpResponse.reason_phrase
- http.StreamingHttpResponse.status_code
- http.StreamingHttpResponse.streaming
- http.StreamingHttpResponse.streaming_content
- http.Http404
messages
- messages.add_message()
- messages.get_messages()
- messages.storage.base.BaseStorage
- messages.storage.base.Message
- messages.storage.cookie.CookieStorage
- messages.storage.fallback.FallbackStorage
- messages.storage.session.SessionStorage
- messages.views.SuccessMessageMixin
- messages.views.SuccessMessageMixin.get_success_message()
- messages.middleware.MessageMiddleware
middleware
- middleware.cache.FetchFromCacheMiddleware
- middleware.cache.UpdateCacheMiddleware
- middleware.clickjacking.XFrameOptionsMiddleware
- middleware.common.BrokenLinkEmailsMiddleware
- middleware.common.CommonMiddleware
- middleware.common.CommonMiddleware.response_redirect_class
- middleware.csrf.CsrfViewMiddleware
- middleware.gzip.GZipMiddleware
- middleware.http.ConditionalGetMiddleware
- middleware.locale.LocaleMiddleware
- middleware.locale.LocaleMiddleware.response_redirect_class
- middleware.security.SecurityMiddleware
postgres
- postgres.aggregates.ArrayAgg
- postgres.aggregates.ArrayAgg.distinct
- postgres.aggregates.ArrayAgg.ordering
- postgres.aggregates.BitAnd
- postgres.aggregates.BitOr
- postgres.aggregates.BitXor
- postgres.aggregates.BoolAnd
- postgres.aggregates.BoolOr
- postgres.aggregates.Corr
- postgres.aggregates.CovarPop
- postgres.aggregates.CovarPop.sample
- postgres.aggregates.JSONBAgg
- postgres.aggregates.JSONBAgg.distinct
- postgres.aggregates.JSONBAgg.ordering
- postgres.aggregates.RegrAvgX
- postgres.aggregates.RegrAvgY
- postgres.aggregates.RegrCount
- postgres.aggregates.RegrIntercept
- postgres.aggregates.RegrR2
- postgres.aggregates.RegrSXX
- postgres.aggregates.RegrSXY
- postgres.aggregates.RegrSYY
- postgres.aggregates.RegrSlope
- postgres.aggregates.StringAgg
- postgres.aggregates.StringAgg.delimiter
- postgres.aggregates.StringAgg.distinct
- postgres.aggregates.StringAgg.ordering
- postgres.constraints.ExclusionConstraint
- postgres.constraints.ExclusionConstraint.condition
- postgres.constraints.ExclusionConstraint.deferrable
- postgres.constraints.ExclusionConstraint.expressions
- postgres.constraints.ExclusionConstraint.include
- postgres.constraints.ExclusionConstraint.index_type
- postgres.constraints.ExclusionConstraint.name
- postgres.constraints.ExclusionConstraint.opclasses
- postgres.expressions.ArraySubquery
- postgres.fields.ArrayField
- postgres.fields.ArrayField.base_field
- postgres.fields.ArrayField.size
- postgres.fields.BigIntegerRangeField
- postgres.fields.CICharField
- postgres.fields.CIEmailField
- postgres.fields.CIText
- postgres.fields.CITextField
- postgres.fields.DateRangeField
- postgres.fields.DateTimeRangeField
- postgres.fields.DateTimeRangeField.default_bounds
- postgres.fields.DecimalRangeField
- postgres.fields.DecimalRangeField.default_bounds
- postgres.fields.HStoreField
- postgres.fields.IntegerRangeField
- postgres.fields.RangeBoundary
- postgres.fields.RangeBoundary.inclusive_lower
- postgres.fields.RangeBoundary.inclusive_upper
- postgres.fields.RangeField
- postgres.fields.RangeField.base_field
- postgres.fields.RangeField.form_field
- postgres.fields.RangeField.range_type
- postgres.fields.RangeOperators
- postgres.fields.django.postgres.forms.BaseRangeField
- postgres.fields.django.postgres.forms.BaseRangeField.base_field
- postgres.fields.django.postgres.forms.BaseRangeField.range_type
- postgres.fields.hstore.KeyTransform
- postgres.forms.DateRangeField
- postgres.forms.DateTimeRangeField
- postgres.forms.DecimalRangeField
- postgres.forms.HStoreField
- postgres.forms.IntegerRangeField
- postgres.forms.RangeWidget
- postgres.forms.RangeWidget.base_widget
- postgres.forms.RangeWidget.decompress()
- postgres.forms.SimpleArrayField
- postgres.forms.SimpleArrayField.base_field
- postgres.forms.SimpleArrayField.delimiter
- postgres.forms.SimpleArrayField.max_length
- postgres.forms.SimpleArrayField.min_length
- postgres.forms.SplitArrayField
- postgres.forms.SplitArrayField.base_field
- postgres.forms.SplitArrayField.remove_trailing_nulls
- postgres.forms.SplitArrayField.size
- postgres.functions.RandomUUID
- postgres.functions.TransactionNow
- postgres.indexes.BTreeIndex
- postgres.indexes.BloomIndex
- postgres.indexes.BrinIndex
- postgres.indexes.GinIndex
- postgres.indexes.GistIndex
- postgres.indexes.HashIndex
- postgres.indexes.OpClass
- postgres.indexes.SpGistIndex
- postgres.operations.AddConstraintNotValid
- postgres.operations.AddIndexConcurrently
- postgres.operations.BloomExtension
- postgres.operations.BtreeGinExtension
- postgres.operations.BtreeGistExtension
- postgres.operations.CITextExtension
- postgres.operations.CreateCollation
- postgres.operations.CreateExtension
- postgres.operations.CreateExtension.name
- postgres.operations.CryptoExtension
- postgres.operations.HStoreExtension
- postgres.operations.RemoveCollation
- postgres.operations.RemoveIndexConcurrently
- postgres.operations.TrigramExtension
- postgres.operations.UnaccentExtension
- postgres.operations.ValidateConstraint
- postgres.search.SearchHeadline
- postgres.search.SearchQuery
- postgres.search.SearchRank
- postgres.search.SearchVector
- postgres.search.SearchVectorField
- postgres.search.TrigramDistance
- postgres.search.TrigramSimilarity
- postgres.search.TrigramWordDistance
- postgres.search.TrigramWordSimilarity
- postgres.validators.KeysValidator
- postgres.validators.RangeMaxValueValidator
- postgres.validators.RangeMinValueValidator
redirects
- redirects.middleware.RedirectFallbackMiddleware
- redirects.middleware.RedirectFallbackMiddleware.response_gone_class
- redirects.middleware.RedirectFallbackMiddleware.response_redirect_class
- redirects.models.Redirect
sessions
- sessions.middleware.SessionMiddleware
- sessions.backends.base.SessionBase
- sessions.backends.base.SessionBase.__contains__()
- sessions.backends.base.SessionBase.__delitem__()
- sessions.backends.base.SessionBase.__getitem__()
- sessions.backends.base.SessionBase.__setitem__()
- sessions.backends.base.SessionBase.clear()
- sessions.backends.base.SessionBase.clear_expired()
- sessions.backends.base.SessionBase.cycle_key()
- sessions.backends.base.SessionBase.delete_test_cookie()
- sessions.backends.base.SessionBase.flush()
- sessions.backends.base.SessionBase.get()
- sessions.backends.base.SessionBase.get_expire_at_browser_close()
- sessions.backends.base.SessionBase.get_expiry_age()
- sessions.backends.base.SessionBase.get_expiry_date()
- sessions.backends.base.SessionBase.get_session_cookie_age()
- sessions.backends.base.SessionBase.items()
- sessions.backends.base.SessionBase.keys()
- sessions.backends.base.SessionBase.pop()
- sessions.backends.base.SessionBase.set_expiry()
- sessions.backends.base.SessionBase.set_test_cookie()
- sessions.backends.base.SessionBase.setdefault()
- sessions.backends.base.SessionBase.test_cookie_worked()
- sessions.backends.cached_db.SessionStore
- sessions.backends.cached_db.SessionStore.cache_key_prefix
- sessions.backends.db.SessionStore
- sessions.backends.db.SessionStore.create_model_instance()
- sessions.backends.db.SessionStore.get_model_class()
- sessions.base_session.AbstractBaseSession
- sessions.base_session.AbstractBaseSession.expire_date
- sessions.base_session.AbstractBaseSession.get_decoded()
- sessions.base_session.AbstractBaseSession.get_session_store_class()
- sessions.base_session.AbstractBaseSession.session_data
- sessions.base_session.AbstractBaseSession.session_key
- sessions.base_session.BaseSessionManager
- sessions.base_session.BaseSessionManager.encode()
- sessions.base_session.BaseSessionManager.save()
- sessions.serializers.JSONSerializer
- sessions.serializers.PickleSerializer
settings
- settings.GDAL_LIBRARY_PATH
- settings.GEOIP_CITY
- settings.GEOIP_COUNTRY
- settings.GEOIP_PATH
- settings.GEOS_LIBRARY_PATH
- settings.ABSOLUTE_URL_OVERRIDES
- settings.ADMINS
- settings.ALLOWED_HOSTS
- settings.APPEND_SLASH
- settings.AUTHENTICATION_BACKENDS
- settings.AUTH_PASSWORD_VALIDATORS
- settings.AUTH_USER_MODEL
- settings.CACHES
- settings.CACHE_MIDDLEWARE_ALIAS
- settings.CACHE_MIDDLEWARE_KEY_PREFIX
- settings.CACHE_MIDDLEWARE_SECONDS
- settings.CSRF_COOKIE_AGE
- settings.CSRF_COOKIE_DOMAIN
- settings.CSRF_COOKIE_HTTPONLY
- settings.CSRF_COOKIE_MASKED
- settings.CSRF_COOKIE_NAME
- settings.CSRF_COOKIE_PATH
- settings.CSRF_COOKIE_SAMESITE
- settings.CSRF_COOKIE_SECURE
- settings.CSRF_FAILURE_VIEW
- settings.CSRF_HEADER_NAME
- settings.CSRF_TRUSTED_ORIGINS
- settings.CSRF_USE_SESSIONS
- settings.DATABASES
- settings.DATABASE_ROUTERS
- settings.DATA_UPLOAD_MAX_MEMORY_SIZE
- settings.DATA_UPLOAD_MAX_NUMBER_FIELDS
- settings.DATETIME_FORMAT
- settings.DATETIME_INPUT_FORMATS
- settings.DATE_FORMAT
- settings.DATE_INPUT_FORMATS
- settings.DEBUG
- settings.DEBUG_PROPAGATE_EXCEPTIONS
- settings.DECIMAL_SEPARATOR
- settings.DEFAULT_AUTO_FIELD
- settings.DEFAULT_CHARSET
- settings.DEFAULT_EXCEPTION_REPORTER
- settings.DEFAULT_EXCEPTION_REPORTER_FILTER
- settings.DEFAULT_FILE_STORAGE
- settings.DEFAULT_FROM_EMAIL
- settings.DEFAULT_INDEX_TABLESPACE
- settings.DEFAULT_TABLESPACE
- settings.DISALLOWED_USER_AGENTS
- settings.EMAIL_BACKEND
- settings.EMAIL_FILE_PATH
- settings.EMAIL_HOST
- settings.EMAIL_HOST_PASSWORD
- settings.EMAIL_HOST_USER
- settings.EMAIL_PORT
- settings.EMAIL_SSL_CERTFILE
- settings.EMAIL_SSL_KEYFILE
- settings.EMAIL_SUBJECT_PREFIX
- settings.EMAIL_TIMEOUT
- settings.EMAIL_USE_LOCALTIME
- settings.EMAIL_USE_SSL
- settings.EMAIL_USE_TLS
- settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS
- settings.FILE_UPLOAD_HANDLERS
- settings.FILE_UPLOAD_MAX_MEMORY_SIZE
- settings.FILE_UPLOAD_PERMISSIONS
- settings.FILE_UPLOAD_TEMP_DIR
- settings.FIRST_DAY_OF_WEEK
- settings.FIXTURE_DIRS
- settings.FORCE_SCRIPT_NAME
- settings.FORMAT_MODULE_PATH
- settings.FORM_RENDERER
- settings.IGNORABLE_404_URLS
- settings.INSTALLED_APPS
- settings.INTERNAL_IPS
- settings.LANGUAGES
- settings.LANGUAGES_BIDI
- settings.LANGUAGE_CODE
- settings.LANGUAGE_COOKIE_AGE
- settings.LANGUAGE_COOKIE_DOMAIN
- settings.LANGUAGE_COOKIE_HTTPONLY
- settings.LANGUAGE_COOKIE_NAME
- settings.LANGUAGE_COOKIE_PATH
- settings.LANGUAGE_COOKIE_SAMESITE
- settings.LANGUAGE_COOKIE_SECURE
- settings.LOCALE_PATHS
- settings.LOGGING
- settings.LOGGING_CONFIG
- settings.LOGIN_REDIRECT_URL
- settings.LOGIN_URL
- settings.LOGOUT_REDIRECT_URL
- settings.MANAGERS
- settings.MEDIA_ROOT
- settings.MEDIA_URL
- settings.MESSAGE_LEVEL
- settings.MESSAGE_STORAGE
- settings.MESSAGE_TAGS
- settings.MIDDLEWARE
- settings.MIGRATION_MODULES
- settings.MONTH_DAY_FORMAT
- settings.NUMBER_GROUPING
- settings.PASSWORD_HASHERS
- settings.PASSWORD_RESET_TIMEOUT
- settings.PREPEND_WWW
- settings.ROOT_URLCONF
- settings.SECRET_KEY
- settings.SECRET_KEY_FALLBACKS
- settings.SECURE_CONTENT_TYPE_NOSNIFF
- settings.SECURE_CROSS_ORIGIN_OPENER_POLICY
- settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
- settings.SECURE_HSTS_PRELOAD
- settings.SECURE_HSTS_SECONDS
- settings.SECURE_PROXY_SSL_HEADER
- settings.SECURE_REDIRECT_EXEMPT
- settings.SECURE_REFERRER_POLICY
- settings.SECURE_SSL_HOST
- settings.SECURE_SSL_REDIRECT
- settings.SERIALIZATION_MODULES
- settings.SERVER_EMAIL
- settings.SESSION_CACHE_ALIAS
- settings.SESSION_COOKIE_AGE
- settings.SESSION_COOKIE_DOMAIN
- settings.SESSION_COOKIE_HTTPONLY
- settings.SESSION_COOKIE_NAME
- settings.SESSION_COOKIE_PATH
- settings.SESSION_COOKIE_SAMESITE
- settings.SESSION_COOKIE_SECURE
- settings.SESSION_ENGINE
- settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
- settings.SESSION_FILE_PATH
- settings.SESSION_SAVE_EVERY_REQUEST
- settings.SESSION_SERIALIZER
- settings.SHORT_DATETIME_FORMAT
- settings.SHORT_DATE_FORMAT
- settings.SIGNING_BACKEND
- settings.SILENCED_SYSTEM_CHECKS
- settings.SITE_ID
- settings.STATICFILES_DIRS
- settings.STATICFILES_FINDERS
- settings.STATICFILES_STORAGE
- settings.STATIC_ROOT
- settings.STATIC_URL
- settings.TEMPLATES
- settings.TEST_NON_SERIALIZED_APPS
- settings.TEST_RUNNER
- settings.THOUSAND_SEPARATOR
- settings.TIME_FORMAT
- settings.TIME_INPUT_FORMATS
- settings.USE_DEPRECATED_PYTZ
- settings.USE_I18N
- settings.USE_L10N
- settings.USE_THOUSAND_SEPARATOR
- settings.USE_TZ
- settings.USE_X_FORWARDED_HOST
- settings.USE_X_FORWARDED_PORT
- settings.WSGI_APPLICATION
- settings.X_FRAME_OPTIONS
- settings.YEAR_MONTH_FORMAT
shortcuts
- shortcuts.get_list_or_404()
- shortcuts.get_object_or_404()
- shortcuts.redirect()
- shortcuts.render()
sitemaps
- sitemaps.GenericSitemap
- sitemaps.Sitemap
- sitemaps.Sitemap.alternates
- sitemaps.Sitemap.changefreq
- sitemaps.Sitemap.get_latest_lastmod()
- sitemaps.Sitemap.i18n
- sitemaps.Sitemap.items
- sitemaps.Sitemap.languages
- sitemaps.Sitemap.lastmod
- sitemaps.Sitemap.limit
- sitemaps.Sitemap.location
- sitemaps.Sitemap.paginator
- sitemaps.Sitemap.priority
- sitemaps.Sitemap.protocol
- sitemaps.Sitemap.x_default
- sitemaps.ping_google()
- sitemaps.views.index()
- sitemaps.views.sitemap()
sites
- sites.managers.CurrentSiteManager
- sites.models.Site
- sites.models.Site.domain
- sites.models.Site.name
- sites.requests.RequestSite
- sites.requests.RequestSite.__init__()
- sites.shortcuts.get_current_site()
- sites.middleware.CurrentSiteMiddleware
staticfiles
- staticfiles.storage.ManifestFilesMixin
- staticfiles.storage.ManifestStaticFilesStorage
- staticfiles.storage.ManifestStaticFilesStorage.file_hash()
- staticfiles.storage.ManifestStaticFilesStorage.manifest_strict
- staticfiles.storage.ManifestStaticFilesStorage.max_post_process_passes
- staticfiles.storage.StaticFilesStorage
- staticfiles.storage.StaticFilesStorage.post_process()
- staticfiles.testing.StaticLiveServerTestCase
- staticfiles.urls.staticfiles_urlpatterns()
- staticfiles.views.serve()
template
- template.Library.filter()
- template.Library.inclusion_tag()
- template.Library.simple_tag()
- template.defaultfilters.stringfilter()
- template.response.SimpleTemplateResponse
- template.response.SimpleTemplateResponse.__init__()
- template.response.SimpleTemplateResponse.add_post_render_callback()
- template.response.SimpleTemplateResponse.context_data
- template.response.SimpleTemplateResponse.is_rendered
- template.response.SimpleTemplateResponse.render()
- template.response.SimpleTemplateResponse.rendered_content
- template.response.SimpleTemplateResponse.resolve_context()
- template.response.SimpleTemplateResponse.resolve_template()
- template.response.SimpleTemplateResponse.template_name
- template.response.TemplateResponse
- template.response.TemplateResponse.__init__()
- template.Context
- template.Context.flatten()
- template.Context.get()
- template.Context.pop()
- template.Context.push()
- template.Context.setdefault()
- template.Context.update()
- template.Engine
- template.Engine.from_string()
- template.Engine.get_default()
- template.Engine.get_template()
- template.Engine.select_template()
- template.RequestContext
- template.Template
- template.Template.render()
- template.base.Origin
- template.base.Origin.loader
- template.base.Origin.name
- template.base.Origin.template_name
- template.context_processors.debug()
- template.context_processors.i18n()
- template.context_processors.static()
- template.context_processors.tz()
- template.loaders.app_directories.Loader
- template.loaders.base.Loader
- template.loaders.base.Loader.get_contents()
- template.loaders.base.Loader.get_template()
- template.loaders.base.Loader.get_template_sources()
- template.loaders.cached.Loader
- template.loaders.filesystem.Loader
- template.loaders.locmem.Loader
- template.backends.base.Template.render()
- template.backends.django.DjangoTemplates
- template.backends.jinja2.Jinja2
- template.loader.engines
- template.loader.get_template()
- template.loader.render_to_string()
- template.loader.select_template()
test
- test.signals.setting_changed
- test.signals.template_rendered
- test.RequestFactory
- test.TransactionTestCase.available_apps
- test.TransactionTestCase.reset_sequences
- test.runner.DiscoverRunner
- test.runner.DiscoverRunner.add_arguments()
- test.runner.DiscoverRunner.build_suite()
- test.runner.DiscoverRunner.get_test_runner_kwargs()
- test.runner.DiscoverRunner.log()
- test.runner.DiscoverRunner.run_checks()
- test.runner.DiscoverRunner.run_suite()
- test.runner.DiscoverRunner.run_tests()
- test.runner.DiscoverRunner.setup_databases()
- test.runner.DiscoverRunner.setup_test_environment()
- test.runner.DiscoverRunner.suite_result()
- test.runner.DiscoverRunner.teardown_databases()
- test.runner.DiscoverRunner.teardown_test_environment()
- test.runner.DiscoverRunner.test_loader
- test.runner.DiscoverRunner.test_runner
- test.runner.DiscoverRunner.test_suite
- test.utils.setup_databases()
- test.utils.setup_test_environment()
- test.utils.teardown_databases()
- test.utils.teardown_test_environment()
- test.Client
- test.Client.cookies
- test.Client.delete()
- test.Client.force_login()
- test.Client.get()
- test.Client.head()
- test.Client.login()
- test.Client.logout()
- test.Client.options()
- test.Client.patch()
- test.Client.post()
- test.Client.put()
- test.Client.session
- test.Client.trace()
- test.LiveServerTestCase
- test.Response
- test.Response.client
- test.Response.content
- test.Response.context
- test.Response.exc_info
- test.Response.json()
- test.Response.request
- test.Response.resolver_match
- test.Response.status_code
- test.Response.templates
- test.Response.wsgi_request
- test.SimpleTestCase
- test.SimpleTestCase.assertContains()
- test.SimpleTestCase.assertFieldOutput()
- test.SimpleTestCase.assertFormError()
- test.SimpleTestCase.assertFormsetError()
- test.SimpleTestCase.assertHTMLEqual()
- test.SimpleTestCase.assertHTMLNotEqual()
- test.SimpleTestCase.assertInHTML()
- test.SimpleTestCase.assertJSONEqual()
- test.SimpleTestCase.assertJSONNotEqual()
- test.SimpleTestCase.assertNotContains()
- test.SimpleTestCase.assertRaisesMessage()
- test.SimpleTestCase.assertRedirects()
- test.SimpleTestCase.assertTemplateNotUsed()
- test.SimpleTestCase.assertTemplateUsed()
- test.SimpleTestCase.assertURLEqual()
- test.SimpleTestCase.assertWarnsMessage()
- test.SimpleTestCase.assertXMLEqual()
- test.SimpleTestCase.assertXMLNotEqual()
- test.SimpleTestCase.client
- test.SimpleTestCase.client_class
- test.SimpleTestCase.databases
- test.SimpleTestCase.modify_settings()
- test.SimpleTestCase.settings()
- test.TestCase
- test.TestCase.captureOnCommitCallbacks()
- test.TestCase.databases
- test.TestCase.setUpTestData()
- test.TransactionTestCase
- test.TransactionTestCase.assertNumQueries()
- test.TransactionTestCase.assertQuerysetEqual()
- test.TransactionTestCase.databases
- test.TransactionTestCase.fixtures
- test.modify_settings()
- test.override_settings()
- test.skipIfDBFeature()
- test.skipUnlessDBFeature()
- test.utils.isolate_apps()
urls
- urls.ResolverMatch
- urls.ResolverMatch.app_name
- urls.ResolverMatch.app_names
- urls.ResolverMatch.args
- urls.ResolverMatch.captured_kwargs
- urls.ResolverMatch.extra_kwargs
- urls.ResolverMatch.func
- urls.ResolverMatch.kwargs
- urls.ResolverMatch.namespace
- urls.ResolverMatch.namespaces
- urls.ResolverMatch.route
- urls.ResolverMatch.tried
- urls.ResolverMatch.url_name
- urls.ResolverMatch.view_name
- urls.get_script_prefix()
- urls.resolve()
- urls.reverse()
- urls.reverse_lazy()
- urls.include()
- urls.path()
- urls.re_path()
- urls.register_converter()
utils
- utils.log.AdminEmailHandler
- utils.log.AdminEmailHandler.send_mail()
- utils.log.CallbackFilter
- utils.log.RequireDebugFalse
- utils.log.RequireDebugTrue
- utils.cache.add_never_cache_headers()
- utils.cache.get_cache_key()
- utils.cache.get_max_age()
- utils.cache.learn_cache_key()
- utils.cache.patch_cache_control()
- utils.cache.patch_response_headers()
- utils.cache.patch_vary_headers()
- utils.dateparse.parse_date()
- utils.dateparse.parse_datetime()
- utils.dateparse.parse_duration()
- utils.dateparse.parse_time()
- utils.decorators.async_only_middleware()
- utils.decorators.decorator_from_middleware()
- utils.decorators.decorator_from_middleware_with_args()
- utils.decorators.method_decorator()
- utils.decorators.sync_and_async_middleware()
- utils.decorators.sync_only_middleware()
- utils.encoding.escape_uri_path()
- utils.encoding.filepath_to_uri()
- utils.encoding.force_bytes()
- utils.encoding.force_str()
- utils.encoding.iri_to_uri()
- utils.encoding.is_protected_type()
- utils.encoding.smart_bytes()
- utils.encoding.smart_str()
- utils.encoding.uri_to_iri()
- utils.feedgenerator.Atom1Feed
- utils.feedgenerator.Enclosure
- utils.feedgenerator.Rss201rev2Feed
- utils.feedgenerator.RssFeed
- utils.feedgenerator.RssUserland091Feed
- utils.feedgenerator.SyndicationFeed
- utils.feedgenerator.SyndicationFeed.__init__()
- utils.feedgenerator.SyndicationFeed.add_item()
- utils.feedgenerator.SyndicationFeed.add_item_elements()
- utils.feedgenerator.SyndicationFeed.add_root_elements()
- utils.feedgenerator.SyndicationFeed.item_attributes()
- utils.feedgenerator.SyndicationFeed.latest_post_date()
- utils.feedgenerator.SyndicationFeed.num_items()
- utils.feedgenerator.SyndicationFeed.root_attributes()
- utils.feedgenerator.SyndicationFeed.write()
- utils.feedgenerator.SyndicationFeed.writeString()
- utils.feedgenerator.get_tag_uri()
- utils.functional.cached_property
- utils.functional.classproperty
- utils.functional.keep_lazy()
- utils.functional.keep_lazy_text()
- utils.html.conditional_escape()
- utils.html.escape()
- utils.html.format_html()
- utils.html.format_html_join()
- utils.html.html_safe()
- utils.html.json_script()
- utils.html.strip_tags()
- utils.http.base36_to_int()
- utils.http.http_date()
- utils.http.int_to_base36()
- utils.http.urlencode()
- utils.http.urlsafe_base64_decode()
- utils.http.urlsafe_base64_encode()
- utils.module_loading.import_string()
- utils.safestring.SafeString
- utils.safestring.mark_safe()
- utils.text.format_lazy()
- utils.text.slugify()
- utils.timezone.activate()
- utils.timezone.deactivate()
- utils.timezone.get_current_timezone()
- utils.timezone.get_current_timezone_name()
- utils.timezone.get_default_timezone()
- utils.timezone.get_default_timezone_name()
- utils.timezone.get_fixed_timezone()
- utils.timezone.is_aware()
- utils.timezone.is_naive()
- utils.timezone.localdate()
- utils.timezone.localtime()
- utils.timezone.make_aware()
- utils.timezone.make_naive()
- utils.timezone.now()
- utils.timezone.override()
- utils.timezone.utc
- utils.translation.activate()
- utils.translation.check_for_language()
- utils.translation.deactivate()
- utils.translation.deactivate_all()
- utils.translation.get_language()
- utils.translation.get_language_bidi()
- utils.translation.get_language_from_request()
- utils.translation.get_supported_language_variant()
- utils.translation.gettext()
- utils.translation.gettext_lazy()
- utils.translation.gettext_noop()
- utils.translation.ngettext()
- utils.translation.ngettext_lazy()
- utils.translation.npgettext()
- utils.translation.npgettext_lazy()
- utils.translation.override()
- utils.translation.pgettext()
- utils.translation.pgettext_lazy()
- utils.translation.templatize()
- utils.translation.to_locale()
- utils.deprecation.MiddlewareMixin
- utils.translation.get_language_info()
views
- views.debug.ExceptionReporter
- views.debug.ExceptionReporter.get_traceback_data()
- views.debug.ExceptionReporter.get_traceback_html()
- views.debug.ExceptionReporter.get_traceback_text()
- views.debug.ExceptionReporter.html_template_path
- views.debug.ExceptionReporter.text_template_path
- views.debug.SafeExceptionReporterFilter
- views.debug.SafeExceptionReporterFilter.cleansed_substitute
- views.debug.SafeExceptionReporterFilter.get_post_parameters()
- views.debug.SafeExceptionReporterFilter.get_traceback_frame_variables()
- views.debug.SafeExceptionReporterFilter.hidden_settings
- views.debug.SafeExceptionReporterFilter.is_active()
- views.decorators.debug.sensitive_post_parameters()
- views.decorators.debug.sensitive_variables()
- views.generic.base.RedirectView
- views.generic.base.RedirectView.get_redirect_url()
- views.generic.base.RedirectView.pattern_name
- views.generic.base.RedirectView.permanent
- views.generic.base.RedirectView.query_string
- views.generic.base.RedirectView.url
- views.generic.base.TemplateView
- views.generic.base.View
- views.generic.base.View.as_view()
- views.generic.base.View.dispatch()
- views.generic.base.View.http_method_names
- views.generic.base.View.http_method_not_allowed()
- views.generic.base.View.options()
- views.generic.base.View.setup()
- views.generic.dates.ArchiveIndexView
- views.generic.dates.BaseArchiveIndexView
- views.generic.dates.BaseDateDetailView
- views.generic.dates.BaseDayArchiveView
- views.generic.dates.BaseMonthArchiveView
- views.generic.dates.BaseTodayArchiveView
- views.generic.dates.BaseWeekArchiveView
- views.generic.dates.BaseYearArchiveView
- views.generic.dates.DateDetailView
- views.generic.dates.DayArchiveView
- views.generic.dates.MonthArchiveView
- views.generic.dates.TodayArchiveView
- views.generic.dates.WeekArchiveView
- views.generic.dates.YearArchiveView
- views.generic.dates.YearArchiveView.get_make_object_list()
- views.generic.dates.YearArchiveView.make_object_list
- views.generic.detail.BaseDetailView
- views.generic.detail.BaseDetailView.get()
- views.generic.detail.DetailView
- views.generic.list.BaseListView
- views.generic.list.BaseListView.get()
- views.generic.list.ListView
- views.generic.edit.BaseCreateView
- views.generic.edit.BaseCreateView.get()
- views.generic.edit.BaseCreateView.post()
- views.generic.edit.BaseDeleteView
- views.generic.edit.BaseFormView
- views.generic.edit.BaseUpdateView
- views.generic.edit.BaseUpdateView.get()
- views.generic.edit.BaseUpdateView.post()
- views.generic.edit.CreateView
- views.generic.edit.CreateView.object
- views.generic.edit.CreateView.template_name_suffix
- views.generic.edit.DeleteView
- views.generic.edit.DeleteView.form_class
- views.generic.edit.DeleteView.template_name_suffix
- views.generic.edit.FormView
- views.generic.edit.UpdateView
- views.generic.edit.UpdateView.object
- views.generic.edit.UpdateView.template_name_suffix
- views.generic.dates.BaseDateListView
- views.generic.dates.BaseDateListView.allow_empty
- views.generic.dates.BaseDateListView.date_list_period
- views.generic.dates.BaseDateListView.get_date_list()
- views.generic.dates.BaseDateListView.get_date_list_period()
- views.generic.dates.BaseDateListView.get_dated_items()
- views.generic.dates.BaseDateListView.get_dated_queryset()
- views.generic.dates.DateMixin
- views.generic.dates.DateMixin.allow_future
- views.generic.dates.DateMixin.date_field
- views.generic.dates.DateMixin.get_allow_future()
- views.generic.dates.DateMixin.get_date_field()
- views.generic.dates.DayMixin
- views.generic.dates.DayMixin.day
- views.generic.dates.DayMixin.day_format
- views.generic.dates.DayMixin.get_day()
- views.generic.dates.DayMixin.get_day_format()
- views.generic.dates.DayMixin.get_next_day()
- views.generic.dates.DayMixin.get_previous_day()
- views.generic.dates.MonthMixin
- views.generic.dates.MonthMixin.get_month()
- views.generic.dates.MonthMixin.get_month_format()
- views.generic.dates.MonthMixin.get_next_month()
- views.generic.dates.MonthMixin.get_previous_month()
- views.generic.dates.MonthMixin.month
- views.generic.dates.MonthMixin.month_format
- views.generic.dates.WeekMixin
- views.generic.dates.WeekMixin.get_next_week()
- views.generic.dates.WeekMixin.get_prev_week()
- views.generic.dates.WeekMixin.get_week()
- views.generic.dates.WeekMixin.get_week_format()
- views.generic.dates.WeekMixin.week
- views.generic.dates.WeekMixin.week_format
- views.generic.dates.YearMixin
- views.generic.dates.YearMixin.get_next_year()
- views.generic.dates.YearMixin.get_previous_year()
- views.generic.dates.YearMixin.get_year()
- views.generic.dates.YearMixin.get_year_format()
- views.generic.dates.YearMixin.year
- views.generic.dates.YearMixin.year_format
- views.generic.edit.DeletionMixin
- views.generic.edit.DeletionMixin.delete()
- views.generic.edit.DeletionMixin.get_success_url()
- views.generic.edit.DeletionMixin.success_url
- views.generic.edit.FormMixin
- views.generic.edit.FormMixin.form_class
- views.generic.edit.FormMixin.form_invalid()
- views.generic.edit.FormMixin.form_valid()
- views.generic.edit.FormMixin.get_context_data()
- views.generic.edit.FormMixin.get_form()
- views.generic.edit.FormMixin.get_form_class()
- views.generic.edit.FormMixin.get_form_kwargs()
- views.generic.edit.FormMixin.get_initial()
- views.generic.edit.FormMixin.get_prefix()
- views.generic.edit.FormMixin.get_success_url()
- views.generic.edit.FormMixin.initial
- views.generic.edit.FormMixin.prefix
- views.generic.edit.FormMixin.success_url
- views.generic.edit.ModelFormMixin
- views.generic.edit.ModelFormMixin.fields
- views.generic.edit.ModelFormMixin.form_invalid()
- views.generic.edit.ModelFormMixin.form_valid()
- views.generic.edit.ModelFormMixin.get_form_class()
- views.generic.edit.ModelFormMixin.get_form_kwargs()
- views.generic.edit.ModelFormMixin.get_success_url()
- views.generic.edit.ModelFormMixin.model
- views.generic.edit.ModelFormMixin.success_url
- views.generic.edit.ProcessFormView
- views.generic.edit.ProcessFormView.get()
- views.generic.edit.ProcessFormView.post()
- views.generic.edit.ProcessFormView.put()
- views.generic.list.MultipleObjectMixin
- views.generic.list.MultipleObjectMixin.allow_empty
- views.generic.list.MultipleObjectMixin.context_object_name
- views.generic.list.MultipleObjectMixin.get_allow_empty()
- views.generic.list.MultipleObjectMixin.get_context_data()
- views.generic.list.MultipleObjectMixin.get_context_object_name()
- views.generic.list.MultipleObjectMixin.get_ordering()
- views.generic.list.MultipleObjectMixin.get_paginate_by()
- views.generic.list.MultipleObjectMixin.get_paginate_orphans()
- views.generic.list.MultipleObjectMixin.get_paginator()
- views.generic.list.MultipleObjectMixin.get_queryset()
- views.generic.list.MultipleObjectMixin.model
- views.generic.list.MultipleObjectMixin.ordering
- views.generic.list.MultipleObjectMixin.page_kwarg
- views.generic.list.MultipleObjectMixin.paginate_by
- views.generic.list.MultipleObjectMixin.paginate_orphans
- views.generic.list.MultipleObjectMixin.paginate_queryset()
- views.generic.list.MultipleObjectMixin.paginator_class
- views.generic.list.MultipleObjectMixin.queryset
- views.generic.list.MultipleObjectTemplateResponseMixin
- views.generic.list.MultipleObjectTemplateResponseMixin.get_template_names()
- views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix
- views.generic.base.ContextMixin
- views.generic.base.ContextMixin.extra_context
- views.generic.base.ContextMixin.get_context_data()
- views.generic.base.TemplateResponseMixin
- views.generic.base.TemplateResponseMixin.content_type
- views.generic.base.TemplateResponseMixin.get_template_names()
- views.generic.base.TemplateResponseMixin.render_to_response()
- views.generic.base.TemplateResponseMixin.response_class
- views.generic.base.TemplateResponseMixin.template_engine
- views.generic.base.TemplateResponseMixin.template_name
- views.generic.detail.SingleObjectMixin
- views.generic.detail.SingleObjectMixin.context_object_name
- views.generic.detail.SingleObjectMixin.get_context_data()
- views.generic.detail.SingleObjectMixin.get_context_object_name()
- views.generic.detail.SingleObjectMixin.get_object()
- views.generic.detail.SingleObjectMixin.get_queryset()
- views.generic.detail.SingleObjectMixin.get_slug_field()
- views.generic.detail.SingleObjectMixin.model
- views.generic.detail.SingleObjectMixin.pk_url_kwarg
- views.generic.detail.SingleObjectMixin.query_pk_and_slug
- views.generic.detail.SingleObjectMixin.queryset
- views.generic.detail.SingleObjectMixin.slug_field
- views.generic.detail.SingleObjectMixin.slug_url_kwarg
- views.generic.detail.SingleObjectTemplateResponseMixin
- views.generic.detail.SingleObjectTemplateResponseMixin.get_template_names()
- views.generic.detail.SingleObjectTemplateResponseMixin.template_name_field
- views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix
- views.decorators.csrf.csrf_exempt()
- views.decorators.csrf.csrf_protect()
- views.decorators.csrf.ensure_csrf_cookie()
- views.decorators.csrf.requires_csrf_token()
- views.defaults.bad_request()
- views.defaults.page_not_found()
- views.defaults.permission_denied()
- views.defaults.server_error()
- views.static.serve()
- views.decorators.cache.cache_page()
- views.decorators.cache.cache_control()
- views.decorators.cache.never_cache()
- views.decorators.common.no_append_slash()
- views.decorators.gzip.gzip_page()
- views.decorators.http.condition()
- views.decorators.http.etag()
- views.decorators.http.last_modified()
- views.decorators.http.require_GET()
- views.decorators.http.require_POST()
- views.decorators.http.require_http_methods()
- views.decorators.http.require_safe()
- views.decorators.vary.vary_on_cookie()
- views.decorators.vary.vary_on_headers()
- views.i18n.JSONCatalog
- views.i18n.JavaScriptCatalog
- views.i18n.JavaScriptCatalog.domain
- views.i18n.JavaScriptCatalog.packages
- views.i18n.set_language()
Intro
- Пишите свой первый патч для Джанго.
- Getting started
- Краткое руководство по установке
- Джанго с первого взгляда
- Продвинутое учебное пособие:Как писать многоразовые приложения
- Написание твоего первого приложения «Джанго»,часть 1.
- Написание твоего первого приложения «Джанго»,часть 2.
- Написание твоего первого приложения «Джанго»,часть 3.
- Написание твоего первого приложения «Джанго»,часть 4.
- Написание твоего первого приложения «Джанго»,часть 5.
- Написание твоего первого приложения «Джанго»,часть 6.
- Написание твоего первого приложения «Джанго»,часть 7.
Работаем с джанго-формами. Часть 1
Работаем с джанго-формами. Часть 2
Работаем с джанго-формами. Часть 3
Работаем с джанго-формами. Часть 4
Работаем с джанго-формами. Часть 5
Привет!
Чтобы лучше усвоить работу с джанго-формами, реализуем небольшой проект.
Легенда такая. Вы — начинающий бэкендер, которого подключили к проекту создания сайта для заказа космических путешествий.
Часть бэка уже написана, есть фронт главной страницы. Как раз на этой стадии подключаетесь вы.
Вместе пройдём по выполнению этой задачи, будем проговаривать каждую деталь, к счастью, спешить некуда.
Важный момент 1: материал устроен так, что ничего (по крайней мере сейчас, у себя на машине запускать не нужно). Весь необходимый код я опишу здесь же.
В идеале, всё должно быть понятно из текста.
Если нет, пожалуйста, пишите в комментариях вопросы/пожелания/замечания, буду дорабатывать. Если всё пойдёт по плану, то получится прекрасное подспорье для будущих бэкендеров, а мечта именно такая.
Важный момент 2: этот мини-проект я делаю, по сути, сейчас вместе с вами, поэтому полной заготовки на гитхабе нет.
По мере прохождения и обсуждения проекта будет расти и код проекта в репозитории.
Репозиторий проекта
Важный момент 3: проект сугубо учебный. Он вряд ли ощутимо пополнит ваше портфолио для потенциальных работодателей, но за то ощутимо прокачает в понимании джанго и максимально осознанном (это главное!) применении инструментов фреймворка.
Поехали!
Работаем с джанго-формами. Часть 1
Какова общая логика сайта?
Пока все очень скромно:
- клиент открывает главную страницу
- оттуда переходит на страницу с формой заказа. Там он должен выбрать:
- корабль, на котором хочет полететь
- дату полета (не раньше чем через месяц после текущей даты)
- выбрать валюту платежа (пока доступны только доллары США и российские рубли)
- после выбора всех указанных опций он кликает на кнопку «Оформить». Важная деталь: если все места на выбранный корабль и выбранную дату исчерпаны, должно появляться информация об этом с просьбой перезаполнить форму.
- если всё прошло штатно, то клиенту должна открыться следующая страница для завершения оформления заказа:
- должна отображаться стоимость полета, исходя из ранее введененных данных.
Сейчас стоимость определена в долларах, поэтому если клиент выбрал в качестве валюты рубли, надо конвертировать стоимость в рубли и показать ее по курсу на сегодня (разумеется, с сайта ЦБ).
Но в базу все равно должно сохраниться значение в долларах. - введет свои имя и фамилию
- адрес электронной почты
- нажимает на кнопку «Поехали!», после чего заказ сохраняется в базе данных.
- должна отображаться стоимость полета, исходя из ранее введененных данных.
Что есть сейчас?
- описаны модели
- подключена админка
- база заполнена данными о кораблях
- открывается главная страница, в ссылке на страницу перехода к форме оформления заказа пока стоит заглушка.
С чего начинаем?
Нужна вот такая форма для получения данных
Необходимые для формы данные нужно получать из БД (кроме валюты платежа).
Шаг 1. Создать страницу, на которой будет располагатся форма выбора корабля, даты полета и валюты платежа
В конце концов, мы же где-то должны показать эту форму
Что нужно:
- решить, куда положить файл с новой страницей
- создать html-файл с минимально необходимой разметкой
- на странице сделать заготовку для формы
1.1. Решить, куда положить шаблон с новой страницей
Джанго ищет шаблоны страниц не рандомно, а во вполне конкретных местах.
Что это за места оговоривают в настройках проекта — settings.py
, константа TEMPLATES
.
Смотрю, что там:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ BASE_DIR / 'templates' ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Собственно, интересуют два ключа: APP_DIRS
и DIRS
.
'APP_DIRS': True
. Ага, значит, шаблоны можно хранить в папкаx templates
внутри приложений проекта.
'DIRS': [ BASE_DIR / 'templates' ]
. Так, ещё можно поместить шаблоны страницу в папку templates
внутри BASE_DIR
.
Поищем в настройках, что за путь у BASE_DIR
(внимание: это дефолтный путь в последних версиях джанго, поэтому понимание, как это расшифровывается, пригодится везде):
from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent
pathlib
— это стандартный (т.е. не требующий pip install ...
) Python
-модуль для работы с файловой системой, не важно unix
или win
.
Если нужно с помощью питона порыться среди своих папок и файлов, что-то создать или удалить, задать пути к тем или иным файлам, то тут либо pathlib
, либо более старый (но по-прежнему актуальный) вариант с os.path
(os
— ещё одна стандартная Python
-библиотека).
Итак, что значит Path(__file__).resolve().parent.parent
__file__
— это текущий файл, в нашем случае settings.py
Path(__file__).resolve()
— построить путь к текущему файлу. В моем случае это
C:Desktopform_webinarconfigsettings.py
Чтобы более нагляднее было
form_webinar
|
config
| ├── __init__.py
| ├── asgi.py
| ├── settings.py
| ├── urls.py
| └── wsgi.py
└──media
| ├── ...
└──spaceflights
| ├── ...
└──manage.py
.parent.parent
— буквально означает «из текущей директории (=директории, которая является родительской для settings.py
) нужно подняться в следующую директорию и взять ее путь»
Таки образом, путем для BASE_DIR
будет C:Desktopform_webinar
, т.е. та папка, в которой у нас лежит весь проект, та папка, из которой мы вызываем manage.py
.
А BASE_DIR / 'templates'
— это не что иное, как C:Desktopform_webinartemplates
.
Итог. Я могу разместить шаблон страницы с формой заказа, как в папке templates
внутри папок с приложениям проекта (а тут оно всего одно, назвали его spaceflights
).
А могу разместить в общей папке templates
в корне проекта, джанго туда тоже заглянет.
Положу в общую папку, проект маленький, да и главную страницу туда же положили. Назову новый шаблон ship.html
.
1.2. Cоздать html-файл с минимально необходимой разметкой
Итак, файл создан.
Теперь нужно описать минимум разметки — теги html
, head
, body
.
В VS Сode достаточно набрать в html-файле восклицательный знак (Emmet Abbreviation
), появится предложение воспользоваться аббревиатурой и приняв это предложение появится необходимая разметка
и даже чуть больше
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> </html>
Всё наше внимание на теге body
, то, что в нем, пользователь увидит на странице в браузере.
1.3. На странице сделать заготовку для формы
Скелет любой html
-формы — это тег form
.
Создадим его внутри body
.
<body> <form> </form> </body>
Теперь нужно ответить на три вопроса:
- После сабмита (проще говоря, нажатия кнопки типа «Отправить»), какой контроллер («вьюха», «вью-функция») будет приводится в действие (работать с данными из формы и т.д.)?
- Какой
url
обслуживает этот контроллер (потому что задействовать мы его можем не иначе, как через обращение по урлу)? - Каким http-методом —
GET
илиPOST
— будут отправляться данные из формы?
Ответы на первые два вопроса нам нужны для описания атрибута actions
в теге form
. Атрибут actions
, конечно, можно опустить, но тогда будет задействован тот же контроллер, что и отрендерил текущую страницу с формой.
Нам это не подходит, ибо ТЗ звучит так
если всё прошло штатно, то клиенту должна открыться следующая страница для завершения оформления заказа
Должен работать следующий контроллер, который будет брать данные из текущей формы и оперировать с ними в следующей форме, которая откроется на следующей странице.
Но пока такого контроллера нет (а, значит, и маршрута). Чтобы вообще не забыть об actions
укажем actions=""
, потом допишем новый маршрут, когда сделаем новый контроллер.
Что касается третьего вопроса, то данные из формы будем передавать POST
-запросом.
Итог
<body> <!-- дописать action, когда появится контроллер следующей формы --> <form action="" method="POST"> </form> </body>
Шаг 2. Сделать заготовку контроллера, который будет рендерить страницу с формой
Собственно говоря, для этого у нас всё есть (точнее одно-единственное — шаблон страницы, который мы положили в правильное место).
Как я уже говорил, вся логика проекта в одном приложении, spaceflights
. Идём в views.py
оттуда и создаём там контроллер.
def flight_details(request): return render(request, template_name='ship.html')
Так, что тут происходит:
- контроллер принимает на вход http-запрос
- возвращает http-ответ, который несет с собой строку со всей разметкой из файла
ship.html
- браузер считывает эту строку и отображает пользователю страницу, составленную из этой разметки
Напомню, что render
из django.shortcuts
, это сахарок или шорткат, функция, которая упаковывает несколько более простых действий, позволяя сократить количество кода.
Круто знать о шорткатах и писать мало кода, но ещё круче хорошо понимать, что за ними стоит.
Не используя render
переписать код можно так:
from django.http import HttpResponse from django.template import loader def flight_details(request): template = loader.get_template('ship.html') return HttpResponse(template.render(request=request))
Кода на строчку больше, но для понимания фундамента всё гораздо яснее.
А фундамент в том, что любой контроллер в ответ на request
(объект джанго-класса HttpRequest
), должен возвращать объект джанго-класса HttpResponse
или его наследников.
В данном случае объекту HttpResponse
мы передаем шаблон, который был предварительно превращен в строку загрузчиком шаблонов. И с этой строкой дальше работает браузер.
Шаг 3. Привязать контроллер к маршруту (url) и починить ссылку с главной страницы
Чтобы заработал контроллер и что-нибудь вернул, к нему нужно обратиться по специально для этого контроллера предусмотренному адресу.
Т.е. складывается следующая цепочка: url-адрес
-> контроллер
-> отображение через контроллер шаблона страницы
.
Мы начали с конца, т.к. без шаблона невозможен (в нашем случае) контроллер, а без контроллера маршрут.
Все маршруты приложения описываются в urls.py
. Добавим туда новый:
# spaceflights/urls.py urlpatterns = [ ... path('flight/', flight_details, name='flight_details'), ]
Три аргумента для path
:
- сам маршрут (получится
http://домен/flight
) - вью-функция, которая обслуживает маршрут
- описательное имя маршрута, чтобы потом не хардкодить ссылки на него, а пользоваться именем
Маршрут готов, его можно набирать в адресной строке браузера, но это не наш путь.
Читаем снова ТЗ:
открывается главная страница, в ссылке на страницу перехода к форме оформления заказа пока стоит заглушка.
Уберем заглушку, чтобы с главной страницы пользователь по ссылке попадал на страницу с нашей формой:
Было:
<h2> <a href="#"> Летим! </a> </h2>
Cтало:
<h2> <a href="{% url 'flight_details' %}"> Летим! </a> </h2>
Нам помог шаблонный тег {% url %}
, мы передали ему имя маршрута из urls.py
. и джанго уже сам превратит его в читаемый для браузера путь. Никакого харкода.
Но если бы захотелось бы похардкодить, то написали бы так
<a href="flight/">
Итого: подготовительный этап завершен. С главной страницы мы попадаем на отдельную страницу, которая пока пуста, но на ней есть заготовка формы.
Шаг 4. Начинаем создавать элементы формы с помощью Django
Сейчас на html
-страницы наша форма обозначена так:
<!-- дописать action, когда появится контроллер следующей формы -->
<form action="" method="POST">
</form>
Внутри формы нужно создать поля (теги input
) разных типов (type=...
), которые будут принимать данные.
Прежде чем писать html-разметку для полей самостоятельно, максимально задействуем возможности джанго. Будем управлять разметкой формы с бэкенда.
Начальный шаг для создания джанго-формы (задача которой: а) взять на себя создание части разметки на странице, б) проверить поступившие из html-формы даннные), всегда один и тот же: нужно создать python-класс у нашей формы и унаследоваться от класс джанго-форм.
Принято формы размещать отдельно от контроллеров, поэтому создадим внутри приложения spaceflights
новый файл forms.py
Объявим класс джанго-формы, предварительно сделав необходимый импорт:
from django import forms class OrderForm(forms.Form): ...
...
это Python-объект ellipsis
, который можно использовать аналогично заглушке pass
(для самых любопытных короткая статья).
Круто, класс формы мы объявили, пока ни одного атрибута (поля) в форме нет.
Приступим к их созданию, руководствуясь ТЗ.
4.1 Первое поле: радиокнопки с выбором корабля
Нам нужно ответить, как минимум, на следующие вопросы (и так для каждого поля создаваемой нами Django-формы):
- Для какого поля в какой модели предназначены данные из конкретного поля
html
-формы? - Как должно называться джанго-поле при рендеринге на
html
-страницe? - Какой класс джанго-формы наиболее подходит для того, чтобы отрендерить поле в
html
-форме и проверить поступившие через него данные? - Подходит ли нам дефолтный виджет (иначе говоря `<input type=»такой-то»…>), который использует подобранное нами поле джанго-формы?
Отвечаем на 1-й вопрос
Вся html
-форма собирает данные, которые нужны для модели Order
class Order(models.Model): ship = models.ForeignKey(to=Spaceship, on_delete=models.CASCADE, verbose_name='корабль') passenger = models.CharField( max_length=10, verbose_name='пассажир', blank=True ) flight_date = models.DateField(verbose_name='дата полета')
Конкретно через поле html
-формы «На чем летим» передаются данные для поля ship
модели Order
.
Что мы из этого вынесли
Поле в джанго-форме должно называться ship
, т.к. именно это название попадет в качестве атрибута name
в тег input
html-формы.
Т.е. наши радиокнопки будет с тегами типа <input type=... name="ship">
. Это важно, т.к. словаре с данными из POST-запроса
ключами будут как раз name
‘ы.
Конечно, можно как угодно назвать поле формы, но гораздо удобнее, когда не нужно каждый раз думать, по какому ключу искать данные из пришедшего запроса для конкретного поля модели.
Отвечаем на 2-й вопрос
При рендеринге поле должно называться На чем летим
. За название любого поля джанго-формы отвечает атрибут label
.
Если его явно не указать при объявлении поля, то лэйблом будет название поля как атрибута класса нашей джанго-формы, только с большой буквы.
Т.е. лэйблом будет Ship
. Но нас это не устраивает.
Пруф из исходного кода джанго (модули forms.boundfield
и forms.utils
):
if self.field.label is None: self.label = pretty_name(name) ... def pretty_name(name): """Convert 'first_name' to 'First name'.""" if not name: return '' return name.replace('_', ' ').capitalize()
Что мы из этого вынесли
При объявлении класса поля надо явно передать ему label="На чем летим"
.
Отвечаем на 3-й вопрос
Какой класс джанго-формы наиболее подходит для того, чтобы отрендерить поле в html
-форме и проверить поступившие через него данные?
В html
-поле формы от пользователя требуется сделать выбор из нескольких вариантов, значит, среди классов полей джанго-форм нам нужно что-то с choices
Идём смотреть классы встроенных полей
Возможные кандидаты:
- forms.ChoiceField
- forms.MultipleChoiceField
- forms.ModelChoiceField
- forms.ModelMultipleChoiceField
Поля с multiple
в названии заворачиваем сразу, перед пользователем стоит не множественный, а единственный выбор.
forms.ChoiceField
нам тоже не подходит, т.к. на вход принимает самостоятельно нами созданный кортеж из двухэлементных кортежей (один элемент будет value
поля, а второй будет рендерится на экране).
Нам не нужно ничего создавать, корабли берутся из связанной с моделью Order
модели Spaceship
.
В итоге остается поле ModelChoiceField
. Почитаем о нем внимательнее в документации:
Позволяет выбор единственного объекта модели, имеет смысл при отображении внешнего ключа.
Класс, ровно то, что нам нужно, поле ship
модели Order
как раз поле с внешним ключом (связь один-ко-многим).
Какие у нас обязательные аргументы для этого поля? Он один queryset
. Логично, ведь нужно же откуда-то брать значения для полей, из которых будет выбирать пользователь.
Единственный обязательный аргумент:
queryset
A QuerySet of model objects from which the choices for the field are derived and which is used to validate the user’s selection. It’s evaluated when the form is rendered.
Что мы из этого вынесли
Теперь внутри нашей джанго-формы мы можем объявить поле, все исходные данные у нас есть:
class OrderForm(forms.Form): ship = forms.ModelChoiceField(queryset=Spaceship.objects, label="На чем летим")
Отвечаем на 4-й вопрос
Подходит ли нам дефолтный виджет (иначе говоря `<input type=»такой-то»…>), который использует подобранное нами поле джанго-формы?
Виджет — это объект питоновского класса, который встраивается в поле джанго-формы и отвечает в нем за то, какой конкретно html
-разметкой будет рендерится поле в шаблоне.
Поэтому, если вы хотели вводить цифры, а перед вами календарь, тут заслуга виджета (ну и разумеется поля джанго-формы, которое этот виджет использовало).
У каждого поля джанго-формы есть дефолтный виджет (что это за виджет написано в описании поля в документации). Но мы всегда можем указать наш собственный виджет.
Итак, у нас по дефолту select
. А селекты и радиокнопки, как говорят в Одессе, это две большие разницы.
Заглянем в классы виджетов, которые доступны из коробки.
Название нужного нам виджета говорит само за себя: RadioSelect
.
Аналогично Select, но отображается в виде списка радио кнопок
Чтобы переопределить виджет поля, класс нового виджета нужно передать в параметре widget
.
В итоге код нашего поля выглядит так:
class OrderForm(forms.Form): ship = forms.ModelChoiceField( queryset=Spaceship.objects, label='На чем летим', widget=RadioSelect # переопределили виджет )
А отрендерено в шаблоне поле будет вот так:
<ul id="id_ship"> <li> <label for="id_ship_0"> <input type="radio" name="ship" value="1" required id="id_ship_0"> Crew Dragon </label> </li> <li> <label for="id_ship_1"> <input type="radio" name="ship" value="2" required id="id_ship_1"> New Shepard </label> </li> <li> <label for="id_ship_2"> <input type="radio" name="ship" value="3" required id="id_ship_2"> VSS Unity </label> </li> </ul>
Всё как и обещал джанго — радиокнопки (<input type="radio"...>
), обернутые в маркированный список (теги ul
, li
).
Уже ощутимый результат.
Теперь нам нужно избавиться от черных маркеров (а может и вообще от тегов списка), а еще рядом с радиокнопками выводить релевантные картинки (те, которые привязаны в базе к конкретному кораблю).
Этим мы займемся в следующий раз.
Остановка, чтобы осмотреться под капотом
НЕ обязательный раздел, можно почитать, если есть время. Цель — посмотреть, как устроено создание форм под капотом.
Сейчас код формы такой:
class OrderForm(forms.Form): ship = forms.ModelChoiceField(queryset=Spaceship.objects, label='На чем летим', widget=RadioSelect)
Посмотрим на класс forms.Form
:
class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass): "A collection of Fields, plus their associated data."
Собственно здесь всё, больше кода (кроме нескольких комментариев после докстринга) нет.
Поэтому идём в родительский класс BaseForm
, а также в метакласс DeclarativeFieldsMetaclass
.
Как джанго отделяет среди атрибутов класса формы поля от других атрибутов и как у объекта формы появляется атрибут fields?
Чтобы было более понятно, о чем речь, добавлю еще один атрибут в класс нашей формы, атрибут field_order
, это список полей, указывающий, в каком порядке их рендерить.
class OrderForm(forms.Form): ship = forms.ModelChoiceField(queryset=Spaceship.objects, label='На чем летим', widget=RadioSelect) field_order = ['ship',]
Вспоминаем основы ООП:
- Всё в Python — объекты.
- Классы сами по себе — объекты.
Привыкаешь всегда смотреть на классы только в формате класс()
, т.е. когда объект класса вызывается и порождает другой объект — экземпляр класса.
Но давайте не будем создавать пока экземпляр класса нашей формы. А посмотрим, как создается сам класс формы.
За создание любого объекта в Python
, в том числе класса, отвечает метод __new__
. Чтобы переопределить поведение __new__
именно для классов-как-объектов, используют метаклассы. Собственно, это мы и видели в сигнатуре класса forms.Form
— на второй позиции указан метакласс: metaclass=DeclarativeFieldsMetaclass
.
Из описания метода __new__
этого метакласса мы и поймем, что делает джанго с атрибутами класса формы при создании этого класса (повторюсь — самого класса, не его экземпляра).
- cильно погружаться в этот исходный код не нужно, важные штуки я выделил комментариями (мои на русском, на английском — это комменты разработчиков джанго)
def __new__(mcs, name, bases, attrs): # Collect fields from current class. current_fields = [] for key, value in list(attrs.items()): # запускается цикл по всем парам "ключ-значение" атрибутов, которыми наделен класс # в нашем случае атрибута два: `ship` и `order_field`. if isinstance(value, Field): # вот момент истины: оценивается, относится значение в атрибуте к класcу `Field`, классу полей джанго-форм current_fields.append((key, value)) # если относится, то атрибут пара "ключ-значение" заносится в список `current_fields` attrs.pop(key) # а сам атрибут поля исключается (словарный метод `pop`) из числа атрибутов класса, т.е. теперь `OrderForm.ship` НЕ сработает attrs['declared_fields'] = dict(current_fields) # зато у `OrderForm` появляется новый атрибут `declared_fields` и в нем как раз доступно поле `ship`. new_class = super().__new__(mcs, name, bases, attrs) # Walk through the MRO. declared_fields = {} for base in reversed(new_class.__mro__): # Collect fields from base class. if hasattr(base, 'declared_fields'): declared_fields.update(base.declared_fields) # Field shadowing. for attr, value in base.__dict__.items(): if value is None and attr in declared_fields: declared_fields.pop(attr) # Итог создания класса нашей формы как самостоятельного объекта - два атрибута класса `base_fields` и `declared_fields`, в которых доступны именно те атрибуты класса формы, которые являются полями. new_class.base_fields = declared_fields new_class.declared_fields = declared_fields
Проверим. Возьмем класс нашей формы как отдельный объект (т.е. вызывать его, создавать другой объект — экземпляр класса, не будем).
order_form_class_as_obj = OrderForm # к этому моменту `__new__` уже отработал, а значит, `OrderForm.ship`, должен вызвать ошибку. order_form_class_as_obj.ship ...AttributeError: type object 'OrderForm' has no attribute 'ship' # действительно, атрибут `ship` исчез... #...но должны появиться `declared_fields` и `base_fields` и там `ship` будет order_form_class_as_obj.base_fields # {'ship': <django.forms.models.ModelChoiceField object at 0x000001B27B3FD1F0>} order_form_class_as_obj.declared_fields # {'ship': <django.forms.models.ModelChoiceField object at 0x000001B27B3FD1F0>} # всё работает, как и должно. Появились новые атрибуты-словари, и наше поле там. Ключ: название поля, значение: объект поля. # а вот атрибут `field_order`, раз не относится к классу `Field` (и его наследникам), должен быть по-прежнему доступен. order_form_class_as_obj.field_order # всё работает, возвращается список [<django.forms.models.ModelChoiceField object at 0x000001DE9F4BF2B0>]
Продолжение выложу здесь в понедельник, 29 ноября, а в среду, 1 декабря в 19.00 по мск продолжим обсуждать джанго-формы на вебинаре.
Что почитать, освежить (но ни в коем случае не застревать, если что-то будет непонятно)
- html-тег
form
- Атрибуты html-тега
input
- Как джанго ищет шаблоны
- Как работает шаблонный тег
url
- Как джанго сопоставляет маршруты с контроллерами
- Атрибут
label
поля джанго-формы - Виджет
RadioSelect
поля джанго-формы - Как устроены метаклассы в Python (русский перевод изумительной статьи с RealPython)
Работаем с джанго-формами. Часть 2
4.2 Дефолтный рендеринг формы не устраивает. Нужно более гибкое управление отображением
4.2.1. По каким правилам рендерится форма целиком и что можно изменить
Сейчас на странице наша форма выглядит так
А в шаблоне так
<form actions="" method="POST"> {% csrf_token %} {{ space_form }} </form>
В шаблонной переменной space_form
находится экземпляр нашей формы OrderForm
(мы передали его в словаре context
через контроллер).
Когда объект формы целиком размещается в верстке (как в нашем случае), у объекта формы срабатывает метод __str__
(отвечает за строковое представление объекта), который, в свою очередь,
вызывает метод формы as_table
.
Вот как это выглядит под капотом:
#django.forms.forms.BaseForm class BaseForm: ... def __str__(self): return self.as_table() def as_table(self): "Return this form rendered as HTML <tr>s -- excluding the <table></table>." return self._html_output( normal_row='<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', error_row='<tr><td colspan="2">%s</td></tr>', row_ender='</td></tr>', help_text_html='<br><span class="helptext">%s</span>', errors_on_separate_row=False, )
Из описания метода as_table
видно, что он вызывает метод _html_output
, передавая ему в качестве аргументов html-разметку для отображения поля, сообщений об ошибках, вспомогательного текста для поля и т.д.
Собственно, вот так и происходит превращение шаблонной переменной с объектом формы в полноценную html-разметку.
as_table
оборачивает каждое поле формы в строку таблицы с невидимыми границами. Благодаря этому достигается выравнивание формы и каждое поле располагается строго под предыдущим.
Важный для понимания момент: за отображение html-тегов
<input>
и<label>
для конкретного поля отвечает виджет этого поля. А вот методыas_...
самой формы уже оборачивают теги каждого отдельного поля (оборачивают=заключают внутрь некой оболочки, оболочкой выступают например теги<p></p>
), в итоге получается матрешка из html-разметки.
Помимо as_table
, который срабатывает по умолчанию, есть методы as_p
и as_ul
, которые отличаются тем, в какой html-тег оборачивается каждое поле.
Если указать {{ space_form.as_p }}
, каждое поле формы будет обернутов в абзацный тег <p></p>
. Если указать {{ space_form.as_ul }}
, каждое поле формы будет обернутов в списочный тег <li></li>
.
Визуально результаты каждого из методов рендеринга фактически не будут отличаться, разве что при as_ul
рядом с лейблом поля появится маркер списка
Фактически рендерить всю форму целиком из переменной с ее объектом не очень практично — верстка, которая заложена под капотом, достаточно топорна и вряд ли пригодится дальше чернового варианта страницы.
Для более гибкого управления рендерингом формы в шаблоне можно обращаться к каждому полю по отдельности.
4.2.2. Как перейти к управлению отображением каждого поля в отдельности
Есть два варианта.
Вариант 1.
Запустить цикл по объекту формы. Объект формы итерабелен, об этом нам говорит наличие методов __iter__
и __getitem__
под капотом
(кстати, вопрос о том, что представляют из себя итераторы, итерабельные объекты в Python и как реализовать протокол итератора, частый вопрос на собеседованиях).
class BaseForm: ... def __iter__(self): for name in self.fields: yield self[name] def __getitem__(self, name): """Return a BoundField with the given name.""" try: field = self.fields[name] ...
При запуске цикла на каждой итерации будет отдаваться конкретное поле (в том порядке, в котором они перечислены в классе формы либо в атрибуте формы field_order
).
Выглядеть это будет так:
<form actions="" method="POST"> {% csrf_token %} <!-- запускаем цикл через специальные шаблонные теги --> {% for field in space_form %} {{ field }} <!-- теперь у нас новая переменная в шаблоне, в которой "сидит" конкретное поле и через точку можно обращаться к различным его атрибутам --> {% endfor %} </form>
Запуск цикла по полям формы особенно удобен, когда каждое поле мы оборачиваем в одну и ту же верстку.
Тогда вместо ее дублирования для каждого поля, можно доверить это циклу.
<form actions="" method="POST"> {% csrf_token %} {% for field in space_form %} <div class=...> <!-- каждое поле будет обернуто html-тег div одного и того же класса, в классе описываем нужные css-стили --> {{ field }} </div> {% endfor %} </form>
Важный момент: если вдруг страница отобразилась не в том виде, в каком вы ожидали, проверьте расположение между собой html-тегов и шаблонных тегов, управляющих разметкой.
Например, не выпал ли закрывающий html-тег из цикла (оказался под, а не над{% endfor %}
). Или, наоборот, в цикл попали лишние html-теги, которые вы и не собирались повторять.
Вариант 2.
Взять напрямую интересующее поле, указав через точку его имя (имя атрибута в классе вашей формы). Очень частое выражение в Python
, да и других языках, точечная нотация
(dot notation
). Ничего сверхъестственного это за собой не несет, это доступ к атрибуту объекта с использованием точки.
В нашем случае до поля выбора кораблей мы доберемся так {{ space_form.ship }}
.
Перед тем, как продолжить работу с полем, нужно остановиться для прояснения принципиально важного момента — что такое класс BoundField
.
4.2.3 Field и BoundField
Когда мы объявлем поле формы, например, так
ship = forms.ModelChoiceField(queryset=Spaceship.objects, label='На чем летим', widget=RadioSelect)
мы создаем объект конкретного поля, который, в конечном итоге, через цепочку наследования, всегда является объектом класса Field
(forms.ModelChoiceField
наследник forms.Field
, forms.CharField
наследник forms.Field
и т.д.).
Независимо от непосредственного родителя, у полей всегда есть атрибуты из конструктора базового класса Field
.
Заглянем под капот джанго:
class Field: ... def __init__(self, *, required=True, widget=None, label=None, initial=None, help_text='', error_messages=None, show_hidden_initial=False, validators=(), localize=False, disabled=False, label_suffix=None)
У поля, например, будет атрибут .required
, от значения которого зависит, пропустит ли is_valid
отсутствие данных для поля или нет.
Есть атрибут label
, о нем мы уже говорили выше, есть initial
, куда можно передать начальное значение (value) поля и т.д.
Чтобы в python
-коде (например, в forms.py
или в коде контроллера), добраться до поля, нужно обратиться к атрибуту fields
формы. fields
— это словарь, поэтому далее используем квадратные скобки, внутри ключ — название поля. Так мы добираемся к объекту поля.
Например, если в переменной space_form
находится экземпляр формы класса OrderForm
, то к объекту поля ship
мы доберемся так
space_form.fields['ship']
. И далее через точечную нотацию мы уже можем работать с различными атрибутами поля.
Промежуточный итог:
- все поля формы получают атрибуты класса
Field
и его наследников (классов, отвечающих за поля конкретного типа) - все поля формы помещаются в словарь в атрибуте
fields
формы, доступ к ним такой `объект_формы.fields[‘название_поля_формы’]
К полю формы в python
-коде можно добраться ещё одним способом: объект_формы['название_поля']
. Но — внимание — так мы получим не поле непосредственно, а объект класса BoundField
.
На заметку. Когда мы любой
python
-объект (совсем не обязательно словарь) пишем в форматеобъект['какая_то_строка']
, т.е. применяем квадратные скобки (square brackets notation
), то вызывается метод__getitem__
, определенный в классе этого объекта. Именно он отвечает за то, что будет происходить дальше. Если метод внутри класса не определен, то поднимется исключениеTypeError: 'название_класса' object is not subscriptable
.
Иными словами, если вы вдруг получаете ошибкуis not subscriptable
, значит, вы не к тому объекту приставили квадратные скобки.
Объекты джанго-формsubscriptable
, к ним можно применить квадратные скобки. При указании в качестве ключа названия поля, под капотом сработаетfield.get_bound_field(self, name)
.
Т.е. вернется объект классаBoundField
.
BoundField
— это класс обертка над объектом нашего поля, т.е. объектом класса Field
.
Поскольку BoundField
— это обертка, то через нее, с помощью атрибута field
можно добраться до объекта нашего поля.
Т.е. space_form.fields['ship']
и space_form['ship'].field
приведут к одному и тому же — к объекту ModelChoiceField
, наследнику Field
.
У BoundField
есть свои собственные атрибуты, которые позволяют не пробираться к field
, а сразу вытащить какие-то из его атрибутов.
Например, у BoundField
есть атрибут help_text
. Такой же атрибут есть и у Field
, в нем мы можем привести какой-то поясняющий текст для поля формы и отобразить его рядом с полем.
Соответственно, обращение к space_form.fields['ship'].help_text
и space_form['ship'].field.help_text
дадут один и тот же результат.
Однако у BoundField
есть далеко не все атрибуты Field
.
Например, чтобы проверить, обязательно поле или нет, нельзя использовать space_form.fields['ship'].required
, потому что атрибута required
у BoundField
нет.
И теперь самое главное 😄: почему это всё так важно? Потому что в шаблоне страницы при цикле по полям формы или при доступе к полю напрямую используется как раз BoundField.
Поэтому, в {{ space_form.ship.какой_то_атрибут }}
в качестве «какого_то_атрибута» можно выбрать только то, что перечислено в атрибутах BoundField,
потому что {{ space_form.ship }}
в шаблоне — это то же самое, что space_form['ship']
в python
-коде.
Если же через шаблонную переменную с полем нужно добраться до атрибутов из класса Field
, которых нет в BoundField
, того же required
, то нужно использовать переходник в виде атрибута field
, т.е. будет так {{ объект_формы.название_поля_формы.field.атрибут }}
.
Попробуйте в шаблон страницы вставить {{ space_form.ship.field.required }}
.
<body> <div class='container'> <h2>Заказ полета</h2> {{ space_form.ship.field.required }} <!-- добрались до атрибута required класса Field через атрибут field класса BoundField --> </div> </body>
Результат:
На странице мы увидели значение атрибута required
— True
. Поле обязательно.
Вот один из многих вариантов того, как это может пригодится на практике — по-разному стилизовать обязательные и не обязательные поля при рендеринге формы.
Отмечу, что if
можно было бы вставить непосредственно в атрибут class
, но для большей иллюстративности я сделал два разных div
.
<form actions="" method="POST"> {% csrf_token %} {% for field in some_form %} <!-- цикл по любой джанго-форме --> {% if field.field.required %} <div class='класс_с_особым_стилем_для_ОБЯЗАТЕЛЬНЫХ_полей'> {{ field }} </div> {% else %} <div class='класс_с_особым_стилем_для_НЕобязательных_полей'> {{ field }} </div> {% endif %} {% endfor %} </form>
4.2.4. Рендерим поле ship отдельно
Обратимся в шаблоне к полю напрямую.
<form actions="" method="POST"> {% csrf_token %} {{ space_form.ship }} </form>
Что поменялось? В верстке исчезла обертка в виде табличных тегов (которые раньше давал form.as_table()
), но это незаметно.
Зато видно, что исчез лейбл поля, нет На чем летим
.
И здесь нужно запомнить важное правило: при непосредственном управлении полем в разметке нужно учитывать, что оно не монолитно.
Оно состоит из целого ряда элементов: само поле (например, поле для ввода текста), лейбл поля, вспомогательный текст (help text), текст ошибки, которые вернет бэкенд после проверки поля и т.д.
Всё это нужно самостоятельно указывать в шаблоне, используя атрибуты BoundField`.
У BoundField
есть атрибут label
,
через него мы получаем доступ к лэйблу поля. Укажем лэйбл в шаблоне
<form actions="" method="POST"> {% csrf_token %} {{ space_form.ship.label }} <!-- label, один из атрибутов BoundField --> {{ space_form.ship }} </form>
Результат
Лэйбл вернулся.
4.2.5 Особенности полей с выбором одного или нескольких предложенных значений
Почему никуда не исчезали названия радиокнопок? Это особенность виджета RadioSelect
и других виджетов, предполагающих выбор из заранее отрендеренных значений (чекбоксы, селекты).
За рендеринг каждой отдельной радиокнопки отвечает «подвиджет» (subwidget
) и в качестве лейблов они используют названия объектов из переданного при создании поля набора записей.
Иными словами, через BoundField.label
мы управляем лейблом всего набора радиокнопок (label
всего поля ship), а не названиями каждой радиокнопки в отдельности.
Можно ли управлять отдельными виджетами (собственно говоря, каждым конкретным инпутом-радиокнопкой)? Да, можно.
Дело в том, что объект класса BoundField
(а именно с ними мы работаем в шаблоне), итерабельны, т.е. по ним можно пройтись циклом.
Но что именно будет перебирать цикл? Смотри на реализацию метода __iter__
у BoundField
def __iter__(self): return iter(self.subwidgets)
Перебираться будут как раз сабвиджеты, поэтому запускать цикл есть смысл только тогда, когда поле использует мультивиджет (радиокнопки, чекбоксы и т.п.).
Каждый сабвиджет (в нашем случае каждая радиокнопка) относится к классу BoundWidget
и содержит, в частности, такие атрибуты и методы:
tag
— сама кнопка (чек-бокс и т.п.), без подписиchoice_label
— подпись к кнопке (чек-боксу и т.п.)id_for_label
— айдишник радиокнопки (в частности, нужен, чтобы связать надпись с конкретной кнопкой)
4.2.6 Берем управление отображением поля выбора кораблей полностью в свои руки
Теперь мы можем полностью избавиться от дефолтной обертки радиокнопок тегами li
(а значит, назойливыми черными буллитами списка), и обернуть кнопки и подписи к ним во что захотим.
Попробуем такой пример
<form actions="" method="POST"> {% csrf_token %} <!-- название всего поля (всей группы радиокнопок)--> {{ space_form.ship.label }} <!-- запускаем цикл по полю == перебираем все радиокнопки --> {% for radiobutton in space_form.ship %} {{ radiobutton }} {% endfor %} </form>
Смотрим, что получилось
Исчезли черные кружки возле каждой радиокнопки, а сами кнопки расположились не в столбик, а в ряд. О чем это говорит?
О том, что дефолтная обертка радиокнопок в теги html
-списка исчезла. Мы взяли вопрос рендеринга каждой кнопки под личный контроль и
джанго отдаёт только разметку для <input>
и для <label>
.
Вот как сейчас выглядит разметка группы радиокнопок — просто подряд идущие лейблы и инпуты, никаких оберток
<!-- название всего поля (всей группы радиокнопок)--> На чем летим <!-- запускаем цикл по полю == перебираем все радиокнопки --> <label for="id_ship_0"> <input type="radio" name="ship" value="1" id="id_ship_0" required> Crew Dragon </label> <label for="id_ship_1"> <input type="radio" name="ship" value="2" id="id_ship_1" required> New Shepard </label> <label for="id_ship_2"> <input type="radio" name="ship" value="3" id="id_ship_2" required> VSS Unity </label>
Сравните эту разметку, что была при рендеринге поля ship
целиком.
Если интересно, почему кнопки расположились в ряд, почитайте о делении html-элементов на строчные и блочные.
Как уже отмечал, можно пойти ещё дальше и управлять отдельно отображением самой кнопки и подписью к ней.
Давайте попробуем этот инструмент: оформим подпись к каждой кнопке текстом красного цвета.
<!-- запускаем цикл по полю == перебираем все радиокнопки --> {% for radiobutton in space_form.ship %} <!-- рендерим саму кнопку --> {{ radiobutton.tag }} <!-- рендерим подпись, переопределив тег label, чтобы добавить новый стиль --> <label for="{{ radiobutton.id_for_label }}" style="color: red;"> {{ radiobutton.choice_label }} </label> {% endfor %}
Что мы сделали:
- запустили цикл по радиокнопкам (цикл по
subwidgets
) - сначала отобразили в разметке саму кнопку
- потом дописали собственный тег
label
, в который добавили css-свойствоcolor
со значениемred
(делает текст красным) - в
label
мы также добавили айдишник радиокнопки, чтобы связать подпись и кнопку - отобразили подпись.
Результат
Смысл тега лейбл с айдишником лишь в том, чтобы пользователь мог кликать не только в саму кнопку, чтобы ее выбрать, но и по тексту рядом с ней.
Продолжение выложу здесь в пятницу, 3 декабря, а в среду, 1 декабря в 19.00 по мск продолжим обсуждать джанго-формы на вебинаре.
Что почитать (освежить в памяти)
- Протокол итерации в Python
- Атрибуты объектов класса
BoundField
- Шаблонный тег for
- Шаблонный тег if
- Цикл по полям джанго-формы, полезные атрибуты
- От чего защищают csrf-токены
- Деление html-элементов на строчные и блочные
- html-тег input type=»radio»
- html-тег label
Работаем с джанго-формами. Часть 3
4.3. Прикручиваем картинки к радиокнопкам
Нам нужно, чтобы рядом с каждой радиокнопкой выбора корабля появлялась картинка, привязанная к записе об этом корабле.
4.3.1. Посмотрим детально, как работает ModelChoiceField
Что сейчас из себя представляет поле spac_ship
в нашей джанго-форме?
Это поле класса ModelChoiceField
. Логика этого поля такова:
- взять набор записей из базы;
- у каждой записи взять уникальный идентификатор (по умолчаню это первичный ключ (
id
,pk
)); - отобразить в
html
поле, предполагающее выбор из нескольких значений — радиокнопки, чекбоксы, выпадающий список; - каждому составному элементу html-поля (если, например, поле в виде радиокнопок, то каждой радиокнопке) присваивается атрибут
value
, значением
которого является тот самый уникальный идентификатор записи.
Пример: набор записей в БД состоит из 3 записей с id 1, 2, 3 соответственно.
Если этот набор передать в ModelChoiceField
с виджетом RadioSelect
, то на странице появится три input type="radio"
с value
1, 2 и 3 соответственно.
Если пользователь выберет радиокнопку, за которой стоит value=1
, значит, на бэкенде мы будем обращаться к соответствующей таблице в БД к записи с id
1.
Ещё больше усилим пример
Заглянем в БД, в таблицу Spaceship
. Чтобы открыть базу sqlite
, я воспользуюсь очень удобным (да-да, звучит, как в рекламе) браузером DB Browser for SQLite
.
Если ещё не установили, сделайте это, куда удобнее смотреть базу через браузер, чем через консоль. Вот ссылка — https://sqlitebrowser.org/dl/.
У нас три записи: id 1 — корабль Crew Dragon
, id 2 — корабль New Shepard
, id 3 — корабль VSS Unity
.
Теперь посмотрим, как мы создавали поле ship
в джанго-форме:
ship = forms.ModelChoiceField( queryset=Spaceship.objects, label='На чем летим', widget=RadioSelect )
В параметр queryset
мы передали Spaceship.objects
, что равнозначно Spaceship.objects.all()
.
Пруфы, почему равнозначно? Вот кусочек кода из-под капота поля
ModelChoiceField
:self._queryset = None if queryset is None else queryset.all()
Итак, в параметре queryset
мы передали все записи (а всего их 3) из модели Spaceship
.
Это означает, что наше поле джанго-формы настрогает ровно 3 сабвиджета <input type="radio">
.
Поскольку никакого особенного порядка сортировки мы в модели не задавали, то записи в наборе будут в порядке возрастания айдишников, значит и радиокнопки отрендерятся в том же порядке: сначала радиокнопка с value=1
, потом с value=2
и, наконец, value=3
.
Разумеется, мы можем взять в качестве queryset
не все записи для модели, а какую-то часть.
Возьмем кверисет из одной записи Spaceship.objects.filter(id=1)
(помним, что метод filter
в джанго ORM возвращает именно кверисет, даже если внутри одна запись).
ship = forms.ModelChoiceField( queryset=Spaceship.objects.filter(id=1), label='На чем летим', widget=RadioSelect )
Результат предсказуем: в верстке появится только одна радиокнопка, т.к. кверисет всего из одной записи
4.3.2. Откуда и в каком виде брать картинки кораблей
В модели Spaceship
предусмотрено поле image
:
class Spaceship(models.Model): ... image = models.ImageField(upload_to=get_upload_path, verbose_name='изображение')
Кстати, что за
get_upload_path
? Это собственная функция, которая формирует путь к сохраняемой картинке.
Благодаря этой функции картинки не валятся скопом в папкуmedia
, а для каждой картинки внутриmedia
автоматом создаётся своя подпапка
Физически загруженные картинки хранятся в директории, которую мы установили в settings.py
по ключу MEDIA_ROOT
. В нашем случае это
MEDIA_ROOT = BASE_DIR / 'media'
(о том, что скрывается за BASE_DIR
, я очень подробно писал в первой части пособия).
В таблице Spaceship
в базе данных в свою очередь хранятся адреса привязанных к записям картинок.
Для того, чтобы отобразить картинку в html-странице, нам как раз нужен ее адрес. Его мы укажем в атрибуте src
тега img
.
Добраться до адреса картинки в базе можно так: объект_записи.название_поля_для_картинок.url
.
Если, к примеру, в переменной spaceship_obj
будет запись из таблицы Spaceship
, то к адресу картинки, привязанной к этой записи,
мы доберемся так: spaceship_obj.image.url
.
4.3.3. В чем проблема
Итак, мы поняли как добраться к адресу картинки для каждой записи. Для этого нужна… да, сама запись.
Заглянем в шаблон, еще раз посмотрим, как на странице появляются радиокнопки:
{% for radiobutton in space_form.ship %}
<!-- рендерим саму кнопку -->
{{ radiobutton.tag }}
<!-- рендерим подпись, переопределив тег label, чтобы добавить новый -->
<label for="{{ radiobutton.id_for_label }}">
{{ radiobutton.choice_label }}
</label>
{% endfor %}
В качестве итерируемого объекта выступает набор сабвиджетов, очень упрощая — просто кусочков html-разметки <input type="radio"...>
Перейти от сабвиджета к объекту записи из набора, который передавался при создании ModelChoiceField
, нельзя.
Иными словами, никакого волшебного radiobutton.obj.image.url
у нас нет.
4.3.4. Решаем проблему: создаём zip-итератор в контроллере
Если сфокусироваться на текущем коде шаблона страницы, то становится понятным, что мы:
а) должны оставить имеющийся цикл, на каждой итерации которого появляется кнопка и подпись к ней
б) должны дополнить цикл еще одной переменной, за которой будет стоять объект записи из кверисета, переданного в ModelChoiceField
в) адрес картинки этой записи мы будем на каждой итерации включать в тег img
и показывать картинку рядом с кнопкой
Чтобы на каждой итерации у нас было две переменных, нам нужны два итерируемых объекта.
Один есть: это набор сабвиджетов. Тогда вторым будет… да, набор записей из модели. Тех самых, что передавались в ModelChoiceField
.
Причем нам не нужны записи целиком, достаточно адресов картинок из них.
Сделать такой вот составной итерируемый объект нам поможет стандартный питоновский итератор zip
.
Мы его создадим в контроллере и через контекст передадим в шаблон и тогда уже доработаем цикл.
Идём в контроллер (см. комментарии к новому коду в сниппете)
def flight_details(request): form = OrderForm() # собрали адреса всех картинок из кверисета ship_images = [ship.image.url for ship in form.fields['ship'].queryset] # создали zip-итератор ship_field = zip(form['ship'], ship_images) return render( request, template_name='ship.html', context={ 'space_form': form, # передали итератор в контекст шаблона в переменной ship_field 'ship_field': ship_field } )
Что мы сделали:
- Взяли набор записей, для этого мы обратились к полю
ship
черезform.fields['ship']
и дальше взяли значение атрибутаqueryset
. Это тот самыйqueryset
, который передавался полю в джанго-форме - Тем самым мы гарантировали, что мы взяли именно тот набор записей, на основе которого рендерятся радиокнопки, и именно в том порядке, в котором они будут расположены
- С помощью
list comprehension
мы прошлись по набору, взяли из каждой записи адрес картинки и составили список из этих адресов - Создали
zip
-итератор:- на первом месте поле (внимание — в данном случае мы взяли поле как объект класса
BoundField
, только он итерабелен) - за полем скрываются три радиокнопки (по числу записей в кверисете)
- на втором месте список адресов картинок (и их, разумеется тоже три, т.к. они из того же набора записей)
- поскольку источником обоих списков является один и тот же набор записей, мы уверены в том, что каждая пара «радиокнопка-картинка» будет правильной.
- на первом месте поле (внимание — в данном случае мы взяли поле как объект класса
- Передали итератор в контекст шаблона (переменная
ship_field
).
4.3.5. Решаем проблему: модифицируем цикл создания радиокнопок в шаблоне
Теперь мы запустим цикл в шаблоне не по полю ship
, а по нашему новому zip-итератору в переменной ship_field
.
Благодаря этому у нас появятся адреса записей на каждой итерации и мы можем создать тег img
для картинок.
<!-- теперь на каждой итерации у нас 2 переменных --> {% for radiobutton, img_url in ship_field %} <!-- рендерим саму кнопку --> {{ radiobutton.tag }} <!-- рендерим подпись, переопределив тег label, чтобы добавить новый --> <label for="{{ radiobutton.id_for_label }}"> {{ radiobutton.choice_label }} <!-- рендерим картинку, прикрепленную к записи из Spaceship --> <img src="{{ img_url }}" style="width: 200px; height: auto;"> </label> {% endfor %}
В теге img
помимо адреса картинки (src
), мы еще задали стилевые свойства, чтобы зафиксировать ширину каждой картинки, а высоту браузер подберет сам, чтобы сохранялись пропорции картинки.
Результат 🔥
4.4. Создаём поле выбора даты полета
К полю согласно ТЗ три требования:
- оно должно называться «Когда летим»
- выбор даты осуществляется с помощью календаря
- должны быть доступны не раньше +1 месяц от текущей даты (тут мы сейчас немного упростим и заменим условие на +4 недели, так проще будет дельту времени задать)
В первой части пособия мы подробно рассматривали алгоритм подбора поля формы, которое будет получать данные для корреспондирующего поля модели.
В данном случае мы выбираем поле forms.DateField
. Начальный код поля в джанго-форме будет выглядеть так:
flight_date = forms.DateField()
Даже не заглядывая в верстку, мы знаем, как это поле будет отображено
Дефолтным лейблом при этом будет Flight date
.
4.4.1. Задаём подпись к полю
Поменять лэйбл просто, надо явно передать нужное значение полю в параметре label
: flight_date = forms.DateField(label='Когда летим')
4.4.2. Меняем тип инпута
Разобраться с лейблом было легко, но есть куда более серьезная проблема. Виджет DateInput
рендерит обычное текстовое поле ввода.
Пользователь должен напечатать дату! Помимо того, что это выглядит, мягко говоря, архаично, так ещё и формат ввода даты должен быть строго определенным.
Читаем документацию к полю DateField
:
If no input_formats argument is provided,
the default input formats are taken from DATE_INPUT_FORMATS if USE_L10N is False,
or from the active locale format DATE_INPUT_FORMATS key if localization is enabled.
Никаких особенных форматов даты мы в настройках нашего проекта не устанавливали, но у нас установлена локализация в settings.py
(строчка USE_L10N = True
) поэтому на вход будут ожидаться следующие форматы:
DATE_INPUT_FORMATS = [ '%d.%m.%Y', # '25.10.2006' '%d.%m.%y', # '25.10.06' ]
Это дефолтные форматы даты для русской локали.
Посмотрев, какие вообще могут быть типы у html-инпутов, мы увидели, что есть такой type="date"
, который как раз отображает календарик. Ровно то, что нам нужно.
type
— это один из возможных атрибутов html-тега input
.
Управлять тегами инпутов можно через словарь attrs
виджета поля джанго-формы.
- Пробираемся к виджету. Для этого обращаемся к параметру
widget
поля и явно указываем в нем объект виджета:
flight_date = forms.DateField( label='Когда летим', widget=DateInput() )
- Теперь в параметрах виджета нужно указать
attrs
и передать этому параметру словарь с нужными нам атрибутами (парами «ключ»-«значение», которые мы хотим видеть внутри<input>
).
flight_date = forms.DateField( label='Когда летим', widget=DateInput( 'attrs': {'type': 'date'} ) )
Вот. Теперь у нас будет рендерится <input type="date"...>
. Проверим
Ура! Календарь готов.
4.4.3. Прикручиваем валидатор на бэкенде
Прежде чем перейти к решению задачи на конкретном поле, посмотрим на вопрос валидации в масштабе.
Самое главное: четко различать уровни валидации.
1. Валидация на фронте
Это валидаторы, которые включены в html-теги, либо джаваскрипт-код, который, опять же на строне браузера, может проверять вводимые пользователем данные.
Для каждого инпут-тега могут быть общие валидаторы (например, required
) и специфичные, зависящие от типа (type) конкретного инпута.
Когда видим в верстке строку типа <input type="text" required minlength="10">
, это означает, что есть поле для ввода пользователем текста, и у этого поля 2 валидатора:
- во-первых, проверяется, что поле действительно заполнено (валидатор
required
) - во-вторых, проверяется, что длина введенного текста не менее 10 символов (валидатор
minlength
)
Какие валидаторы какому типу инпута можно прикрутить, нужно читать в спецификации к этому типу, например, в русскоязочной версии MDN.
Если данные пользователя не проходят проверку фронтовыми валидаторами, то сабмит формы (нажатие условной кнопки «Отправить») не приводит к ее отправке на бэк.
Вместо этого браузер сообщает пользователю, в каких полях он ошибся.
Суперважный момент: установка валидаторов на фронте никак не влияет на бэкенд. Иными словами, если по каким-то причинам фронт пропустил невалидные данные, то на бэкенде они будут с радостью приняты.
Другой разговор, что при использовании джанго-форм, может происходить так, что у поля появляется один и тот же (по логике работы) валидатор и на фронте, и на бэкенде.
Это связано с тем, что, как мы уже говорили, джанго-форма выступает сразу в двух ролях — и как инструмент рендеринга html-разметки (а значит, можно включить в нее и фронтовые валидаторы), и как инструмент проверки данных на бэкенде.
Пример: required
. По дефолту, если поле джанго-формы создано на бэкенде с required=True
, то и на фронте оно будет отрендерено с required
в инпут теге.
Следовательно, даже если фронт пропустит пустое значение в этом поле, бэк подстрахует и вернет форму с ошибкой.
Но всё описанное, это не результат какой-то предопределенности (установил валидатор на фронте = автоматом продублировал на бэкенд), это запрограммированое поведение конкретных инструментов конкретного фреймворка.
Поэтому, если не уверены, что фреймворк за вас продублирует валидатор с фронта на бэкенд или наоборот, сделайте это сами.
2. Валидация в джанго-форме
Это валидация на бэкенде. Она независима от валидации на фронте и запускается при передаче в объект формы полученных в POST-запросе данных и вызове .is_valid()
либо .errors
(errors
запустит is_valid
).
Даже если не будет (или не сработает) валидация на фронте, то валидация через джанго-форму остановит плохие данные и пользователю можно вернуть ту же форму с указанием допущенных ошибок.
3. Валидация на уровне полей модели
Это тоже валидация на бэкенде, но пока мы ее затрагивать не будем. Но вернемся к ней, когда будем рассматривать класс ModelForm
.
4.4.4. Валидатор минимальной даты
В поле джанго-формы можно установить и валидацию на бэкенде, и валидацию на фронтенде. Начнем с первой.
Чтобы установить валидатор на бэкенде, нужно указать его в параметре validators
поля.
Важный момент. validators — это всегда список, даже если валидатор будет всего один.
flight_date = forms.DateField( label='Когда летим', # внимание: даже если валидатор один, он должен передаваться в списке validators=[], widget=DateInput( attrs={ 'type': 'date', } ) )
Нам нужен валидатор минимального значения даты (+4 недели от текущей). Для нам подойдет стандартный джанго-валидатор MinValueValidator
.
Его особенность в том, что он может принимать не только какие-то простые значения, но и функции, возвращающие значение, которое принимается за минимальное.
Далее мы напишем маленькую функцию, которая и будет возвращать нужное нам значение. Воспользуемся стандартным модулем datetime
.
def get_min_date(): """Возвращает текущую дату, увеличенную на 4 недели.""" return datetime.date.today() + datetime.timedelta(weeks=4)
Эту функцию (без вызова!) передадим в валидатор, а его в свою очередь поместим в список validators
. Получится так:
flight_date = forms.DateField( label='Когда летим', validators=[MinValueValidator(get_min_date)], ...
Теперь в какой бы день пользователь не обратился к форме, она не пропустит дату, которая меньше чем дата обращения + 4 недели.
Но об этом пользователь, увы, узнает только после сабмита формы, т.е. он будет надеяться, что ввел все правильно.
Можно, конечно, добавить к форме help text с поясненим к полю. Написать рядом с полем, какая дата будет считаться правильной.
Но есть вариант еще более удобный для пользователя — сразу скрыть из календаря даты, которые он не может выбрать. За это отвечает валидатор на фронте.
4.4.5. Прикручиваем валидатор на фронте
Мы помним: чтобы сделать валидацию на фронте, нужно указать валидатор в html-разметке, в теге input
. А ещё мы помним, что составом тегов внутри input
мы можем управлять прямо из кода джанго-формы с помощью словаря attrs
виджета поля.
Зная, что в input type="date"
за валидацию минимальной даты отвечает атрибут min
, передадим его в attrs
flight_date = forms.DateField( label='Когда летим', validators=[MinValueValidator(get_min_date)], # проверка минимальной даты на бэкенде widget=DateInput( attrs={ 'type': 'date', 'min': get_min_date # проверка минимальной даты на фронтенде } ) )
Чтобы синхронизировать наши валидаторы, мы фронту тоже передадим функцию get_min_date
. Вот что с ней произойдёт при рендеринге:
- рендер под капотом джанго увидит, что в атрибуте
min
вызываемый (callable) объект - этот объект (наша функция) будет вызван
- результат (к примеру, по состоянию на 3 декабря) —
datetime.date(2021, 12, 31)
(объектdatetime
) - в итоге будет возвращено строковое представление этого объекта (str(datetime.date(2021, 12, 31)), т.е. «2021-12-31»
- в верстку пойдёт
min="2021-12-31"
. Именно в таком (международном) форматеYYYY-MM-DD
и ожидает значение атрибутmin
тегаinput type="date"
.
Что почитать, освежить в памяти:
- 9 уровней применения функции zip
- list comprehensions за 5 минут
- html-тег img
- Поле модели ImageField
- Свойство url полей модели для загрузки файлов
- Поле джанго-формы
DateField
- Виджет
DateInput
- input type=»date»
- form.errors
- form.is_valid()
- field.validators
- MinValueValidator
- datetime.date.today
- datetime.timedelta
- Почему str к объекту даты возвращает строку именно в формате ISO 8601
Полезные инструменты:
- Страница установки DB Browser for SQlite
Работаем с джанго-формами. Часть 4
5. Создаём поле для выбора валюты платежа
Мы уже достаточно набили руку в создании полей джанго-формы, поэтому сразу в бой:
-
Назовем поле
currency
. -
Класс поля джанго-формы —
ChoiceField
. НеModelChoiceField
, а именноChoiceField
, т.к. корреспондирующего поля в модели для данного поля формы нет.
Иными словами, в модели нет такого поля, данными из которых мы бы нагенерировали опции выбора в форме (как мы это сделали для поля выбора корабля). -
Из описания поля
ChoiceField
в документации мы видим, что его стандартный виджетSelect
. А стандартный виджетSelect
, в свою очередь, рендерится в
в виде выпадающего списка опций.
<select>
<option ...>...
</select>
В рамках нашего ТЗ дефолтный виджет нас полностью устраивает, ничего переопределять не будем.
-
Поле
ChoiceField
при создании принимает аргумент для параметраchoices
. В качестве аргумента передается список или кортеж с опциями, которые будет выбирать пользователь. -
Каждая опция представлена в виде кортежа из двух элементов. Первый — что будет приходить на бэкенд, второй — что будет видеть пользователь в форме,
Для нашей ситуации choices
будет таким
(
('RUB', 'Российский рубль'),
('USD', 'Доллар США')
)
В выпадающем списке на странице в браузере пользователь будет видеть Российский рубль
и Доллар США
, а на бэкенд в составе POST-запроса будет приходить либо пара
"currency": "RUB"
, либо пара "currency": "USD"
.
- Согласно ТЗ в форме на странице поле должно называться «Валюта платежа», поэтому передадим соответствующую строку для параметра
label
поля.
В итоге код поля в джанго-форме будет таким
currency = forms.ChoiceField( choices=( ('RUB', 'Российский рубль'), ('USD', 'Доллар США') ), label='Валюта платежа' )
Добавим его в шаблон html-страницы
<div style="margin-top: 30px;"> {{ space_form.currency.label }} {{ space_form.currency }} </div>
Отрендерится поля вот так:
Полный код формы теперь такой:
def get_min_date(): """Возвращает текущую дату, увеличенную на 4 недели.""" return datetime.date.today() + datetime.timedelta(weeks=4) class OrderForm(forms.ModelForm): ship = forms.ModelChoiceField( label='На чем летим', queryset=Spaceship.objects, widget=RadioSelect, ) flight_date = forms.DateField( label='Когда летим', validators=[MinValueValidator(get_min_date)], widget=DateInput( attrs={ 'type': 'date', 'min': get_min_date } ) ) currency = forms.ChoiceField( label='Валюта платежа', choices = ( ('RUB', 'Российский рубль'), ('USD', 'Доллар США') ) )
Прежде чем двигаться дальше прочитайте каждую строчку кода и убедитесь, что вы на 100% понимаете эти строчки, например:
- что такое
ModelChoiceField
? - для чего нужно явно указывать
widget=RadioSelect
? - а вообще, что такое виджет?
- в чем отличие валидатора в строке
validators=[MinValueValidator(get_min_date)],
от валидатора в строке'min': get_min_date
- и т.д.
Если возникают трудности, лучше перечитать материал выше. Потому что цель учебы не в том, чтобы быстро проскочить по материалу. Это ничего не даст.
Цель — учиться писать код осмысленно, на 100% понимая, что вы хотите сказать или что хотел сказать тот, чей код вы читаете (а на практике читать чужой код придется куда чаще, чем писать собственный).
6. Рефакторим джанго-форму: используем класс ModelForm
6.1. Минимальный набор кода для создания модельной формы
До текущего момента мы создавали форму на основе джанго класса Form
, наследника BaseForm
.
У BaseForm
есть еще один класс-наследник — класс ModelForm
.
Он полезен, когда поля формы используются для получения данных, на основе которых будет формироваться запись для БД.
Иными словами, когда можно выстроить цепочку поле формы -> поле модели
.
У нас как раз такой случай — 2 из 3 полей формы можно сопоставить с полями модели Order
:
- поле джанго-формы
ship
-> полеship
моделиOrder
- поле джанго-формы
flight_date
-> полеflight_date
моделиOrder
.
Создание формы на базе класса ModelForm
позволяет нам доверить создание полей формы самому джанго.
В минимальном варианте требуется следующее:
- прописать внутри класса формы класс
Meta
- в классе
Meta
два атрибута:model
— модель, которую обслуживает джанго-формаfields
— список (или кортеж) полей модели (важно: именно модели, не формы), для которых джанго автоматически создаст поля в нашей форме.
6.2. Что стоит за «магией» создания модельной формы
Вот что представляет из себя модельная форма, если бы мы описали поля самостоятельно через «обычную» форму.
Модельная форма "Обычная" форма -------------------------------------------|---------------------------------------- class OrderForm(forms.ModelForm): |class OrderForm(forms.Form): | ship = forms.ModelChoiceField(label='Корабль', queryset=Spaceship.objects.all()) class Meta: | flight_date = forms.DateField(label='Дата полета') model = Order | fields = ['ship', 'flight_date'] |
Вот эту «магию» по превращению 'ship
из метаопции fields
в ship = forms.ModelChoiceField(label='Корабль', queryset=Order.objects.all())
джанго делает за нас.
Разберем, как он это делает:
- Джанго идёт в модель
Order
и смотрит, какой класс у поляship
этой модели. Видит, что классForeignKey
- Джанго смотрит «карту» соответствия классов полей модели классам полей формы. Видит, что классу поля модели
ForeignKey
соответствует класс поля формыModelChoiceField
. - Поскольку поле
ship
моделиOrder
через внешний ключ связано с модельюSpaceship
, то атрибутомqueryset
дляModelChoiceField
становитсяSpaceship.objects.all()
. - Также джанго видит, что у поля
ship
естьverbose_name='корабль'
, т.е. человекочитаемое название, его он берет в качестве лейбла для поля формы. - В качестве названия самого поля джанго берет то же название, что и поле модели, т.е.
ship
.
Так и рождается поле джанго-формы ship = forms.ModelChoiceField(label='Корабль', queryset=Spaceship.objects.all())
.
С полем flight_date
происходят точно такие же манипуляции.
6.3. Как быть с полем currency
, его же нет в модели Order
Создание модельной формы (формы-наследника класса ModelForm
) не исключает возможности объявлять поля формы самостоятельно, а не поручать это джанго.
Это делается за пределами внутреннего класса Meta
ровно также, как мы это делали, собирая «обычную» джанго-форму.
class OrderForm(forms.ModelForm): currency = forms.ChoiceField( choices = ( ('RUB', 'Российский рубль'), ('USD', 'Доллар США') ), label='Валюта платежа' ) class Meta: model = Order fields = ['ship', 'flight_date']
По сути, мы сказали «Джанго, создай за нас поля формы ship
и flight_date
на основе одноименных полей модели Order
, а ещё в форме будет поле currency
, его создание мы полностью берем на себя».
Что произойдёт, если указать currency
в fields
внутри Meta
? Тут возможны два сценария:
- Поле
currency
явно объявлено внеMeta
(так, как сейчас). Тогда ничего не произойдёт, джанго проигнорирует наличиеcurrency
вfields
. - Поле
currency
не объявлено явно. Тогда джанго пойдёт в модельOrder
искать полеcurrency
, не найдёт его там и вы увидите ошибку
django.core.exceptions.FieldError: Unknown field(s) (currency) specified for Order
Отсюда вывод: указывать в fields
названия полей, которых нет в модели, как минимум, бессмысленно, а как максимум всё сломает.
6.4. Автоматически созданные поля не устраивают. Что делать?
Да-да, доверив создание полей ship
и flight_date
полностью джанго мы сталкиваемся с теми же проблемами, которые решали, когда объявляли поля самостоятельно:
- Не устраивает дефолтный виджет
select
для поляModelChoiceField
, нужны радиокнопки, а не выпадающий список опций. - Поле для даты опять не календарик, а окошко для ввода даты текстом, плюс никаких валидаторов на фронте
- Дефолтные подписи к полям не подходят.
Иными словами, джанго создал
ship = forms.ModelChoiceField(label='Корабль', queryset=Spaceship.objects.all())
а нам-то нужно
ship = forms.ModelChoiceField(label='На чем летим', queryset=Spaceship.objects, widget=RadioSelect)
т.е. требуется переопределить label
и widget
.
Вариант 1
Для того, чтобы переопределить некоторые (но не все!) атрибуты полей в модельной форме, в Meta
есть набор опций:
labels
— для переопределения подписей к полямhelp_texts
— для указания поясняющего текста к полям, который потом можно отрендерить рядом с полемwidgets
— для переопределения виджетов полейfield_classes
— для переопределения класса поля формы (если не устраивает дефолтное сопоставление полей или поле кастомного класса)error_messages
— для переопределения сообщений об ошибках по их кодам
Каждый из этих атрибутов принимает словарь, в котором ключами выступают названия полей, а значениями — новые значения для label
, для help_text
или другого атрибута.
В нашем случае и для поля ship
, и для поля flight_date
нужно переопределить виджет и лейбл. Через Meta
это будет выглядеть так:
class Meta: model = Order fields = ['ship', 'flight_date',] widgets = { 'ship': RadioSelect, 'flight_date': DateInput( attrs={'type': 'date', 'min': get_min_date} ) } labels = { 'ship': 'На чем летим', 'flight_date': 'Когда летим' }
Как видим, никакой магии — лейблы и виджеты, которые мы с вами указывали при объявлении полей самостоятельно, мы просто перенесли в соответствующие атрибуты внутри Meta
.
Вариант 2
Поля ship
и flight_date
можно объявить явно, ровно так же как мы это делали в «обычной» форме.
class OrderForm(forms.ModelForm): ship = forms.ModelChoiceField( queryset=Spaceship.objects, label='На чем летим', widget=RadioSelect ) flight_date = forms.DateField( label='Когда летим', widget=DateInput( attrs={'type': 'date', 'min': get_min_date} ) ) class Meta: model = Order fields = ['ship', 'flight_date', ]
Важный момент: чтобы переопределить класс поля, виджеты, вспомогательный текст, лейблы нужно использовать или вариант 1, или вариант 2.
Смешивать эти варианты не получится. Невозможно, допустим, вне Meta
переопределить виджет конкретного поля формы, а внутри Meta
, допустим, переопределить лейбл этого же поля.
Иными словами, если начали явно описывать поле вне Meta
, то делайте это до конца.
Ещё важный момент: наверняка вы обратили внимание, что несмотря на то, что мы определили ship
и flight_date
явно, мы их оставили и в атрибуте fields
класса Meta
.
В чем смысл?
- Причина первая, банальная — без полей в
fields
мы вообще не можем создать модельную форму. Но зачем тогда использоватьModelForm
, если можно создать «обычную» форму (наследникаforms.Form
)? - Автоматическое создание полей формы — не единственная фишка
ModelForm
. Есть ещё как минимум две очень важных вещи:
- при валидации модельной формы запускаются проверки не только на уровне формы, но и на уровне модели. Если мы создадим поле
ship
в обычной форме, то, разумеется, валидаторы, которые есть для одноименного поля в модели запускаться не будут (потому что связки «модель — форма» нет). В модельной же форме будут (еслиship
будет указано вfields
внутриMeta
). - в модельной форме есть методы
save
иsave_m2m
, возможности которых значительно упрощают создание записи в БД на основе данных из полей модельной формы.
Для поля формы currency
всё остаётся так, как было написано выше, — поскольку корреспондирующего поля модели для него нет, то и в fields
его указывать бессмысленно.
Какой вариант переопределения атрибутов полей модельной формы выбрать? Ответ прост — если вам нужно переопределить что-то, для чего предусмотрены опции в Meta
(а именно виджет, вспомогательный текст, лейбл, класс поля, сообщения об ошибках), то воспользуйтесь вариантом 1. Если же опций в Meta
не хватает (например, в Meta
нет validators
), тогда переопределяйте поля из fields
явно, т.е. описывая их вне Meta
.
Что почитать, освежить в памяти:
- класс поля джанго-формы
ChoiceField
- виджет джанго-формы
Select
- как джанго подбирает класс поля формы для конкретного класса поля модели
- как переопределить атрибуты поля модельной формы и его класс
Работаем с джанго-формами. Часть 5
7. Как устроена валидация в джанго-формах
Независимо от того, какую форму мы создаём — «обычную» (наследника forms.Form
) или модельную (наследника forms.ModelForm
) — валидация на уровне формы устроена одинаково. Фишка модельной формы в том, что после валидации на уровне формы она подключает валидацию на уровне модели.
7.1. Как запустить валидацию данных в джанго-форме
Чтобы джанго-форма начала процедуру валидации нужны две вещи:
- Данные, которые нужно валидировать. Они передаются в параметре
data
при создании объекта формы.
В качестве данных для проверки мы берем данные, которые пришли с фронтенда в словареrequest.POST
.
order_form = OrderForm(data=request.POST)
- Нужно запустить валидацию. Для этого нужен экземпляр джанго-формы с переданными ему данными для проверки и вызов у него одного из двух методов (атрибутов):
.is_valid()
.errors
Т.е. order_form.is_valid()
и order_form.errors
(обратите внимание, errors
без скобок, т.к. это проперти) будут иметь один и тот же результат — запуск валидации.
Теперь сделаем экскурс в исходный код Django. Круто, когда не доверяешься чьим-то текстам (например, моим), а знаешь наверняка, потому что заглядывал под капот.
Важный момент: все выкладки из исходного кода даны по последней (4.0) версии Django, но они абсолютно актуальны и для версии 3.2 и для версии 2.2 (к вопросу о том, что не надо переживать, что вот вышла 4-я джанга, а мы изучаем 2.2 или 3.2, радикальные изменения больших кусков фреймворка происходят очень редко).
Код метода is_valid()
def is_valid(self): """Return True if the form has no errors, or False otherwise.""" return self.is_bound and not self.errors
Видим, что is_valid
возвращает True
, если нет ошибок (not self.errors
) и если в форму вообще передавались какие-то данные в аргументе data
(is_bound
).
Распутываем клубок и идем смотреть как устроен self.errors
@property def errors(self): """Return an ErrorDict for the data provided for the form.""" if self._errors is None: self.full_clean() return self._errors
Если в атрибуте _errors
ничего нет (а на старте валидации и не может быть в принципе), то запускается метод full_clean
экземпляра нашей формы.
Теперь исходный код метода full_clean формы
def full_clean(self): """ Clean all of self.data and populate self._errors and self.cleaned_data. """ self._errors = ErrorDict() if not self.is_bound: # Stop further processing. return self.cleaned_data = {} # If the form is permitted to be empty, and none of the form data has # changed from the initial data, short circuit any validation. if self.empty_permitted and not self.has_changed(): return self._clean_fields() self._clean_form() self._post_clean()
В этом коде сердце валидации и мы можем понять ее фундамент:
- Создаются два словаря — словарь для сбора данных об ошибках (
self._errors
) и словарь для сбора проверенных, не вызвавших ошибок, данных (self.cleaned_data
). - Одна за другой следуют три волны проверок:
- методом
_clean_fields()
- методом
_clean_form()
- методом
_post_clean()
Видим, что метод full_clean
формы ничего не возвращает. Его задача запустить методы различных проверок и через них наполнить (если есть чем) словари ошибок и проверенных данных.
7.2. Первая волна валидации — метод _clean_fields()
Если очень коротко, то суть метода _clean_fields
в том, чтобы запустить цикл по всем полям формы.
На каждой итерации у конкретного поля вызывается метод clean
, который, в свою очередь, запускает несколько проверок значения, которое поступило для этого поля.
7.2.1 Как работает метод clean
поля
Прежде чем разбирать этот вопрос, давайте остановимся, и опишем на примере, как мы пришли к вызову этого метода.
В request.POST
пришел словарь {'ship': '1', 'flight_date': '2022-01-31', 'currency': 'RUB'}
.
Для каждого ключа из этого словаря в джанго-форме есть одноименное поле.
Мы создаём экземпляр формы и помещаем туда данные order_form = OrderForm(data=request.POST)
. Сделав это, наша форма теперь считается «связанной данными» (is bound).
Запускаем валидацию order_form.is_valid()
.
Поскольку данные в форме есть, т.е. order_form.is_bound
возвращает True
, то запускается полноценная валидация.
Первым делом у формы срабатывает _clean_fields
, это означает, что:
- значение
'1'
передается полю формыship
(полю классаModelChoiceField
) и у этого поля вызывается методclean
, который проверяет значение1
- значение
'2022-01-31'
передается полю формыflight_date
(полю классаDateField
) и у этого поля вызывается методclean
, который проверяет значение2022-01-31
- значение
'RUB'
передается полю формыcurrency
(полю классаChoiceField
) и у этого поля вызывается методclean
, который проверяет значение'RUB'
.
К какому бы классу не относилось конкретное поле формы, все они в конечном счете наследники класса Field
. Поэтому именно там мы посмотрим описание метода clean
.
def clean(self, value): """ Validate the given value and return its "cleaned" value as an appropriate Python object. Raise ValidationError for any errors. """ value = self.to_python(value) self.validate(value) self.run_validators(value) return value
Код совершенно несложный:
- сначала поступившее значение преобразуется методом
to_python
поля - затем проверяется методом
validate
поля - затем проверяется методом
run_validators
Далее рассмотрим все три метода на примере поля flight_date
и значения '2022-01-31'
.
7.2.1.1 Метод to_python
поля
Задача этого метода в каждом поле — нормализовать поступившую строку с данными, привести ее к какому-либо Python-объекту.
Каким-либо образом переопределять этот метод для полей «из коробки» (т.е. классов, которые предлагает джанго) вам не нужно.
Просто знайте, что такой метод есть и зачем он нужен.
В каждом классе полей джанго-формы логика to_python
своя. Например, если поле класса IntegerField
, то поступившие значение будет преобразовываться в целое число вызовом int(value)
.
У нас поле DateField
и его логика to_python
в том, чтобы поступившую строку 2022-01-31
преобразовать в объект datetime.date
следующим образом
datetime.datetime.strptime(value, format).date()
В результате после to_python
строка 2022-01-31
станет объектом datetime.date(2022, 1, 31)
.
7.2.1.2 Метод validate
поля
Метода validate
вполне может не быть в классе конкретного поля, тогда работает validate
из класса-родителя Field
def validate(self, value): if value in self.empty_values and self.required: raise ValidationError(self.error_messages['required'], code='required')
Т.е. по сути задача validate
проверить, что для обязательного для заполнения поля пришли какие-либо данные.
Так же, как и в случае с to_python
, переопределять это поведение вам вряд ли понадобится.
7.2.1.3 Метод run_validators
поля
Этот метод запускает валидаторы, которые мы передали через параметр validators
. Вспомним, как объявляли поле flight_date
формы.
flight_date = forms.DateField( label='Когда летим', validators=[MinValueValidator(get_min_date)], # валидаторы отсюда запускает `run_validators` widget=...
Сам метод run_validators
трогать не нужно, просто следует знать, что валидаторы можно передать списком через validators
.
Теперь мы можем описать весь путь строки '2022-01-31'
при обработке методом clean
поля:
to_python
превращает вdatetime.date(2022, 1, 31)
validate
пропускает дальше, т.к. для обязательного пришли данныеrun_validators
запускаетMinValueValidator(get_min_date)
, которые тоже пропускает объект даты, т.к. она не меньше минимальной
7.2.2. Что происходит после отработки метода clean
поля?
Джанго ищет, а есть ли в классе нашей формы метод, название которого соответствовало бы формату def clean_<название_поля>
. Вот как это описано в исходном коде
if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value
Этот метод вы описываете самостоятельно и только тогда, когда проверок через to_python
, validate
и run_validators
вам недостаточно.
В методе приводится нужная логика проверок и проверенное значение (в случае успеха) передается в словарь cleaned_data
.
Остановимся и посмотрим, какая последовательность проверок у нас вырисовывается
is_valid() # старт валидации errors # второй вариант, как можно запустить валидацию full_clean() _clean_fields() # запуск цикла с перебором каждого поля формы # у каждого поля clean() # всегда to_python() # всегда validate() # всегда run_validators() # если передавали что-то в validators clean_<название_поля>() # если самостоятельно описали этот метод внутри класса формы
После вызова clean
и (при наличии) clean_<название_поля_формы>
работа _clean_fields
заканчивается и в дело вступает следующий этап проверок — метод _clean_form()
формы.
7.3. Как работает _clean_form
и clean
джанго-формы
После того, как провалидировано значение для каждого поля в отдельности, запускается метод _clean_form
, позволяющий проверить все или часть полей совместно.
Посмотрим исходный код метода _clean_form
:
def _clean_form(self): try: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data
Логика метода:
- Запустить метод
clean
формы - Если этот метод поднимет исключение
ValidationError
, то добавить это исключение в словарь с ошибками - Если метод отработает без ошибок, то есть два варианта:
- ничего не делать, если успешно отработавший
clean
вернулNone
- если
clean
что-то вернул, то это «что-то» становится новым словарем валидированных данных всей формы.
По поводу последнего нужно несколько пояснений. None
может вернуться в трех случаях:
- функция (метод) заканчивается
return
без значения; - функция (метод) заканчивается
return None
; - функция (метод) в принципе не содержат инструкцию
return
, т.е. не возвращают ничего.
Как правило, при описании логики метода clean
формы ничего не возвращают. Т.е. либо метод поднимает исключение (==проверка провалена), либо метод не возвращает ничего (т.е. исключение не поднялось, значит, проверка прошла успешно, и можно использовать ранее сформированный словарь cleaned_data
).
Метод clean мы описываем самостоятельно внутри формы, если хотим проверить уже отвалидированные значения для полей между собой или проверка требует участия значений из нескольких полей
У нас есть как раз такая задача: нам нужно проверить, что в выбранном пользователем корабле на выбранную дату есть места.
Т.е. нам нужно сделать проверку, в которой будут участвовать значения из двух полей — поля ship
и поля flight_date
.
Напишем код этой проверки (комментарии по ходу кода):
def clean(self): # берем значение поля `flight_date` из словаря валидированных данных flight_date = self.cleaned_data['flight_date'] # берем значение поля `ship` из словаря валидированных данных ship = self.cleaned_data['ship'] # делаем запрос к таблице заказов - считаем, сколько уже заказов есть на тот же корабль и на ту же дату existing_orders = Order.objects.filter( flight_date=flight_date, ship=ship ).count() # если количество заказов больше или равно вместимости выбранного корабля # capacity - это поле таблицы Spaceship, в котором хранится информация о предельном числе мест на конкретном корабле if existing_orders >= ship.capacity: # поднимаем исключение с описанием ошибки raise forms.ValidationError( 'К сожалению, на эту дату мест нет. ' 'Пожалуйста, выберите другую дату или другой корабль.' )
Заметьте, что наш метод clean
ничего не возвращает (return
нет). Потому что задача метода не добавить или изменить что-то в существующем словаре cleaned_data
, а подтвердить его правильность путем дополнительных проверок с участием значений из нескольких полей.
Дополним схему валидации новыми методами:
Остановимся и посмотрим, какая последовательность проверок у нас вырисовывается
is_valid() # старт валидации errors # второй вариант, как можно запустить валидацию full_clean() _clean_fields() # запуск цикла с перебором каждого поля формы # у каждого поля clean() # всегда to_python() # всегда validate() # всегда run_validators() # если передавали что-то в validators clean_<название_поля>() # если самостоятельно описали этот метод внутри класса формы _сlean_form() _clean() # описываем самостоятельно внутри класса формы, если требуется проверка с участием значений из нескольких полей
Остался ещё один рубеж валидации в форме — метод _post_clean()
. Он запускается только в модельных формах (формах-наследниках forms.ModelForm
).
7.3.1. Как отрендерить на странице в браузере ошибки, которые вернет метод clean
формы?
По умолчанию ошибки, которые возвращаются после отработки метода clean
формы, не записываются в словарь с ошибками конкретного поля.
Они записываются в словарь «ошибок вне полей», он доступен через вызов метода non_field_errors()
самого объекта формы.
Объект нашей формы находится в переменной space_form
, поэтому ошибки от clean
джанго-формы отрендерятся в том месте шаблона страницы, где мы пропишем {{ space_form.non_field_errors }}
Разместим данные об ошибках от clean
в самом начале формы
<form actions="" method="POST"> {% csrf_token %} {{ space_form.non_field_errors }} ...
А теперь попробуем сделать заказ полета на CrewDragon в день, когда мест на корабле уже нет (число заказов == количество мест на корабле).
Страница с формой после проверки на бэкенде отрендерится так
7.4. Как работает _post_clean
модельной формы
Задача метода _post_clean
модельной формы — запустить валидацию на уровне модели, которую обслуживает джанго-форма (модель, указанная в model
в Meta
).
«Проверка на уровне модели» означает, что в полях модели и на уровне всей модели тоже могут быть различные проверочные механизмы (валидаторы, ограничения (constraints)) и вот _post_clean
как раз и запускает эти механизмы (но есть исключения, о них тоже скажу).
Как _post_clean
модельной формы запускает валидацию в модели? Очень просто. У любой модели есть метод full_clean
(не путать с full_clean
формы).
Обратимся к документации:
Этот метод вызывает Model.clean_fields(), Model.clean(), и Model.validate_unique()
(если ``validate_unique
() равно True) в указанном порядке и вызывает исключение ValidationError, которое содержит атрибут message_dict с ошибками всех трех этапов проверки.
Метод clean_fields
модели действует ровно также как одноименный метод джанго-формы: берет значение для поля и прогоняет его через три проверки — to_python
, validate
и run_validators
(см. исходный код).
Метод clean
модели нужно описать самостоятельно, если требуются дополнительные проверки с участием значений из нескольких полей.
Вот что говорит документация:
Этот метод должен быть переопределен, если вам нужна дополнительная проверка модели или изменить значения атрибутов. Для объектов, вы можете определить его для автоматического определения полей, или для проверки, которая требует значения нескольких полей.
Метод validate_unique
модели проверит все ограничения по уникальности для валидируемых полей (unique=True
внутри поля, unique_together
в Meta
, UniqueConstraint
в Meta
. Для UniqueConstraint
, правда, есть исключение, о нем ниже). Повторюсь: проверка будет касаться только полей, которые указаны в модельной форме.
Вернемся к коду нашей модельной формы и увидим, что для flight_date
сейчас в форме нет валидатора, который мы прикручивали в «обычной» (до превращения в модельную) форме. Этот валидатор должен проверять, что выбранная дата не меньше чем на 4 недели позже текущей даты.
class OrderForm(forms.ModelForm): currency = forms.ChoiceField( choices = ( ('RUB', 'Российский рубль'), ('USD', 'Доллар США') ), label='Валюта платежа' ) class Meta: model = Order # поле `flight_date` модельное, вне `Meta` мы его не описывали, а внутри `Meta` валидаторы задать нельзя fields = ['ship', 'flight_date', 'currency'] widgets = { 'ship': RadioSelect, 'flight_date': DateInput(attrs={'type': 'date','min': get_min_date}) } labels = {'ship': 'На чем летим', 'flight_date': 'Когда летим'}
У нас есть два варианта (причем они не исключают друг друга):
- Определить поле формы
flight_date
явно внеMeta
и передать емуvalidators=[MinValueValidator(get_min_date)]
- Точно такой же валидатор прикрутить к полю
flight_date
в моделиOrder
(модели, которую обслуживает наша форма).
Не будем менять код формы и задействуем второй вариант.
# spaceflights/models.py ... def get_min_date(): """Возвращает текущую дату, увеличенную на 4 недели.""" return datetime.date.today() + datetime.timedelta(weeks=4) class Order(models.Model): ... flight_date = models.DateField( # перенесли валидатор из формы в модель validators=[ MinValueValidator( get_min_date, message=f"Дата должна быть не меньше {get_min_date().strftime('%d-%m-%Y')}" ) ], verbose_name='дата полета', ) ...
Давайте разберемся, как будет происходить валидация выбранной пользователем даты полета через модельную форму:
- в словаре
request.POST
на бэкенд приходит строка с датой по ключуflight_date
- после передаче этого словаря через параметр
data
в форму и вызова.is_valid()
джанго ищет среди полей формы поле с именемflight_date
- в поле формы
flight_date
(которое относится к классуDateField
) строка с датой преобразуется в объектdatetime.date
- после валидации остальных значений из
request.POST
джанго через методclean()
формы проверяет, есть ли на выбранную дату места на выбранном корабле - если валидация методом
clean
формы прошла успешна, джанго идет в полеflight_date
моделиOrder
и запускает теперь уже проверки на его уровне - в поле модели
flight_date
есть валидаторMinValueValidator(get_min_date)
, поступившее значение проходит проверку этим валидатором.
7.4.1. form.instance
— особенность работы _post_clean
Во время работы _post_clean
(который, как мы помним, актуален только для модельных форм) идёт постепенное создание инстанса (instance
) объекта записи, который в последующем можно записывать в базу через form.save
.
Для этого в классе BaseModelForm
есть даже специальный метод construct_instance
.
Вот как суть этого метода описана в исходном коде
Construct and return a model instance from the bound
form
‘s
cleaned_data
, but do not save the returned instance to the database.
Выглядит это так:
- Допустим, форма обслуживает модель
Order
- Сначала
form.instance = Order()
— т.е. пустой объект записи, никакие поля не заполнены - После валидации значения для поля
ship
модели происходитform.instance.ship = <проверенное_значение_из_поля_ship_модельной_формы>
- После валидации значения для поля
flight_date
модели происходитform.instance.flight_date = <проверенное_значение_из_поля_flight_date_модельной_формы>
Повторюсь: form.instance
— это подготовленный, но еще не сохраненный в базу объект записи. В этом объекте вполне может не хватать данных для какого-либо поля (потому что они, по самым разным причинам, не поступали через форму).
Работа с form.instance
в контроллере («вьюхе») позволяет дополнить объект записи новыми данными, чтобы к моменту сохранения объекта записи в БД в нем были все необходимые данные.
Важный момент: в обычных (не модельных) формах атрибута instance
нет.
7.4.2. Картина всей последовательности валидации с учетом _post_clean
is_valid() # старт валидации errors # второй вариант, как можно запустить валидацию full_clean() _clean_fields() # запуск цикла с перебором каждого поля формы # у каждого поля clean() # всегда to_python() # всегда validate() # всегда run_validators() # если передавали что-то в validators clean_<название_поля>() # если самостоятельно описали этот метод внутри класса формы _сlean_form() _clean() # описываем самостоятельно внутри класса формы, если требуется проверка с участием значений из нескольких полей ===============================Дальше только для модельных форм======================================== _post_clean() full_clean(validate_unique=False) # вызывается `full_clean` объекта модели (модели, НЕ формы) clean_fields() # у каждого валидируемого поля модели вызывается метод `clean` clean() validate_unique() # вызывается проверка ограничителей по уникальности, установленных для объекта модели (при наличии таковых)
7.5. Вопросы и подводные камни, связанные с валидацией в форме
1. Что НЕ проверит валидация данных через модельную форму?
Валидация через модельную форму НЕ проверит соблюдение ограничений в модели (constraints
), кроме ограничений уникальности.
Иными словами, если у вас в классе Meta
модели объявлен атрибут constraints
и в нем помимо (или вместо) UniqueConstraint
есть CheckConstraint
(например, вы запрещаете указывать одни и те же данные в двух разных полях), CheckConstraint
при валидации модельной формы проверяться НЕ будет.
Обратимся к документации:
In general constraints are not checked during full_clean(), and do not raise ValidationErrors. Rather you’ll get a database integrity error on save(). UniqueConstraints without a condition (i.e. non-partial unique constraints) are different in this regard, in that they leverage the existing validate_unique() logic, and thus enable two-stage validation. In addition to IntegrityError on save(), ValidationError is also raised during model validation when the UniqueConstraint is violated.
Ещё раз: In general constraints are not checked during full_clean(), and do not raise ValidationErrors («По общему правилу ограничения НЕ проверяются при вызове full_clean()
и не поднимают ValidationError
«). Исключение — UniqueConstraint
.
Но и UniqueConstraint
будут проверены не все. Вне проверки останутся UniqueConstraint
с условием. Пример такого ограничения есть в документации.
Если CheckConstraint
будет нарушено (или UniqueConstraint
с условием), то при попытке сохранить в базу валидированных данных, вы получите исключение IntegrityError
.
Как быть? Есть два варианта:
- Предусмотреть обработку
IntegrityError
(черезtry - except
) при вызовеform.save
- Предусмотреть валидацию аналогичную
CheckConstraint
(илиUniqueConstraint
с условием) в джанго-форме, например, в методеclean
.
2. is_valid()
вернул True
— значит, при вызове form.save
в базу всё запишется без проблем?
Как говорится, it depends. Зависит от двух моментов:
- Достаточно ли данных, которые поступили и были проверены через джанго-форму, для создания записи в конкретной таблицы. Если не хватает данных для какого-либо поля, это поле обязательно и у него нет дефолтного значения, то конечно сохранение в базу окончится неудачей.
- Настроена обработка исключений, которые могут возникнуть из-за проверок, которые не охвачены валидацией (см. предыдущий вопрос).
3. Где лучше размещать валидаторы — в форме или в в модели, стоит ли их дублировать?
Универсального ответа нет.
Нужно определить (даже если форма модельная) — есть ли у поля формы корреспондирующее поле в модели. Например, в нашей OrderForm
у поля currency
нет корреспондирующего поля в модели.
В таком случае валидатор нужен в поле формы, больше ему отработать негде.
Если поле формы связано с полем модели, то нужно оценить, как вообще устроена последовательность проверок в конкретной форме.
В примере с OrderForm
мы перенесли валидатор минимальной даты в поле модели. В чем может быть нелогичность этого шага?
Проверки на уровне модели начинаются строго после того, как отработали все проверки на уровне формы.
В нашей форме есть метод clean
, который проверяет наличие мест на выбранном корабле на выбранную дату, а для этого дергает базу (отправляет запрос к ней).
Т.е. сначала мы дергаем базу в clean
формы, а только потом, уже в модели, мы проверяем, а дата-то вообще ниже минимальной или нет.
Получается, мы можем напрасно дергать базу, если изначально дата по критерию минимальности не проходит. Логичнее сначала проверить минимальность даты (через validators
в поле формы), а потом уже в clean
(если всё хорошо) обращаться к базе для вычисления количества заказов на конкретный день.
Значит, нужно убрать валидатор из поля модели и перенести его в поле формы? Тут тоже надо подумать. Само по себе дублирование валидатора в поле модели и в поле формы ни хорошо, ни плохо.
Нужно решить, а как в вашем проекте данные будут попадать в базу. Только через форму по конкретному url
или как-то ещё?
Если вариантов записи в базу несколько (например, не только через конкретную форму, но и через админку или через API), то, конечно, валидаторы в модели нужно оставлять.
Что почитать, освежить в памяти:
- Проверка форм и полей форм
- Валидаторы полей формы
- Проверки в кастомном поле формы
- Как написать проверку методом
clean_названиеПоляФормы
- Как работает метод clean формы
- Метод
non_field_errors
формы - Метод
full_clean
модели - Установка ограничений в модели
- Django’s Field Choices Don’t Constrain Your Data(маленькая, но очень познавательная статья)