Модели: классы Python как таблицы БД

Модель — это класс Python, который Django превращает в таблицу базы данных. Вы описываете данные один раз на Python, а SQL Django пишет за вас.
Суть: модель = таблица, атрибуты класса = поля = столбцы, экземпляр класса = строка. ORM избавляет от ручного SQL и защищает от инъекций.

Декларативное описание данных

В большинстве веб-приложений данные живут в реляционной базе: таблицы, столбцы, строки. Django позволяет не писать SQL руками. Вместо CREATE TABLE вы объявляете класс, наследник models.Model, а каждое поле описываете как атрибут. Это и есть ORM — Object-Relational Mapping, мост между миром объектов Python и миром таблиц SQL.

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    is_published = models.BooleanField(default=False)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

Соответствие прямое и наглядное:

Класс Post              →  таблица blog_post
  title (CharField)     →  столбец title VARCHAR(200)
  body  (TextField)     →  столбец body  TEXT
  created_at            →  столбец created_at TIMESTAMP
  is_published          →  столбец is_published BOOLEAN
экземпляр Post(...)     →  строка таблицы
Post.objects.all()      →  SELECT * FROM blog_post

Типы полей

Поле — это не просто тип данных, а ещё и правила валидации и поведения. Чаще всего используют: CharField (короткая строка, обязателен max_length), TextField (длинный текст), IntegerField, BooleanField, DateTimeField, EmailField, SlugField (URL-дружественная строка), DecimalField (для денег — не используйте FloatField!). У каждого поля есть опции: null (можно ли NULL в базе), blank (можно ли пустое значение в формах), default, unique, choices.

Метод __str__ и класс Meta

Метод __str__ определяет, как объект выглядит в виде строки — в админке, в shell, в логах. Без него вы увидите бесполезное Post object (1). Вложенный класс Meta задаёт метаданные: ordering (сортировка по умолчанию), verbose_name (человекочитаемое имя), indexes (индексы для ускорения запросов), constraints (ограничения целостности на уровне базы).

Как это работает под капотом

Django использует метаклассы: при импорте модели он читает атрибуты-поля и строит внутреннее описание таблицы. На основе этого описания генерируются миграции (об этом — следующий урок) и SQL-запросы. Когда вы пишете Post.objects.filter(is_published=True), Django строит SQL с параметризованными значениями — поэтому ORM по умолчанию защищён от SQL-инъекций.

Поведение choices и валидации легко промоделировать на чистом Python. Вот как выглядит проверка значения по списку допустимых — ровно та логика, что Django применяет к полю с choices:

# Попробуй сам ▶ — валидация choices как в Django-поле
STATUS_CHOICES = [
    ("draft", "Черновик"),
    ("review", "На проверке"),
    ("published", "Опубликовано"),
]

valid = {code for code, _ in STATUS_CHOICES}

def set_status(value):
    if value not in valid:
        allowed = ", ".join(sorted(valid))
        return f"Ошибка: '{value}' недопустимо. Можно: {allowed}"
    label = dict(STATUS_CHOICES)[value]
    return f"OK: статус = {value} ({label})"

for v in ["draft", "published", "deleted"]:
    print(set_status(v))

Денежные значения и точность

Отдельно предупредим про деньги. FloatField хранит число с плавающей точкой, и из-за двоичного представления 0.1 + 0.2 там не равно 0.3. Для цен, балансов и любых финансов используйте DecimalField(max_digits=..., decimal_places=2) — он хранит точное десятичное значение.

Частые ошибки

  • Забыть max_length у CharField. Django выдаст ошибку — это обязательный параметр.
  • Путать null и blank. null — про базу, blank — про формы. Для строк обычно null не ставят, используют пустую строку.
  • Не определить __str__. Объекты станут нечитаемыми в админке и shell.
  • Хранить деньги во FloatField. Будут ошибки округления. Только DecimalField.

Best practices

  • Называйте модели в единственном числе и CamelCase: Post, Order, UserProfile.
  • Задавайте ordering в Meta, если данные почти всегда показываются в одном порядке.
  • Добавляйте verbose_name и индексы через Meta для читаемости и скорости.
  • Используйте choices вместо «магических строк» для статусов и типов.

Итоги

Модель — декларативное описание таблицы на Python. Поля — это столбцы с правилами, Meta — метаданные, __str__ — человеческое представление. ORM пишет SQL за вас и защищает от инъекций. Дальше превратим модель в реальную таблицу через миграции.

Проверьте себя
1. Чему соответствует экземпляр модели (объект)?
AЦелой таблице
BОдной строке таблицы
CОдному столбцу
DБазе данных целиком
2. Какое поле выбрать для хранения цены товара?
AFloatField
BCharField
CDecimalField с decimal_places
DIntegerField