Как называется встроенная база данных в django
Перейти к содержимому

Как называется встроенная база данных в django

  • автор:

Django, начало работы с базой данных

На Хабре существует много тем о django, с описанием различных вкусностей. Но мне не встречался пост, про начало пути, так сказать для новичка. Так что хочется написать короткое руководство начинающего бойца, по собственным шагам.
Cпасибо www.djbook.ru, русский перевод онлайн книги о django, именно отсюда я черпал данные для написания поста.

  1. python 2.6
  2. django 1.1.1
  3. postgreSQL 8.4.2
  4. python-psycopg2 2.0.8-0

здесь ссылка на то как установить django
Поскольку это описание собственного опыта, буду описывать всё по шагам, как делал я.

Первым шаг. Создание проекта

Для начала, нужно создать новый проект. Для этого нужно определиться с именем каталога проекта и местом его расположения. В выбранном каталоге выполним команду:
django-admin.py startproject hellowDjango

Данное действие приведёт к созданию шаблона нового проекта под названием hellowDjango. Подробнее о том, что происходит при выполнении команды можно прочитать здесь. Проект создан и пора переходить к следующему шагу.

Второй шаг, Настройка БД

Django может работать с множеством БД, но в данном примере я использую postgresql.

  • ‘postgresql_psycopg2’ — драйвер через который приложение будет выполнять запросы к БД и получать данные;
  • myDBName — название БД;
  • myUserDB — основной пользователь БД;
  • myUserDBPasswor — пароль основного пользователя.

Теперь необходимо проверить, что всё настроено правильно, для этого нужно выполнить команду в каталоге проекта:

python manage.py shell

Данная команда запустит интерпретатор python с настройками проекта. В интерпретаторе проверим подключение к БД.
>>> from django.db import connection
>>> cursor = connection.cursor()

В случае ошибки в настройках, появится сообщение которое поможет внести нужные исправления.

Третий шаг, Создание приложения.

Приложение в django — набор модели БД и представления хранящиеся в одном пакете python. Для того что бы, создать приложение, необходимо находясь в каталоге проекта выполнить команду:

python manage.py startapp MyName

Эта команда создаст директорию MyName, в каталоге проекта hellowDjango, а так же создаст файлы «заготовки» приложения в каталоге MyName.

Четвёртый шаг, Описание модели.

Модель БД в django — описание таблиц на языке python. Для создания модели приложения необходимо отредактировать файл MyName/models.py. В модели описывается много классов, каждый из которых будет описывать одну таблицу БД, а свойство класса — это один столбец таблицы. Класс должен быть унаследован от models.Model описанного в пакете django.db.models. Свойства класса должны иметь типы описанные в пакете django.db.models. Все типы данных и их применение описаны здесь.

  1. models.DateTimeField() — поле содержащее в себе Дату и Время
  2. models.CharField(max_length= xx) — Текстовое поле ограниченной длины. Длина строки = xx символов
  3. models.BooleanField() — Поле содержащие в себе булево значение
  4. models.ForeignKey() — ссылка на внешнюю таблицу
  5. models.PositiveIntegerField() — положительное целое значение
  6. models.URLField(max_length=хх) — ссылка на web страницу длина ссылки ограничена xx символами
  7. models.DateField() — поле содержащие Дату

При создании модели данных у меня возникла проблема с внешними ключами. При ссылке на внешнюю таблицу которая описывалась ниже происходила ошибка. Перестановка таблиц решила данную проблему. Отмечу, что при создании внешних ключей, django добавляет к имени поля с внешним ключом постфикс _id.

Для проверки корректности созданной модели необходимо выполнить команду:
python manage.py validate
После того как модель прошла проверку, можно посмотреть как django предложит сгенерировать таблицы. Для этого выполним ещё одну команду:
python manage.py sqlall MyName
Для создания модели в БД, выполним следующую команду:
python manage.py syncdb

Пятый шаг. Работа с БД в интерпретаторе.

Ниже представлены варианты вставки данных в БД

Запускаем интерпретатор
python manage.py shell
>>> import MyName.models as mo # импортируем все модели проекта

