Когда выходить за пределы ORM
ORM закрывает большую часть задач, а annotate, Func, Q и F решают многое. К собственному полю или сырому SQL стоит переходить, когда вы снова и снова преобразуете одно и то же значение, нужна возможность конкретной базы или замер показал, что запрос ORM слишком медленный.
| Задача | Инструмент |
|---|---|
| Хранить свой тип | подкласс Field |
Добавить фильтр вроде tags__has | свой Lookup |
| Одно SQL-выражение внутри queryset | RawSQL, Func |
| Сложный запрос, возвращающий модели | Model.objects.raw() |
| Отчёт, DDL, массовая запись | connection.cursor() |
💡 Сырой SQL не переносится между SQLite, PostgreSQL и MySQL. Покройте его тестами и держите в одном месте.
Своё поле Field
Поле переводит значение между объектом Python и типом базы. Наследуйтесь от ближайшего встроенного поля, чтобы тип столбца был готов, и переопределите преобразования.
from django.db import models
class TagListField(models.TextField):
description = "A list of tags stored as comma-separated text"
def from_db_value(self, value, expression, connection):
if value is None:
return value
return [t for t in value.split(",") if t]
def to_python(self, value):
if value is None or isinstance(value, list):
return value
return [t for t in value.split(",") if t]
def get_prep_value(self, value):
if value is None or isinstance(value, str):
return value
return ",".join(value)
class Post(models.Model):
title = models.CharField(max_length=100)
tags = TagListField(default=list)