📖 Теория: Django модели и Admin перед практикумом

Краткое напоминание ключевых концепций

⚡ Ключевые концепции практикума 5

  • ForeignKey(Model, on_delete=PROTECT): запрет удаления родителя при наличии дочерних записей
  • OneToOneField(Model, on_delete=CASCADE): связанная запись удаляется вместе с родителем
  • OneToOneField(Model, null=True, on_delete=SET_NULL): поле обнуляется при удалении
  • DecimalField(max_digits=10, decimal_places=2): точные денежные значения
  • Meta: ordering, verbose_name_plural, get_latest_by
  • Admin: list_display + list_editable — поле должно быть в обоих списках

1. Схема данных практикума

В практикуме 5 мы строим систему управления складом. Вот схема связей между моделями:

Category ←── Product ──→ Supplier
                │
                └──(OneToOne)── ProductDetail

Address ←──(OneToOne)── Customer ──→ Order ──→ OrderItem ──→ Product
  • Category — категория товара (уникальное имя, сортировка по алфавиту)
  • Supplier — поставщик (имя, email, телефон — все уникальные)
  • Product — товар (связан с Category и Supplier через ForeignKey PROTECT)
  • ProductDetail — детали продукта (OneToOne → Product, CASCADE)
  • Address — адрес доставки
  • Customer — покупатель (OneToOne → Address, SET_NULL; поддержка мягкого удаления)
  • Order — заказ (ForeignKey → Customer, PROTECT)
  • OrderItem — позиция заказа (ForeignKey → Order CASCADE, ForeignKey → Product PROTECT)

2. on_delete — стратегии при удалении родителя

Стратегия Поведение Пример применения
CASCADE Удалить дочерние записи ProductDetail при удалении Product
PROTECT Запретить удаление родителя Category нельзя удалить, пока есть товары
SET_NULL Обнулить поле (нужен null=True) Customer.address при удалении адреса
SET_DEFAULT Установить значение по умолчанию Редко используется

3. DecimalField для денег

Для хранения денежных значений всегда используйте DecimalField, а не FloatField. Float имеет погрешности при округлении, что недопустимо для финансовых данных.

price = models.DecimalField(max_digits=10, decimal_places=2)
# max_digits=10 — всего 10 цифр (включая после точки)
# decimal_places=2 — 2 знака после точки
# Диапазон: до 99_999_999.99

4. Мягкое удаление (soft delete)

В модели Customer реализован паттерн мягкого удаления: вместо физического удаления записи устанавливается флаг deleted=True и дата deleted_at. Это позволяет восстанавливать данные и сохранять историю.

class Customer(models.Model):
    deleted = models.BooleanField(default=False)
    deleted_at = models.DateTimeField(null=True, blank=True)

    # Пример использования:
    # customer.deleted = True
    # customer.deleted_at = timezone.now()
    # customer.save()

5. Meta: get_latest_by

Атрибут get_latest_by указывает поле для метода Model.objects.latest(). Без него .latest() требует явного аргумента.

class Order(models.Model):
    order_date = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-order_date']
        get_latest_by = 'order_date'

# Использование:
latest_order = Order.objects.latest()  # работает без аргумента

6. Admin: list_editable

Атрибут list_editable позволяет редактировать поля прямо в списке записей, без открытия формы редактирования каждой записи. Важно: поле должно присутствовать в list_display.

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ['name', 'price', 'quantity', 'available']
    list_editable = ['price', 'quantity', 'available']
    # ✅ Все поля из list_editable есть в list_display

7. db_index на поле

Параметр db_index=True создаёт индекс БД по этому полю. Ускоряет выборки по полю, но замедляет вставку/обновление. Применяйте к полям, по которым часто фильтруете или ищете.

article = models.CharField(
    max_length=100,
    unique=True,
    help_text='Unique string product id',
    db_index=True  # уже создаётся при unique=True, здесь явно для наглядности
)
В Django поле с unique=True автоматически получает индекс. Указывать db_index=True дополнительно избыточно, но допустимо — как в задании практикума.