>>> type = mo.ProductClass() # создаём экземпляр класса Типы Продуктов
>>> type.class_name = ‘сырьё’ # название типа
>>> type.save() # записываем экземпляр в БД
>>> type
&lt ProductClass: ProductClass object&gt

# так же можно воспользоваться вставкой данных другого вида
>>> mo.Dealer(organization_name = «ООО Рога И Копыта»).save() # создаст запись в таблице Dealer
>>> mo.Dealer.objects.all() # выполняем выборку данных из таблицы Dealer
[&lt Dealer: ООО Рога И Копыта&gt] # поскольку запись в таблице только 1 то и коллекция содержит всего 1 элемент, обратиться к нему можно через индексы.

# если для создания записи будет не хватать каких либо данных вы уведите протокол ошибки. Например такой:
>>> mo.Product(name = ‘овёс’, price = 50, product_class = type).save()
Traceback (most recent call last):
.
IntegrityError: null value in column «diler_id» violates not-null constraint
>>> mo.Product(name = ‘овёс’, price = 50, product_class = type, diler = mo.Dealer.objects.all()[0]).save() # вторая запись в таблице Product

Теперь нужно поговорить о выборе данных из таблиц.

# полная выборка из таблицы
>>> mo.Product.objects.all()
[&lt Product: мука&gt, &lt Product: овёс&gt, &lt Product: рожь&gt]

# выборка по полю name
>>> mo.Product.objects.filter(name = ‘овёс’)
[&lt Product: овёс&gt]

# выборка по частичному совпадению
>>> mo.Product.objects.filter(name__contains = ‘о’)
[&lt Product: овёс&gt, &lt Product: рожь&gt]

Мы рассмотрели вставку и выборку данных.

Давайте рассмотрим варианты обновления записей.

# для того что бы провести обновление записи необходимо выполнить следующие действия
>>> item2 = mo.Product.objects.get(name = ‘овёс’)
>>> item2.name = «овёс золотистый»
>>> item2.save()

Данный пример прост но имеет свой недостаток, он выполняет обновление всех полей записи, а не только тех которые изменились. Данный факт может привести к «гонке» пользователей, когда происходит массовое изменение данных в Таблице. Для решения такой проблемы правильно будет использовать метод update. Данный метод изменяет только указанные поля.
>>> mo.Product.objects.filter(id=3).update(name=’oves’)
1
>>> cole[2]
&lt Product: oves&gt

Последнее, что хочется описать, это удаление записей из БД.

Существует два способа удаления записей:

Первый удаление всех данных из таблицы
>>> cm.Dealer.objects.all()
[&lt Dealer: ООО Рога И Копыта&gt]
>>> cm.Dealer.objects.all().delete()
>>> cm.Dealer.objects.all()
[]

Второй удаление отобранных записей
>>> cm.ProductClass.objects.all()[0].id
1
>>> cm.ProductClass.objects.filter(id=1).delete()
>>> cm.ProductClass.objects.all()
[]

Более подробно о всех командах api работы с БД и можно почитать здесь.

Вот краткое описание возможностей работы с БД в django, надеюсь данная информация будет полезна.
Немного более развёнутую статью можно прочитать у меня в блоге

Модели

Для хранения данных в веб-приложении, как правило, применются базы данных. И фреймворк Django уже по умолчанию предоставляет удобный функционал для работы с различными системами баз данных.

Настройки подключения к базе данных

По умолчанию Django в качестве базы данных использует SQLite. Она очень проста в использовании и не требует запущенного сервера. Все файлы базы данных могут легко переноситься с одного компьютера на другой. Однако при необходимости мы можем использовать в Django большинство распространенных СУБД.

Для работы с базами данных в проекте Django в файле settings.py определен параметр DATABASES , который по умолчанию выглядит следующим образом:

DATABASES = < 'default': < 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', >>

Переменная DATABASES содержит набор конфигураций подключений к базам данных в виде словаря. Ключи в этом словаре — названия подключений. То есть мы можем определить кучу подключений. Но как минимум одно подключение должно быть определено в переменной DATABASES — подключение с именем default , которое представляет подключение по умолчанию.

