Ошибка 1: Не указан on_delete
TypeError: ForeignKey() missing 1 required positional argument: 'on_delete'
Причина: в Django 2.0+ параметр on_delete стал обязательным. В старых учебниках встречается код без него.
Неправильно
category = models.ForeignKey(Category)
# TypeError в Django 2.0+
Правильно
category = models.ForeignKey(
Category,
on_delete=models.PROTECT
)
Ошибка 2: null=True без blank=True
Причина: null=True — это настройка БД (разрешить NULL). blank=True — настройка Django-форм (разрешить пустое значение при валидации). Оба нужны вместе для опциональных полей.
Неправильно
description = models.TextField(null=True)
# В форме обязательно для заполнения!
Правильно
description = models.TextField(
null=True, blank=True
)
# И в БД NULL, и форма принимает пустое
Ошибка 3: list_editable без list_display
ImproperlyConfigured: 'ProductAdmin.list_editable[0]', 'price'
is not contained in 'list_display'.
Причина: Django требует, чтобы поле из list_editable было видно в таблице (присутствовало в list_display). Иначе пользователь не видит что редактирует.
Неправильно
list_display = ['name', 'category']
list_editable = ['price'] # price не в list_display!
Правильно
list_display = ['name', 'category', 'price']
list_editable = ['price'] # price есть в list_display
Ошибка 4: Пропущена запятая в списке list_display
FieldDoesNotExist: Product has no field named 'quantityarticle'
Причина: Python автоматически склеивает соседние строковые литералы без оператора. Если между элементами кортежа/списка нет запятой, строки объединяются в одну.
Неправильно (из источника — опечатка)
list_display = [
'quantity' # нет запятой!
'article',
]
# Python видит: 'quantityarticle'
Правильно
list_display = [
'quantity', # запятая обязательна
'article',
]
Ошибка 5: ProtectedError при удалении связанного объекта
django.db.models.deletion.ProtectedError:
("Cannot delete some instances of model 'Category' because they are
referenced through protected foreign keys: 'Product.category'", ...)
Причина: on_delete=models.PROTECT намеренно запрещает удаление Category, пока в ней есть Product. Это защита данных.
Решение: сначала удалить или перенести в другую категорию все продукты, затем удалить категорию.
# Перенести все продукты перед удалением категории:
other_category = Category.objects.get(name='Other')
Product.objects.filter(category=old_category).update(category=other_category)
old_category.delete() # теперь безопасно
Ошибка 6: Забыли сделать makemigrations после изменения models.py
Причина: изменения в models.py не попадают в БД автоматически — нужно явно создать миграцию и применить её.
python manage.py makemigrations # создать файл миграции
python manage.py migrate # применить к БД
Ошибка 7: FloatField вместо DecimalField для денег
Причина: FloatField хранит числа как IEEE 754 float — с погрешностью. Для финансовых данных нужен DecimalField.
Неправильно
price = models.FloatField()
Правильно
price = models.DecimalField(
max_digits=10, decimal_places=2
)