Хранение корзины покупок в сессиях

Необходимо создать простую структуру, которая может быть сериализована в JSON для хранения элементов корзины в сессии. Корзина должна включать следующие данные для каждого содержащегося в ней элемента:

  • id экземпляра Product
  • Количество товара, выбранное для данного продукта
  • Цена единицы для данного продукта

Поскольку цены на продукцию могут различаться, мы приближаемся к сохранению цены продукта вместе с продуктом, когда он добавляется в корзину. Таким образом, мы будем сохранять ту же цену, которую пользователи увидели при добавлении товара в корзину, даже если цена продукта изменится после этого.

Теперь необходимо управлять созданием корзин и связывать их с сеансами. Корзина покупок должна работать следующим образом:

  • Когда требуется корзина, мы проверяем, установлен ли пользовательский session key. Если в сессии не задана корзина, мы создадим новую корзину и сохраним ее в session key корзины.
  • Для последовательных запросов мы выполняем одну и ту же проверку и получая номенклатуры корзины из session key корзины. Мы извлекаем элементы корзины из базы данных и связанные с ними объекты продукта.

Измените файл settings.py проекта и добавьте в него следующий параметр:

CART_SESSION_ID = 'cart'

Это ключ, который мы собираемся использовать для хранения корзины в сессии пользователя.

Давайте создадим приложение для управления корзинами покупок. Откройте терминал и создайте новое приложение, запустив следующую команду из каталога проекта:

python manage.py startapp cart

Затем отредактируйте файл settings.py проекта и добавьте "cart" к параметру INSTALLED_APPS следующим образом:

INSTALLED_APPS = (
    # ...
    'shop',
    'cart',
)

Создайте новый файл в каталоге приложения cart и назовите его cart.py. Добавьте в него следующий код:

from decimal import Decimal
from django.conf import settings
from shop.models import Product


class Cart(object):

    def __init__(self, request):
        """
        Инициализируем корзину
        """
        self.session = request.session
        cart = self.session.get(settings.CART_SESSION_ID)
        if not cart:
            # save an empty cart in the session
            cart = self.session[settings.CART_SESSION_ID] = {}
        self.cart = cart

Это класс Cart, который позволит нам управлять корзиной для покупок. Требуется инициализация корзины с помощью объекта request. Мы храним текущую сессию с помощью self.session = request.session, чтобы сделать его доступным для других методов класса Cart. Во-первых, мы пытаемся получить корзину с текущей сессии с помощью self.session.get(settings.CART_SESSION_ID). Если в сессии отсутствует корзина, то мы создадим сессию с пустой корзиной, установив пустой словарь в сессии. Мы ожидаем, что наш словарь корзины будет использовать коды продуктов в качестве ключей и словарь с количеством и ценой в качестве значения для каждого ключа. Таким образом, мы можем гарантировать, что продукт не будет добавлен в корзину более одного раза; можно также упростить доступ к данным элементов корзины.

Создадим метод для добавления продуктов в корзину или обновления их количества. Добавьте следующие методы add() и save() в класс Cart:

def add(self, product, quantity=1, update_quantity=False):
    """
    Добавить продукт в корзину или обновить его количество.
    """
    product_id = str(product.id)
    if product_id not in self.cart:
        self.cart[product_id] = {'quantity': 0,
                                 'price': str(product.price)}
    if update_quantity:
        self.cart[product_id]['quantity'] = quantity
    else:
        self.cart[product_id]['quantity'] += quantity
    self.save()

def save(self):
    # Обновление сессии cart
    self.session[settings.CART_SESSION_ID] = self.cart
    # Отметить сеанс как "измененный", чтобы убедиться, что он сохранен
    self.session.modified = True

Метод add() принимает следующие параметры:

  • product : Экземпляр Product для добавления или обновления в корзине
  • quantity : Необязательное целое число для количества продукта. По умолчанию используется значение 1 .
  • update_quantity : Это логическое значение, которое указывает, требуется ли обновление количества с заданным количеством (True), или же новое количество должно быть добавлено к существующему количеству (False).

id продукта используется в качестве ключа в словаре содержимого корзины. id продукта преобразуется в строку, так как Джанго использует JSON для сериализации данных сессии, а JSON разрешает только имена строк. id продукта — это ключ, а значение, которое мы сохраняем, — словарь с количеством и ценой для продукта. Цена продукта преобразуется из десятичного разделителя в строку, чтобы сериализовать его. Наконец, мы вызываем метод save(), чтобы сохранить корзину в сессии.

Метод save() сохраняет все изменения в корзине в сессии и помечает сессию как modified с помощью session.modified = True. Это говорит о том, что сессия modified и должна быть сохранена.

Нам также нужен метод для удаления продуктов из корзины. Добавьте следующий метод в класс Cart:

def remove(self, product):
    """
    Удаление товара из корзины.
    """
    product_id = str(product.id)
    if product_id in self.cart:
        del self.cart[product_id]
        self.save()

Метод remove() удаляет заданный продукт из словаря корзины и вызывает метод save() для обновления корзины в сессии.

Нам придется перебрать элементы, содержащихся в корзине, и получить доступ к соответствующим экземплярам Product. Для этого в классе можно определить метод __iter__(). Добавьте следующий метод в класс Cart:

def __iter__(self):
    """
    Перебор элементов в корзине и получение продуктов из базы данных.
    """
    product_ids = self.cart.keys()
    # получение объектов product и добавление их в корзину
    products = Product.objects.filter(id__in=product_ids)
    for product in products:
        self.cart[str(product.id)]['product'] = product

    for item in self.cart.values():
        item['price'] = Decimal(item['price'])
        item['total_price'] = item['price'] * item['quantity']
        yield item

В методе __iter__() мы извлекаем экземпляры продукта, присутствующие в корзине, чтобы включить их в номенклатуры корзины. Наконец, мы проходим по элементам корзины, преобразуя цену номенклатуры обратно в десятичное число и добавляя атрибут total_price к каждому элементу. Теперь можно легко выполнить итерацию по товарам в корзине.

Нам также нужен способ вернуть общее количество товаров в корзине. Когда функция len() выполняется на объекте, Python вызывает метод __len__() для извлечения ее длины. Мы собираемся определить пользовательский метод __len__(), чтобы вернуть общее количество элементов, хранящихся в корзине. Добавьте следующий метод __len__() в класс Cart:

def __len__(self):
    """
    Подсчет всех товаров в корзине.
    """
    return sum(item['quantity'] for item in self.cart.values())

Мы возвращаем сумму количества всех товаров.

Добавьте следующий метод для расчета общей стоимости товаров в корзине:

def get_total_price(self):
    """
    Подсчет стоимости товаров в корзине.
    """
    return sum(Decimal(item['price']) * item['quantity'] for item in
               self.cart.values())

И, наконец, добавьте метод для очистки сеанса корзины:

def clear(self):
    # удаление корзины из сессии
    del self.session[settings.CART_SESSION_ID]
    self.session.modified = True

Теперь наш класс Cart готов к управлению корзиной для покупок.

results matching ""

    No results matching ""