Конфигурация каждого подключения может состоять из ряда параметров. По умолчанию указываются только два параметра. Параметр ENGINE указывает на используемый движок для доступа к БД. В данном случае это встроенный пакет django.db.backends.sqlite3 .

Второй параметр — NAME указывает на путь к базе данных. По умолчанию база данных называется db.sqlite3 . Для установки пути используется каталог из переменной BASE_DIR, которая задана в начале файла:

BASE_DIR = Path(__file__).resolve().parent.parent

По умолчанию BASE_DIR указывает на каталог, в котором находится папка проекта. И после первого запуска проекта в указанном каталоге по умолчанию будет создан файл db.sqlite3 , который собственно и будет использоваться в качестве базы данных.

Установка пути к базе данных в проекте Django

Поддерживаемые субд

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

Как называется встроенная база данных в django

Django это фреймворк для какого языка?%%QUESTION%% %%QUESTION%% Java%%ANSWER%% Jinja%%ANSWER%% C++%%ANSWER%% Django – язык программирования. Он сам по себе%%ANSWER%% Python %%%%%%NEXT__ONE Как называется шаблонизатор в Django?%%QUESTION%% %%QUESTION%% pip%%ANSWER%% npm%%ANSWER%% Python%%ANSWER%% HTML%%ANSWER%% Jinja %%%%%%NEXT__ONE Какой пакетный менеджер есть в Python?%%QUESTION%% %%QUESTION%% npm%%ANSWER%% Jinja%%ANSWER%% Django%%ANSWER%% manage.py%%ANSWER%% pip %%%%%%NEXT__ONE На Django можно построить..%%QUESTION%% %%QUESTION%% Только сайты одностраничники%%ANSWER%% Социальные сети%%ANSWER%% Поисковые системы%%ANSWER%% Интернет магазины%%ANSWER%% Сайты любого типа %%%%%%NEXT__ONE Как называется встроенная база данных в Django?%%QUESTION%% %%QUESTION%% MySQL%%ANSWER%% NoSQL%%ANSWER%% PostgreSQL%%ANSWER%% SQLite %%%%%%NEXT__ONE Какая команда устанавливает Django?%%QUESTION%% %%QUESTION%% pip Django%%ANSWER%% pip create Django%%ANSWER%% install Django%%ANSWER%% pip start Django%%ANSWER%% pip install Django %%%%%%NEXT__ONE Через какой файл можно запускать локальный сервер из командной строки?%%QUESTION%% %%QUESTION%% settings.py%%ANSWER%% urls.py%%ANSWER%% views.py%%ANSWER%% __init__.py%%ANSWER%% manage.py %%%%%%NEXT__ONE Какая команда запускает локальный сервер?%%QUESTION%% %%QUESTION%% startserver%%ANSWER%% openserver%%ANSWER%% server%%ANSWER%% runserver %%%%%%NEXT__ONE Что необходимо выполнить первым делом для создания нового проекта?%%QUESTION%% * Все необходимые библиотеки уже установлены на вашем компьютере%%QUESTION%% Создать первое приложение при помощи startapp%%ANSWER%% Запустить локальный сервер%%ANSWER%% Создать новый проект через файл manage.py%%ANSWER%% Создать проект через django-admin startproject %%%%%%NEXT__ONE Что такое Джанго приложение?%%QUESTION%% %%QUESTION%% Это небольшая программа на сайте, выполняющая различные функции%%ANSWER%% Это весь сайт со всеми его комплектующими%%ANSWER%% Это часть сайта, которая отвечает за его определенный раздел %%%%%%NEXT__ONE На каком языке пишутся шаблоны в Django?%%QUESTION%% %%QUESTION%% Jinja%%ANSWER%% Python%%ANSWER%% JavaScript%%ANSWER%% CSS%%ANSWER%% HTML %%%%%%NEXT__ONE Что делают следующие строки?%%QUESTION%%

urlpatterns = [ path('posts/best/', include('posts.urls')), ]

