When a shopping cart is checked out, you need to save an order into the database. Orders will contain information about customers and the products they are buying.
Create a new application for managing customer orders using the following command:
python manage.py startapp orders
Edit the settings.py
file of your project and add 'orders'
to the INSTALLED_APPS
setting as follows:
INSTALLED_APPS = (
# ...
'orders',
)
You have activated the new application.
You will need a model to store the order details, and a second model to store items bought, including their price and quantity. Edit the models.py
file of the orders
application and add the following code to it:
from django.db import models from shop.models import Product class Order(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) email = models.EmailField() address = models.CharField(max_length=250) postal_code = models.CharField(max_length=20) city = models.CharField(max_length=100) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) paid = models.BooleanField(default=False) class Meta: ordering = ('-created',) def __str__(self): return 'Order {}'.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(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField(default=1) def __str__(self): return '{}'.format(self.id) def get_cost(self): return self.price * self.quantity
The Order
model contains several fields for customer information and a paid
boolean field, which defaults to False
. Later on, we are going to use this field to differentiate between paid and unpaid orders. We also define a get_total_cost()
method to obtain the total cost of the items bought in this order.
The OrderItem
model allows us to store the product, quantity, and price paid for each item. We include get_cost()
to return the cost of the item.
Run the next command to create initial migrations for the orders
application:
python manage.py makemigrations
You will see the following output:
Migrations for 'orders': 0001_initial.py: - Create model Order - Create model OrderItem
Run the following command to apply the new migration:
python manage.py migrate
Your order models are now synced to the database.
Let's add the order models to the administration site. Edit the admin.py
file of the orders
application to make it look like this:
from django.contrib import admin from .models import Order, OrderItem class OrderItemInline(admin.TabularInline): model = OrderItem raw_id_fields = ['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)
We use a ModelInline
for the OrderItem
model to include it as an inline in the OrderAdmin
class. An inline allows you to include a model for appearing on the same edit page as the parent model.
Run the development server with the command python manage.py runserver
, and then open http://127.0.0.1:8000/admin/orders/order/add/
in your browser. You will see the following page:
We need to use the order models that we just created to persist the items contained in the shopping cart when the user finally wishes to place an order. The functionality for creating a new order will work as follows:
Order
instance with the data entered by users, and then we create an associated OrderItem
instance for each item in the cart.First, we need a form to enter order details. Create a new file inside the orders
application directory and name it forms.py
. Add the following code to it:
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']
This is the form that we are going to use for creating new Order
objects. Now, we need a view to handle the form and create a new order. Edit the views.py
file of the orders
application and add the following code to it:
from django.shortcuts import render from .models import OrderItem from .forms import OrderCreateForm from cart.cart import Cart def order_create(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']) # clear the cart cart.clear() return render(request, 'orders/order/created.html', {'order': order}) else: form = OrderCreateForm() return render(request, 'orders/order/create.html', {'cart': cart, 'form': form})
In the order_create
view, we will obtain the current cart from the session with cart = Cart(request)
. Depending on the request method, we will perform the following tasks:
OrderCreateForm
form and renders the template orders/order/create.html
.order = form.save()
to create a new Order
instance. We will then save it to the database, and then store it in the order
variable. After creating the order
, we will iterate over the cart items and create OrderItem
for each of them. Finally, we will clear the cart contents.Now, create a new file inside the orders
application directory and name it urls.py
. Add the following code to it:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^create/$', views.order_create, name='order_create'), ]
This is the URL pattern for the order_create
view. Edit the urls.py
file of myshop
and include the following pattern. Remember to place it before the shop.urls
pattern:
url(r'^orders/', include('orders.urls', namespace='orders')),
Edit the cart/detail.html
template of the cart
application and replace this line:
<a href="#" class="button">Checkout</a>
Replace this with the following ones:
<a href="{% url "orders:order_create" %}" class="button"> Checkout </a>
Users can now navigate from the cart detail page to the order form. We still need to define templates for placing orders. Create the following file structure inside the orders
application directory:
templates/ orders/ order/ create.html created.html
Edit the orders/order/create.html
template and include the following code:
{% extends "shop/base.html" %} {% block title %} Checkout {% endblock %} {% block content %} <h1>Checkout</h1> <div class="order-info"> <h3>Your order</h3> <ul> {% for item in cart %} <li> {{ item.quantity }}x {{ item.product.name }} <span>${{ item.total_price }}</span> </li> {% endfor %} </ul> <p>Total: ${{ cart.get_total_price }}</p> </div> <form action="." method="post" class="order-form"> {{ form.as_p }} <p><input type="submit" value="Place order"></p> {% csrf_token %} </form> {% endblock %}
This template displays the cart items including totals and the form to place an order.
Edit the orders/order/created.html
template and add the following code:
{% extends "shop/base.html" %} {% block title %} Thank you {% endblock %} {% block content %} <h1>Thank you</h1> <p>Your order has been successfully completed. Your order number is <strong>{{ order.id }}</strong>.</p> {% endblock %}
This is the template that we render when the order is successfully created. Start the web development server to track new files. Open http://127.0.0.1:8000/
in your browser, add a couple of products to the cart, and continue to checkout the page. You will see a page like the following one:
Fill in the form with the valid data and click on Place order button. The order will be created and you will see a success page like this: