Как добавить несколько товаров в корзину?

Django Admin — довольно мощный инструмент при работе с данными в стиле CRUD (создание, чтение, модификация, удаление). Одна из особенностей, о которой многие (даже опытные) разработчики не подозревают, — это наличие нескольких строк «many-one» или «many-to-many» на одной странице, как показано на этом рисунке:

Потрясно, правда? А рецепт простой, давайте посмотрим.

Data (Models)

from django.db import models


class Item(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(default=0, decimal_places=2, max_digits=10)

    def __str__(self):
        return f"{self.name}: ${self.price}"


class Cart(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    paid = models.BooleanField(default=False)

    def total_price(self):
        return sum([
            cart_item.total()
            for cart_item in CartItem.objects.filter(cart=self)
        ])

    def __str__(self):
        return f"{self.created}, ${self.total_price()}: {self.paid}"


class CartItem(models.Model):
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    count = models.IntegerField(default=1)

    def total(self):
        return self.count * self.item.price

    def __str__(self):
        return f"{self.item.name}, " \
               f"${self.item.price} * {self.count} = ${self.total()}"

Модели просты: у нас есть Items, Carts и таблица CartItem для отношения многие-ко-многим между Items и Carts. Мы не могли использовать здесь models.ManyToManyField, так как нам нужно дополнительное поле “count” внутри отношений. Когда я сам создаю отношения, это кажется более прозрачным.

Admin

Внутри Admin мы не просто регистрируем модели напрямую, нам нужно создать классы ModelAdmin и StackedInline.

Идея заключается в следующем. Сначала мы создаем многострочное представление (multiline view) для поддержки CRUD в таблице CartItem. А затем мы добавляем это представление к представлению корзины.

Это делается следующим образом:

from django.contrib import admin
from shop.models import Cart, Item, CartItem


class ItemInline(admin.StackedInline):
    model = CartItem
    extra = 5


class CartAdmin(admin.ModelAdmin):
    inlines = [ItemInline]
    list_display = ["created", "total_price", "paid"]


admin.site.register(Cart, CartAdmin)
admin.site.register(Item)

Вот и все. Полезная вещь и не занимает много времени.


Скоро состоится открытое занятие «О "регулярках" и не только», на котором:
- Поговорим о роли регулярных выражений в разработке.
- Оценим эффект от re.compile.
- Попробуем переписать код без использования регулярок и посмотрим, стоит ли тратить время на изучение этой непростой темы.

Регистрируйтесь по ссылке.

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


  1. danilovmy
    27.09.2022 09:06

    Я бы не рискнул StackedInline называть Multiline-View. Стакед Инлайн это менеджер для организации работы формсета внутри Model admin form со своим косячным способом этот формсет отрендерить. Multiline-View это например List View - отношения к инлайнам не имеет. Тимуру спасибо за копи паст документации отсюда https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#inlinemodeladmin-objects