%%QUESTION%% Подключают новое приложение к сайту%%ANSWER%% Ничего, так как в коде есть ошибка%%ANSWER%% Подключают файл из приложения posts при переходе на ссылку posts/best/ %%%%%%NEXT__ONE Что делает HttpResponse?%%QUESTION%% %%QUESTION%% Устанавливает соединение с сервером%%ANSWER%% Выводит информацию по поводу соединения%%ANSWER%% Выводит на экран HTML-шаблоны%%ANSWER%% Выводит на экран текст без HTML%%ANSWER%% Выводит на экран текст с HTML %%%%%%NEXT__ONE Какие функции выполняет файл views.py?%%QUESTION%% %%QUESTION%% Он позволяет выводить информацию на экран%%ANSWER%% Через него можно выводить значения переданные из Python%%ANSWER%% Служит для создания HTML-шаблонов%%ANSWER%% Указывает какие HTML-шаблоны должны открываться %%%%%%NEXT__ONE Каждое новое приложение необходимо зарегистрировать в. %%QUESTION%% %%QUESTION%% файле urls.py%%ANSWER%% файле views.py%%ANSWER%% не требуется регистрация приложений%%ANSWER%% командной строке через файл setting.py%%ANSWER%% файле setting.py, добавив в список INSTALLED_APPS

Django ORM и эффективная работа с базой данных

Архитектура Django позволяет значительно ускорить процесс разработки благодаря простой схеме использования баз данных в приложениях. Django ORM предоставляет простой механизм работы с базой без изучения синтаксиса SQL запросов. Однако подобное абстрагирование может привести к неэффективному использованию БД, что может сказаться на медленной работе сайтов даже при небольших объемах данных.

Давайте посмотрим, как можно создавать модели и работать с ними эффективно.

Тестирование производительности приложения Django

Иногда обнаружить проблемы производительности удается довольно быстро. Часто они появляются при первой попытке запустить приложение с реальными данными. Это может стать очевидным, когда выполнение набора из нескольких тестов занимает больше 5 минут. В других случаях медленная работа приложения становится заметной визуально. К счастью, существуют некоторые шаблоны проблем производительности, которые легко идентифицировать и исправить. В листинге 1 (файл models.py приложения) и листинге 2 показан пример типичной ошибки.
Листинг 1. Базовые модели для приложения examples: файл models.py

from django.db import models # Некоторый документ, такой как запись в блоге или wiki-страничка class Document(models.Model): name = models.CharField(max_length=255) # Генерируемый пользователем комментарий, похожий на комментарии на сайте # Digg или Reddit class Comment(models.Model): document = models.ForeignKey(Document, related_name='comments') content = models.TextField()

Во всех приведенных примерах предполагается, что

  • Проект Django называется better_models.
  • В проекте better_models имеется приложение с именем examples.

Приложение examples моделирует простую, похожую на блог систему документов, каждый из которых может иметь комментарии.

В листинге 2 показано, как можно осуществлять доступ к модели данных, показанной в листинге 1 неэффективным способом.
Листинг 2. Медленный доступ к моделям данных

from examples.model import * import uuid # Сначала создадим много документов и назначим им случайные имена for i in range(0, 10000): Document.objects.create(name=str(uuid.uuid4())) # Запрашиваем имена документов для последующего просмотра names = Document.objects.values_list('name', flat=True)[0:5000] # Медленный способ получения списка документов с # заданными именами documents = [] for name in names: documents.append(Document.objects.get(name=name))

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

Приведенный выше наивный код выполняется около 65 секунд при использовании размещаемой в оперативной памяти базе данных sqlite3. С базой данных, зависящей от файловой системы, он бы работал еще дольше. В листинге 3 показано, как исправить этот медленный код. Вместо выполнения запроса к базе данных для каждого имени, следует использовать оператор fieldname__in , который сгенерирует SQL-запрос, выглядящий примерно так:

SELECT * FROM model WHERE fieldname IN ('1', '2', . )

(Точный синтаксис запроса будет различаться в зависимости от используемой базы данных.)

Листинг 3. Быстрый запрос для получения списка элементов

