Применение купона к корзине

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

  1. Пользователь добавляет товары в корзину для покупок.
  2. Пользователь может ввести код купона в форме, отображаемой на странице корзины для покупок.
  3. Когда пользователь вводит код купона и отправляет форму, мы ищем существующий купон с заданным кодом, который в настоящее время действителен. Мы должны проверить, что код купона совпадает с введенным пользователем, атрибут active имеет значение True, а текущее значение datetime находится между значениями valid_from и valid_to.
  4. Если купон найден, мы его сохраняем в сессии пользователя и выводим на экран корзину, включая применяемую к ней скидку, и обновленную общую сумму.
  5. Когда пользователь размещает заказ, мы сохраняем купон в этот заказ.

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

from django import forms


class CouponApplyForm(forms.Form):
    code = forms.CharField()

Это форма, которая будет использоваться пользователем для ввода кода купона. Измените файл views.py в приложении coupons и добавьте в него следующий код:

from django.shortcuts import render, redirect
from django.views.decorators.http import require_POST
from django.utils import timezone
from .models import Coupon
from .forms import CouponApplyForm


@require_POST
def coupon_apply(request):
    now = timezone.now()
    form = CouponApplyForm(request.POST)
    if form.is_valid():
        code = form.cleaned_data['code']
        try:
            coupon = Coupon.objects.get(code__iexact=code,
                                        valid_from__lte=now,
                                        valid_to__gte=now,
                                        active=True)
            request.session['coupon_id'] = coupon.id
        except Coupon.DoesNotExists:
            request.session['coupon_id'] = None
    return redirect('cart:cart_detail')

Представление coupon_apply проверяет купон и сохраняет его в сессии пользователя. Мы применяем декоратор require_POST к этому представлению, чтобы ограничить его учетом запросов. В представлении мы выполняем следующие задачи:

  1. Мы создаем экземпляр формы CouponApplyForm, используя учтенные данные, и проверяем, что форма является валидной.
  2. Если форма является валидной, мы получим код, введенный пользователем из формы cleaned_data. Мы пытаемся извлечь объект Coupon с данным кодом. Мы используем поиск в поле iexact, чтобы проверить точное совпадение без учета регистра. Купон должен быть активен в данный момент (active=True) и действителен для текущего datetime. Мы используем функцию Джанго timezone.now(), чтобы получить текущую дату и время, сопоставленные с часовым поясом, и сравнить ее с полями valid_from и valid_to, выполняющими lte(меньше или равными) и gte(больше или равным).
  3. Идентификатор купона хранится в сессии пользователя.
  4. Мы перенаправим пользователя на URL-адрес cart_detail, чтобы отобразить корзину с примененным купоном.

Нам нужен шаблон URL-адреса для представления coupon_apply. Создайте новый файл в каталоге приложения coupons и назовите его urls.py. Добавьте в него следующий код:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'^apply/$', views.coupon_apply, name='apply'),
]

Затем отредактируйте основной urls.py проекта myshop и включите шаблоны URL-адресов купонов следующим образом:

url(r'^coupons/', include('coupons.urls', namespace='coupons')),

Не забудьте поместить этот шаблон перед шаблоном shop.urls.

Теперь отредактируйте файл cart.py приложения cart. Включите следующий импорт:

from coupons.models import Coupon

Добавьте следующий код в конец метода __init__() класса Cart для инициализации купона из текущей сессии:

# сохранение текущего примененного купона
self.coupon_id = self.session.get('coupon_id')

В этом коде мы пытаемся получить session key coupon_id из текущей сессии и сохранить его значение в объекте Cart. Добавьте в объект Cart следующие методы:

@property
def coupon(self):
    if self.coupon_id:
        return Coupon.objects.get(id=self.coupon_id)
    return None

def get_discount(self):
    if self.coupon:
        return (self.coupon.discount / Decimal('100')) * self.get_total_price()
    return Decimal('0')

def get_total_price_after_discount(self):
    return self.get_total_price() - self.get_discount()

