Назовите распространенные ошибки и антипаттерны при работе с Django ORM

«Назовите распространенные ошибки и антипаттерны при работе с Django ORM» — вопрос из категории Django, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Распространенные ошибки и антипаттерны в Django ORM часто приводят к неэффективному коду, багам и проблемам с производительностью.

  1. Проблема N+1 запросов

    • Ошибка: Получение связанных объектов в цикле без использования select_related или prefetch_related.
    • Последствие: Резкое увеличение количества запросов к БД и деградация производительности.
    # Антипаттерн: N+1 запросов
    for book in Book.objects.all():
        print(book.author.name) # Новый запрос для каждого автора
    
    # Правильно: 1 запрос
    for book in Book.objects.select_related('author'):
        print(book.author.name)
  2. Использование len(queryset) вместо .count()

    • Ошибка: Подсчет количества объектов путем загрузки всего QuerySet в память.
    • Последствие: Избыточное потребление памяти и медленная работа на больших таблицах. Правильно использовать queryset.count(), который транслируется в эффективный SELECT COUNT(*).
  3. Создание объектов в цикле вместо bulk_create()

    • Ошибка: Вызов Model.objects.create() или model.save() внутри цикла.
    • Последствие: Один INSERT запрос на каждую итерацию. bulk_create() объединяет все операции в один запрос.
  4. Использование filter() вместо get() для уникального объекта

    • Ошибка: Искать один объект через MyModel.objects.filter(pk=1), что возвращает QuerySet.
    • Последствие: Требуется дополнительная обработка результата (.first(), [0]). Метод get() предназначен именно для этого: он возвращает объект или вызывает исключение (DoesNotExist или MultipleObjectsReturned), что делает код более строгим и предсказуемым.
  5. Отсутствие атомарных транзакций

    • Ошибка: Выполнение нескольких операций записи (создание, обновление, удаление), которые должны быть единым целым, без обертывания в transaction.atomic().
    • Последствие: Риск нарушения целостности данных, если одна из операций завершится с ошибкой.
  6. Путаница между null=True и blank=True

    • Ошибка: Неправильное использование этих атрибутов поля.
    • Последствие: null=True разрешает хранить NULL в базе данных (для полей, не являющихся текстовыми). blank=True относится к валидации форм Django и разрешает оставлять поле пустым. Неправильное использование может привести к ошибкам на уровне БД или валидации.