from examples import models import uuid for i in range(0, 10000): Document.objects.create(name=str(uuid.uuid4())) names = Document.objects.values_list('name', flat=True)[0:5000] documents = list(Document.objects.filter(name__in=names))

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

Гуру баз данных, для которых написание SQL-кода обычное дело, сочтут этот пример очевидным, но многие программисты Python не имеют существенного опыта работы с базами данных. Иногда самые хорошие привычки разработчика могут сыграть против эффективности. В листинге 4 показан один из способов рефакторинга кода из листинга 2, который можно было бы применить, не понимая его ошибочности.
Листинг 4. Типичный шаблон кода, приводящего к медленной работе с базой данных

for name in names: documents.append(get_document_by_name(name)) def get_document_by_name(name): return Document.objects.get(name=name))

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

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

Встроенный управляющий класс модели, называемый Manager , используют все разработчики Django: именно он вызывается для всех методов формы Model.objects.* . Базовый класс Manager доступен автоматически и предоставляет часто используемые методы, возвращающие объекты QuerySet (например, all() ), простые значения (например, count() ) и объекты класса Model (например, get_or_create() ).

Платформа Django поощряет разработчиков переопределять базовый класс Manager . Чтобы проиллюстрировать, почему это может быть полезным, добавим в приложение examples новую модель Format , которая описывает формат хранимых в системе файлов документов, например, как это показано в листинге 5.
Листинг 5. Добавление модели в приложение examples

from django.db import models class Document(models.Model): name = models.CharField(max_length=255) format = models.ForeignKey('Format') class Comment(models.Model): document = models.ForeignKey(Document, related_name='comments') content = models.TextField() class Format(models.Model): type = models.CharField(choices=( ('Text file', 'text'), ('ePub ebook', 'epub'), ('HTML file', 'html')), max_length=10)

Передовые техники обновления базы данных

Каждый раз при добавлении в models.py новой таблицы или новых столбцов в существующую таблицу необходимо повторно синхронизировать соответствующую базу данных. Воспользовавшись следующими советами, вы сможете делать это более эффективно.

  • На ранних стадиях разработки используйте только размещаемую в оперативной памяти базу данных, такую как sqlite3. Пользуйтесь возможностями по автоматической загрузке данных в базу из файлов с тестовым содержимым. Базы данных, размещаемые в оперативной памяти, работают достаточно быстро для одного пользователя и позволяют существенно сократить время ожидания при удалении и повторном создании таблиц в традиционных СУБД, таких как MySQL.
  • Придерживайтесь стиля разработки через тестирование. Инфраструктура тестирования Django каждый раз пересоздает базу данных с нуля, поэтому таблицы всегда будут актуальными. Применение этой функциональности в сочетании с размещаемой в оперативной памяти базой данных sqlite3 делает тестирование еще быстрее.
  • Попробуйте одну из многочисленных надстроек Django, управляющих синхронизацией базы данных. У меня был положительный опыт использования пакета django-evolution , кроме него имеются и другие пакеты. Больше информации о django-evolution можно найти в разделеРесурсы.

Если вы решили использовать для разработки или тестирования sqlite3, обязательно проведите финальное интеграционное тестирование на рабочей базе данных. Для большинства случаев механизм ORM Django помогает сгладить различия между разными СУБД, но нет гарантии, что поведение будет во всем идентичным.

Далее воспользуемся измененной моделью и создадим несколько документов различных форматов(листинг 6).
Листинг 6. Создаем несколько документов различных форматов

# Сначала создадим набор объектов класса Format # и сохраним их в базе данных format_text = Format.objects.create(type='text') format_epub = Format.objects.create(type='epub') format_html = Format.objects.create(type='html') # Создадим несколько документов в различных форматах for i in range(0, 10): Document.objects.create(name='My text document', format=format_text) Document.objects.create(name='My epub document', format=format_epub) Document.objects.create(name='My HTML document', format=format_html)

Допустим, нужно, чтобы приложение предоставляло возможность сначала фильтровать документы по формату, а затем фильтровать этот объект QuerySet по другим полям, например по названию. Следующий простой запрос возвращает только текстовые документы: Document.objects.filter(format=format_text) .