Вот что делают эти методы:

  • coupon() : Этот метод определяется как property. Если корзина содержит функцию coupon_id, возвращается объект Coupon с заданным id.
  • get_discount() : Если корзина содержит купон, мы получаем скидку по ставке и возвращаем сумму, которая будет вычтена из общей суммы корзины.
  • get_total_price_after_discount() : Мы возвращаем общую сумму корзины после вычета суммы, возвращенной методом get_discount().

Теперь класс Cart готов обработать купон, примененный к текущей сессии, и применить соответствующую скидку.

Давайте включим систему купонов в detail view корзины. Отредактируйте файл views.py приложения cart и добавьте следующий импорт в верхнюю часть файла:

from coupons.forms import CouponApplyForm

Далее, отредактируйте представление cart_detail и добавьте в него новую форму следующим образом:

def cart_detail(request):
    cart = Cart(request)
    for item in cart:
        item['update_quantity_form'] = CartAddProductForm(
                            initial={'quantity': item['quantity'],
                            'update': True})
    coupon_apply_form = CouponApplyForm()
    return render(request,
                  'cart/detail.html',
                  {'cart': cart,
                   'coupon_apply_form': coupon_apply_form})

Измените шаблон cart/detail.html приложения cart и найдите следующие строки:

<tr class="total">
    <td>Total</td>
    <td colspan="4"></td>
    <td class="num">${{ cart.get_total_price }}</td>
</tr>

Замените их следующими:

{% if cart.coupon %}
    <tr class="subtotal">
        <td>Subtotal</td>
        <td colspan="4"></td>
        <td class="num">${{ cart.get_total_price }}</td>
    </tr>
    <tr>
        <td>
            "{{ cart.coupon.code }}" coupon
            ({{ cart.coupon.discount }}% off)
        </td>
        <td colspan="4"></td>
        <td class="num neg">
            - ${{ cart.get_discount|floatformat:"2" }}
        </td>
    </tr>
{% endif %}
<tr class="total">
    <td>Total</td>
    <td colspan="4"></td>
    <td class="num">
        ${{ cart.get_total_price_after_discount|floatformat:"2" }}
    </td>
</tr>

Это код для отображения дополнительного купона и его скидки. Если корзина содержит купон, мы выводим первую строку, включая общую сумму корзины в качестве промежуточного итога. Затем мы используем вторую строку для отображения текущего купона, примененного к корзине. Наконец, мы отображаем общую цену, включая скидки, вызвав метод get_total_price_after_discount() объекта cart.

В том же файле следует включить следующий код после тега </table>:

<form action="{% url "coupons:apply" %}" method="post">
    {{ coupon_apply_form }}
    <input type="submit" value="Apply">
    {% csrf_token %}
</form>

Будет отображена форма для ввода кода купона и применения его к текущей корзине.

Откройте в браузере http://127.0.0.1:8000/, добавьте товар в корзину и примените купон, созданный с помощью ввода его кода в форму. В корзине отобразится скидка по купону:

Давайте добавим купон к следующему шагу процесса покупки. Измените шаблон orders/order/create.html приложения orders и найдите следующие строки:

<ul>
    {% for item in cart %}
        <li>
            {{ item.quantity }}x {{ item.product.name }}
            <span>${{ item.total_price }}</span>
        </li>
    {% endfor %}
</ul>

Замените их следующим кодом:

<ul>
    {% for item in cart %}
        <li>
            {{ item.quantity }}x {{ item.product.name }}
            <span>${{ item.total_price }}</span>
        </li>
    {% endfor %}
    {% if cart.coupon %}
        <li>
            "{{ cart.coupon.code }}" ({{ cart.coupon.discount }}% off)
            <span>- ${{ cart.get_discount|floatformat:"2" }}</span>
        </li>
    {% endif %}
</ul>

Заказ должен теперь включать примененный купон, если он есть. Теперь найдите следующую строку:

<p>Total: ${{ cart.get_total_price }}</p>

Замените ее на следующий код:

<p>Total: ${{ cart.get_total_price_after_discount|floatformat:"2" }}</p>

При этом общая цена будет также рассчитываться путем применения скидки купона.

Откройте в браузере http://127.0.0.1:8000/orders/create/. Вы увидите, что заказ примененный купон:

Теперь пользователи могут применять купоны к своим покупкам.

results matching ""

    No results matching ""