← Назад к вопросам

Как создаются индексы в Django ORM?

2.0 Middle🔥 181 комментариев
#Django#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как создаются индексы в Django ORM

Индекс в БД ускоряет поиск данных, создавая отсортированную структуру. Django ORM предоставляет несколько способов создать индексы как в модели, так и через миграции.

1. Индекс на одно поле

from django.db import models

class User(models.Model):
    # Параметр db_index=True создаёт индекс
    email = models.EmailField(unique=True)  # unique автоматически создаёт индекс
    username = models.CharField(max_length=100, db_index=True)
    
    class Meta:
        # Или в Meta если нужен индекс на существующее поле
        indexes = [
            models.Index(fields=['username']),
        ]

2. Составной индекс (на несколько полей)

class Article(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    published_date = models.DateField()
    status = models.CharField(max_length=10)
    
    class Meta:
        indexes = [
            # Индекс на несколько полей
            models.Index(fields=['author', 'published_date']),
            
            # Со специальным именем
            models.Index(
                fields=['author', 'status'],
                name='author_status_idx'
            ),
        ]

3. Индекс с условием (partial index)

from django.db.models import Q, Index

class Product(models.Model):
    name = models.CharField(max_length=200)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        indexes = [
            # Индекс только для активных товаров
            Index(
                fields=['name'],
                name='active_products_idx',
                condition=Q(is_active=True)  # Только active
            ),
            
            # Более сложное условие
            Index(
                fields=['created_at'],
                condition=Q(is_active=True) & Q(created_at__gte='2024-01-01')
            ),
        ]

4. Уникальный индекс

class Email(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    email = models.EmailField()
    
    class Meta:
        constraints = [
            # Уникальность на два поля вместе
            models.UniqueConstraint(
                fields=['user', 'email'],
                name='unique_user_email'
            ),
        ]

5. Индекс на выражение

from django.db.models import F, Index
from django.db.models.functions import Lower

class Category(models.Model):
    name = models.CharField(max_length=100)
    
    class Meta:
        indexes = [
            # Индекс на lowercase значения
            Index(
                models.F('name').asc(nulls_last=True),
                name='name_lower_idx'
            ),
            
            # Или с функцией
            Index(
                fields=['name'],
                name='name_idx'
            ),
        ]

6. Порядок сортировки в индексе

class Order(models.Model):
    customer = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    total = models.DecimalField(max_digits=10, decimal_places=2)
    
    class Meta:
        indexes = [
            # DESC для created_at, ASC для total
            models.Index(
                fields=['-created_at', 'total'],
                name='recent_orders_idx'
            ),
        ]

7. Хеш-индекс

class Document(models.Model):
    content = models.TextField()
    
    class Meta:
        indexes = [
            # HASH индекс для длинных строк (если БД поддерживает)
            models.Index(
                fields=['content'],
                name='content_hash_idx',
                opclasses=['hash']  # PostgreSQL feature
            ),
        ]

8. Проверка индексов в БД

# management command для просмотра индексов
# python manage.py sqlsequencereset app

# Или явно
from django.core.management import call_command
import io
import sys

out = io.StringIO()
call_command('sqlmigrate', 'app', '0001', stdout=out)
print(out.getvalue())  # Покажет CREATE INDEX запросы

9. Миграция: добавление индекса

# После изменения модели
# python manage.py makemigrations
# python manage.py migrate

# Сгенерированная миграция будет что-то вроде:
from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.AddIndex(
            model_name='article',
            index=models.Index(
                fields=['author', 'published_date'],
                name='author_pub_idx'
            ),
        ),
    ]

10. Ручное создание индекса через миграцию

from django.db import migrations
from django.db.models import Index, Q

class Migration(migrations.Migration):
    dependencies = []
    
    operations = [
        migrations.AddIndex(
            model_name='product',
            index=Index(
                fields=['name', '-created_at'],
                name='product_search_idx',
                condition=Q(is_active=True)
            ),
        ),
    ]

11. Удаление индекса

from django.db import migrations

class Migration(migrations.Migration):
    dependencies = []
    
    operations = [
        migrations.RemoveIndex(
            model_name='article',
            name='author_status_idx',
        ),
    ]

12. БД-специфичные индексы (PostgreSQL)

from django.contrib.postgres.indexes import BTreeIndex, BrinIndex

class BigTable(models.Model):
    data = models.DateTimeField()
    value = models.IntegerField()
    
    class Meta:
        indexes = [
            # BRIN индекс для больших таблиц
            BrinIndex(
                fields=['data'],
                name='data_brin_idx'
            ),
            
            # Обычный BTree
            BTreeIndex(
                fields=['value'],
                name='value_btree_idx'
            ),
        ]

13. Анализ плана запроса

from django.db import connections

# Посмотреть план выполнения запроса
cursor = connections['default'].cursor()
cursor.execute("""
    EXPLAIN ANALYZE
    SELECT * FROM myapp_article
    WHERE author_id = 1 AND published_date > '2024-01-01'
""")
print(cursor.fetchall())

# Выведет информацию об использовании индекса

14. Best practices

# ✓ Хорошо: Индекс на поле для фильтрации
class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
    status = models.CharField(max_length=10, db_index=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)

# ✓ Хорошо: Составной индекс для JOIN
class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    
    class Meta:
        indexes = [
            models.Index(fields=['post', 'author']),
        ]

# ✗ Плохо: Слишком много индексов
class BadModel(models.Model):
    field1 = models.CharField(db_index=True)
    field2 = models.CharField(db_index=True)
    field3 = models.CharField(db_index=True)
    # Индекс на каждое поле замедляет write операции!

# ✓ Хорошо: Индекс с условием для экономии
class Document(models.Model):
    status = models.CharField(max_length=10)
    content = models.TextField()
    
    class Meta:
        indexes = [
            # Индекс только на active документы
            models.Index(
                fields=['status'],
                condition=models.Q(status='published')
            ),
        ]

15. Инструменты для анализа

# Посмотреть все миграции
python manage.py showmigrations

# SQL для миграции
python manage.py sqlmigrate app 0002

# Чтобы увидеть индексы в БД (PostgreSQL)
psql -c "\di" database_name

# MySQL
SHOW INDEXES FROM table_name;

Заключение

Индексы в Django создаются через:

  • db_index=True на полях (простой способ)
  • Meta.indexes со списком models.Index (для составных и сложных)
  • Meta.constraints для уникальных индексов
  • Миграции для добавления/удаления

Главное правило: создавай индексы на полях, по которым часто фильтруешь, но помни что каждый индекс замедляет write операции. Баланс — ключ к производительности.

Как создаются индексы в Django ORM? | PrepBro