В этом примере смысл запроса достаточно ясен, однако в крупных приложениях, возможно, на результат придется накладывать гораздо больше ограничений. Может быть, вы захотите выводить только документы, помеченные какпубличные, или документы созданные не позднее, чем 30 дней назад. Если в приложении вам придется вызывать эти запросы в различных местах, то поддержка всех условий фильтрации в синхронизованном состоянии может стать источником ошибок и головной болью при сопровождении кода.

В таких ситуациях может помочь создание собственного управляющего класса модели. Собственные управляющие классы позволяют задавать неограниченное количество «шаблонных» запросов подобно методам встроенного управляющего класса модели, таким как latest() (который возвращает только последний экземпляр модели) или distinct() (который добавляет к сгенерированному запросу инструкцию SELECT DISTINCT ). Управляющие классы не только сокращают дублирование кода в приложении, но также улучшают читаемость кода. Согласитесь, что спустя некоторое время по сравнению с кодом:

Documents.objects.filter(format=format_text,publish_on__week_day=todays_week_day, is_public=True).distinct().order_by(date_added).reverse()

вам или новому разработчику будет гораздо проще понять следующий код:

Documents.home_page.all()

Создать собственный управляющий класс модели чрезвычайно просто. В листинге 7 показан пример создания для модели документа управляющего класса get_by_format .
Листинг 7. Собственный управляющий класс модели, предоставляющий методы для каждого типа формата документа

from django.db import models class DocumentManager(models.Manager): # Класс модели всегда доступен управляющему классу через # self.model, но в этом примере мы используем только метод # filter(), унаследованный от models.Manager. def text_format(self): return self.filter(format__type='text') def epub_format(self): return self.filter(format__type='epub') def html_format(self): return self.filter(format__type='html') class Document(models.Model): name = models.CharField(max_length=255) format = models.ForeignKey('Format') # Новый управляющий класс модели get_by_format = DocumentManager() # Управляющий класс по умолчанию теперь нужно определять явно objects = models.Manager() class Comment(models.Model): document = models.ForeignKey(Document, related_name='comments') content = models.TextField() class Format(models.Model): type = models.CharField(choices=( ('Text file', 'text'), ('ePub ebook', 'epub'), ('HTML file', 'html')), max_length=10) def __unicode__(self): return self.type

Несколько замечаний относительно этого кода.

  • Если вы создаете собственный управляющий класс модели, Django автоматически исключает управляющий класс по умолчанию. Я предпочитаю оставлять как управляющий класс модели по умолчанию, так и собственный управляющий класс, чтобы другие разработчики (или я сама, если забуду) могли все так же использовать objects , которые будут вести себя в точности так, как ожидается. Однако так как управляющий класс, доступный по имени get_by_format , является просто подклассом встроенного класса models.Manager , в нем доступны все методы по умолчанию, такие как all() . Делать или не делать одновременно доступными управляющий класс по умолчанию и собственный управляющий класс, зависит от личных предпочтений.
  • Также есть возможность напрямую назначать для objects новый управляющий класс. Единственный недостаток проявится, если вы захотите вручную переопределить изначальный класс QuerySet . В таком случае ваши новые objects могут вести себя неожиданным для других разработчиков образом.
  • Вам необходимо определить управляющий класс в models.py перед определением вашего класса модели, иначе он не будет доступным для Django. Это похоже на ограничения, имеющиеся у класса ForeignKey .
  • Можно было бы просто реализовать класс DocumentManager с единственным методом, принимающим аргумент, например with_format(format_name) . Однако в общем случае я предпочитаю создавать методы управляющего класса с подробными именами, но не принимающие никаких аргументов.
  • Не существует технического ограничения на количество собственных управляющих классов, которые можно назначать модели, но маловероятно, что вам понадобится больше чем один или два.

Использовать методы нового управляющего класса модели достаточно просто.

In [1]: [d.format for d in Document.get_by_format.text_format()][0] Out[1]: In [2]: [d.format for d in Document.get_by_format.epub_format()][0] Out[2]: In [3]: [d.format for d in Document.get_by_format.html_format()][0] Out[3]:

Теперь появилось удобное место, в котором можно размещать любую функциональность, относящуюся к этим запросам, также сюда можно добавлять дополнительные ограничения, не засоряя код. Такой подход согласуется с видением в Django шаблона модель–вид–контроллер (model-view-controller или MVC), в соответствии с которым функциональность подобного рода следует размещать в models.py, а не скапливать ее в представлениях или шаблонах

Переопределяем изначальный класс QuerySet, возвращаемый пользовательским управляющим классом модели

В другом шаблоне реализации управляющих классов модели собственные методы вообще не создаются. Вместо определения нового метода, возвращающего только HTML-документы, можно определить управляющий класс, который будет работать только на этом ограниченном множестве данных. Пример показан в листинге 8.
Листинг 8. Пользовательский управляющий класс для HTML-документов

class HTMLManager(models.Manager): def get_query_set(self): return super(HTMLManager, self).get_query_set().filter(format__type='html') class Document(models.Model): name = models.CharField(max_length=255) format = models.ForeignKey('Format') html = HTMLManager() get_by_format = DocumentManager() objects = models.Manager()

В этом примере метод get_query_set() наследуется из models.Manager и переопределяется. В этом методе за основу берется базовый запрос (тот же самый, который генерируется методом all() ), к которому затем применяется дополнительный фильтр. Все методы, которые мы будем впоследствии добавлять в этот управляющий класс, будут сначала вызывать метод get_query_set() , а затем применять к результату дополнительные методы запросов, как показано в листинге 9.
Листинг 9. Использование управляющего класса, работающего только с документами формата html

# Наш запрос HTML-документов возвращает то же количество # документов, что и управляющий класс по умолчанию, явно выполняющий фильтрацию # данных. In [1]: Document.html.all().count() Out[1]: 10 In [2]: Document.get_by_format.html_format().count() Out[2]: 10 # Можно доказать, что они возвращают в точности один и тот же результат In [3]: [d.id for d in Document.get_by_format.html_format()] == [d.id for d in Document.html.all()] Out[3]: True # В HTMLManager() уже нельзя работать с нефильтрованными данными In [4]: Document.html.filter(format__type='epub') Out[4]: []

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

Использование классов и статических методов в моделях

Нет никаких ограничений на типы методов, которые можно добавлять в управляющий класс. Методы могут возвращать объекты QuerySet , как показано выше, или экземпляры соответствующего класса модели (доступного через self.model ).

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

Вот простой пример вспомогательного метода, который относится ко всему классу Format , а не к какому-либо конкретному его экземпляру:

# Возвращает каноническое имя формата для некоторых часто # встречающихся в реальной жизни расширений def check_extension(extension): if extension == 'text' or extension == 'txt' or extension == '.csv': return 'text' if extension.lower() == 'epub' or extension == 'zip': return 'epub' if 'htm' in extension: return 'html' raise Exception('Did not get known extension')

Этот код не принимает и не возвращает экземпляр класса Format , поэтому он не может быть методом экземпляра класса. Его можно было бы поместить в класс FormatManager , но так как этот метод вообще не обращается к базе данных, это место для него также не совсем подходит.

В качестве решения можно добавить этот метод в класс Format и объявить его статическим методом с помощью декоратора @staticmethod , как показано в листинге 10.
Листинг 10. Добавляем вспомогательную функцию в виде статического метода класса модели

class Format(models.Model): type = models.CharField(choices=( ('Text file', 'text'), ('ePub ebook', 'epub'), ('HTML file', 'html')), max_length=10) @staticmethod def check_extension(extension): if extension == 'text' or extension == 'txt' or extension == '.csv': return 'text' if extension.lower() == 'epub' or extension == 'zip': return 'epub' if 'htm' in extension: return 'html' raise Exception('Did not get known extension') def __unicode__(self): return self.type

Этот метод можно вызывать в виде Format.check_extension(extension) , и для этого не требуется иметь экземпляр класса Format или создавать управляющий класс.

