Дикий Григорий

Full-stack веб-разработчик

Интернет-магазин на Django. Страница заказов. Часть 6

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

$ python manage.py startapp orders

Теперь давайте добавим наше приложение в INSTALLED_APPS в файле settings.py директории myshop:

INSTALLED_APPS = [
    ...
    'orders'
]

Теперь когда наше приложение подключено к Django можно создать модели для orders. Для этого откроем models.py в директории orders и напишем следующее:

from django.db import models
from shop.models import Product


class Order(models.Model):
    first_name = models.CharField(verbose_name='Имя', max_length=50)
    last_name = models.CharField(verbose_name='Фамилия', max_length=50)
    email = models.EmailField(verbose_name='Email')
    address =  models.CharField(verbose_name='Адрес', max_length=250)
    postal_code = models.CharField(verbose_name='Почтовый код', max_length=20)
    city = models.CharField(verbose_name='Город', max_length=100)
    created = models.DateTimeField(verbose_name='Создан', auto_now_add=True)
    updated = models.DateTimeField(verbose_name='Обновлен', auto_now=True)
    paid = models.BooleanField(verbose_name='Оплачен', default=False)

    class Meta:
        ordering = ('-created', )
        verbose_name = 'Заказ'
        verbose_name_plural = 'Заказы'

    def __str__(self):
        return 'Заказ: {}'.format(self.id)

    def get_total_cost(self):
        return sum(item.get_cost() for item in self.items.all())

class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name='items')
    product = models.ForeignKey(Product, related_name='order_items')
    price = models.DecimalField(verbose_name='Цена', max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField(verbose_name='Количество', default=1)

    def __str__(self):
        return '{}'.format(self.id)

    def get_cost(self):
        return self.price * self.quantity

Теперь давайте синхронизируем нашу базу данных с созданными моделями. Для этого в консоли выполним следующие команды:

$ python manage.py makemigrations
$ python manage.py migrat

Админ-панель

Теперь настало время добавить наше приложение в встроенную админ-панель django. Для этого добавим в файл admin.py следующее содержание:

from django.contrib import admin
from .models import Order, OrderItem


class OrderItemInline(admin.TabularInline):
    model = OrderItem
    raw_id_field = ['product']

class OrderAdmin(admin.ModelAdmin):
    list_display = ['id', 'first_name', 'last_name', 'email', 'address',
                    'postal_code', 'city', 'paid', 'created', 'updated']
    list_filter = ['paid', 'created', 'updated']
    inlines = [OrderItemInline]

admin.site.register(Order, OrderAdmin)

Давайте зайдем в админ-панель и посмотрим как это выглядит:

Пользовательский заказ

Нам нужно чтобы пользователь сам смог оформлять заказ и добавлять его на обработку. Мы предоставим форму заказа. Давайте первым делом создадим форму, для этого создадим и отредактируем файл forms.py:

from django import forms
from .models import Order


class OrderCreateForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ['first_name', 'last_name', 'email', 'address', 'postal_code',
                  'city']

Для того, чтобы обрабатывать форму добавим в файл views.py из каталога orders следующий код:

from django.shortcuts import render
from .models import OrderItem
from .forms import OrderCreateForm
from cart.cart import Cart


def OrderCreate(request):
    cart = Cart(request)
    if request.method == 'POST':
        form = OrderCreateForm(request.POST)
        if form.is_valid():
            order = form.save()
            for item in cart:
                OrderItem.objects.create(order=order, product=item['product'],
                                         price=item['price'],
                                         quantity=item['quantity'])
            cart.clear()
            return render(request, 'orders/order/created.html', {'order': order})

    form = OrderCreateForm()
    return render(request, 'orders/order/create.html', {'cart': cart,
                                                        'form': form})

Во вьюхе мы обрабатываем нашу форму. Первым делом проверяем, если был POST запрос на этот URL и если был, то проверяем если данные в форме валидны. Если эти проверки пройдены, то мы сохраняем данные из формы в БД. Теперь нужно выгрузить все товары из нашей сессии в БД и связать с нашим заказом. Для этого мы итерируем товары в сессиях, попутно создавая в нашей БД записи используя OrderItem.objects.create(). После этого мы очищаем сессию.

Теперь настала пора роутинга. Для этого создадим и запишем в файл urls.py в директории orders:

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


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

Подключим этот роутинг в главный urls.py в папке myshop:

urlpatterns = [
    ...
    url(r'^order/', include('orders.urls', namespace='orders')),
]

Теперь давайте добавим возможность создавать заказ в файле шаблона cart/detail.html в папке cart/templartes для этого сроку:

<a href="#" class="btn">Оформить заказ</a> 

заменим на:

<a href="{% url "orders:OrderCreate" %}" class="btn">Оформить заказ</a>

Нам осталось только создать шаблон. Первым делом создадим следующую структуру файлов:

templates/
    orders/
        order/
            create.html
            created.html

Заполним файл create.html следующим содержимым:

{% extends "shop/base.html" %}

{% block title %}
  Оформление заказа
{% endblock %}

{% block content %}
  <h1>Оформление заказа</h1>
  <div class="col-sm-4">
    <h2>Заполните форму</h2>
    <form class="order-form" action="." method="post">
      {{ form.as_p }}
      {% csrf_token %}
      <input type="submit" value="Отправить">
    </form>
  </div>

  <div class="order-info col-sm-8">
    <h2>Ваш заказ</h2>
    <ul>
      {% for item in cart  %}
        <li>
          {{ item.product.name }} | Количество: {{ item.quantity }} | Цена: {{ item.total_price }}
        </li>
      {% endfor %}
    </ul>
  </div>
{% endblock %}

А также заполним файл created.html, который будет от отображаться при успешном заказе:

{% extends "shop/base.html" %}

{% block title %}
  Спасибо за заказ
{% endblock %}

{% block content %}
  <h1>Спасибо за заказ</h1>
  <p>Идентификатор вашего заказа: {{ order.id }}</p>
{% endblock %}

В итоге вы при заказе должны видеть следующую станицу заказа:

После успешного заказа вы перейдете на страницу:

А в самой админ-панели заказ будет отображаться следующим образом:

Репозиторий проекта: github