В Python также имеется декоратор @classmethod , который генерирует методы, оперирующие над классами. Такие методы принимают в качестве первого аргумента сам класс. Это может быть полезно, когда вы хотите выполнить какую-либо интроспекцию класса, не создавая экземпляр этого класса.

Агрегирующие запросы в Django

В Django 1.1 в механизм ORM включены мощные методы запросов, позволяющие реализовывать функциональность, ранее доступную только через использование SQL напрямую. Для разработчиков Python, остерегающихся работать с SQL, и для всех, кто хочет поддерживать работоспособность своего Django-приложения с различными базами данных, это является действительно ценным нововведением.

В современных приложениях, ориентированных на общение пользователей, очень часто данные сортируются не по статическому полю, например по алфавиту или времени создания, а на основе динамических данных. Допустим, в приложении examples мы хотим сортировать документы по популярности, определяемой по количеству комментариев к документу. До Django 1.1 такое можно было сделать либо написав собственный SQL-код, либо реализовав непереносимую хранимую процедуру, либо, что хуже всего, написав несколько неэффективных объектно-ориентированных запросов. При другом подходе можно было бы определить в базе данных фиктивное поле для хранения желаемого значения (например, количества комментариев к документу) и обновлять это поле вручную, переопределив метод save() документа.

Механизм агрегации Django устраняет необходимость прибегать к таким хитростям. Теперь можно упорядочивать документы по количеству комментариев, используя лишь один метод QuerySet : annotate() . Пример приведен в листинге 11.
Листинг 11. Использование агрегации для упорядочения результатов по количеству комментариев

from django.db.models import Count # Создадим несколько документов unpopular = Document.objects.create(name='Unpopular document', format=format_html) popular = Document.objects.create(name='Popular document', format=format_html) # Документу "popular" добавим больше комментариев, чем документу "unpopular" for i in range(0,10): Comment.objects.create(document=popular) for i in range(0,5): Comment.objects.create(document=unpopular) # Если мы возвращаем результаты, сортируя их по времени создания (по умолчанию по id), # первым будет выведен документ "unpopular". In [1]: Document.objects.all() Out[1]: [, ] # Если же вместо этого мы аннотируем результат общим количеством комментариев # у каждого документа и затем упорядочим его по этому вычисленному значению, # то первым будет выведен документ "popular". In [2]: Document.objects.annotate(Count('comments')).order_by('-comments__count') Out[2]: [, ]

Метод annotate() класса QuerySet сам по себе не выполняет никакой агрегации. Вместо этого он командует Django назначить значение переданного выражения псевдостолбцу в полученном результате. По умолчанию именем этого столбца является строка из названия предоставленного поля (здесь значение Comment.document.related_name() ) и имени агрегирующего метода. В этом коде вызывается django.db.models.Count – одна из простых математических функций, доступных в библиотеке агрегации (с полным списком методов можно ознакомиться по ссылке в разделе Ресурсы).

Результатом вызова Document.objects.annotate(Count(‘comments’)) является объект QuerySet , имеющий новое свойство comments__count . Чтобы переопределить имя по умолчанию, можно передать желаемое имя в качестве именованного аргумента.

Document.objects.annotate(popularity=Count('comments'))

Теперь, когда промежуточный объект QuerySet содержит количество комментариев, ассоциированных с каждым документом, можно упорядочить его по этому новому полю. Так как мы хотим вначале видеть документы с наибольшим количеством комментариев, задаем сортировку по убыванию: .order_by(‘-comments__count’) .

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

Другие типы агрегации в Django 1.1

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

In [1]: from django.db.models import Avg In [2]: Document.objects.aggregate(Avg(‘comments’)) Out[2]:

Агрегацию можно применять как к отфильтрованным, так и неотфильтрованным запросам. Кроме того, можно фильтровать данные по столбцам, сгенерированным с помощью annotate , так же как по обычным столбцам. Также агрегирующие методы можно применять к объединениям данных. Например, можно агрегировать документы на основе рейтинга комментариев к ним, как это сделано в сайтах наподобие Slashdot.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *