Django 购物车与订单微服务开发指南
1. 创建购物车表单
在已经有了模型的基础上,我们要添加一个新表单,用于在页面上显示购物车数据以便编辑。具体操作步骤如下:
1. 打开
gamestore/main/
目录下的
forms.py
文件,在文件末尾添加以下代码:
ShoppingCartFormSet = inlineformset_factory(
ShoppingCart,
ShoppingCartItem,
fields=('quantity', 'price_per_unit'),
extra=0,
widgets={
'quantity': forms.TextInput({
'class': 'form-control quantity',
}),
'price_per_unit': forms.HiddenInput()
}
)
这里使用
inlineformset_factory
函数创建了一个内联表单集。内联表单集适用于通过外键处理相关对象的情况,在我们的场景中,
ShoppingCart
模型与
ShoppingCartItem
模型相关联,这种方式非常方便。
我们给
inlineformset_factory
函数传递了几个参数:
- 第一个参数是父模型
ShoppingCart
。
- 第二个参数是子模型
ShoppingCartItem
。
- 由于在购物车中我们只想编辑商品数量并移除商品,所以传入一个包含要在页面上渲染的
ShoppingCartItem
字段的元组,这里是
quantity
和
price_per_unit
。
-
extra
参数指定表单是否应在表单上渲染任何空的额外行,在我们的例子中不需要,因为我们不想在购物车视图中添加额外的商品。
- 最后一个参数
widgets
用于指定字段在表单中的渲染方式。
quantity
字段将渲染为文本输入框,而
price_per_unit
我们不希望其可见,所以将其定义为隐藏输入框,这样在提交表单时它会被发送回服务器。
- 在同一文件中添加必要的导入语句:
from django.forms import inlineformset_factory
from .models import ShoppingCartItem
from .models import ShoppingCart
-
打开
views.py文件,添加一个基于类的视图。首先添加一些导入语句:
from django.views.generic.edit import UpdateView
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from django.db.models import Sum, F, DecimalField
from .models import ShoppingCart
from .models import ShoppingCartItem
from .forms import ShoppingCartFormSet
然后创建类:
class ShoppingCartEditView(UpdateView):
model = ShoppingCart
form_class = ShoppingCartFormSet
template_name = 'main/cart.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
items = ShoppingCartItem.objects.get_items(self.object)
context['is_cart_empty'] = (items.count() == 0)
order = items.aggregate(
total_order=Sum(F('price_per_unit') * F('quantity'),
output_field=DecimalField())
)
context['total_order'] = order['total_order']
return context
def get_object(self):
try:
return ShoppingCart.objects.get_by_user(self.request.user)
except ShoppingCart.DoesNotExist:
new_cart = ShoppingCart.objects.create_cart(self.request.user)
new_cart.save()
return new_cart
def form_valid(self, form):
form.save()
return HttpResponseRedirect(reverse_lazy('user-cart'))
这个基于类的视图继承自
UpdateView
,与之前创建的视图略有不同。在 Django 中,视图实际上是可调用对象,使用类而不是函数时,我们可以利用继承和混合类的优势。这里使用
UpdateView
是因为它用于显示编辑现有对象的表单。
该类视图定义了几个属性:
-
model
:要在表单中编辑的模型。
-
form_class
:用于编辑数据的表单。
-
template_name
:用于渲染表单的模板。
我们重写了
get_context_data
方法,以便在表单上下文中包含一些额外的数据。首先调用基类的
get_context_data
方法构建上下文,然后获取当前购物车的商品列表,判断购物车是否为空,并将结果存储在
is_cart_empty
上下文中,该值可以在模板中访问。接着计算购物车中商品的总价值,通过
aggregate
函数计算每个商品的总价(
price_per_unit * quantity
)并求和,结果存储在
total_order
上下文中。
重写
get_object
方法,尝试获取请求用户的购物车,如果购物车不存在,则为用户创建一个新的购物车并返回。
实现
form_valid
方法,保存表单并将用户重定向回购物车页面。
2. 创建购物车视图
现在要创建购物车视图,该视图将渲染我们刚刚创建的表单,用户可以更改购物车中每个商品的数量并移除商品。如果购物车为空,将显示一条消息提示。具体步骤如下:
1. 打开
gamestore/main/
目录下的
urls.py
文件,添加以下 URL:
path(r'cart/', views.ShoppingCartEditView.as_view(), name='user-cart'),
这里定义了一个新的 URL
cart/
,访问该 URL 时将执行
ShoppingCartEditView
类视图,并为该 URL 定义了一个名称以方便使用。
-
在
gamestore/main/templates/main目录下创建一个名为cart.html的新文件,内容如下:
{% extends 'base.html' %}
{% block 'content' %}
{% load humanize %}
<div class='cart-details'>
<h3>{{ shoppingcart}}</h3>
{% if is_cart_empty %}
<h2>Your shopping cart is empty</h2>
{% else %}
<form action='' method='POST'>
{% csrf_token %}
{{ form.management_form }}
<button class='btn btn-success'>
<i class="fa fa-refresh" aria-hidden="true"></i>
Updated cart
</button>
<hr/>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">Game</th>
<th scope="col">Quantity</th>
<th scope="col">Price per unit</th>
<th scope="col">Options</th>
</tr>
</thead>
<tbody>
{% for item_form in form %}
<tr>
<td>{{item_form.instance.game.name}}</td>
<td class="{% if item_form.quantity.errors %}has-errors{% endif%}">
{{item_form.quantity}}
</td>
<td>${{item_form.instance.price_per_unit|floatformat:2|intcomma}}</td>
<td>{{item_form.DELETE}} Remove item</td>
{% for hidden in item_form.hidden_fields %}
{{ hidden }}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</form>
<hr/>
<div class='footer'>
<p class='total-value'>Total of your order:
${{total_order|floatformat:2|intcomma}}</p>
<button class='btn btn-primary'>
<i class="fa fa-check" aria-hidden="true"></i>
SEND ORDER
</button>
</div>
{% endif %}
</div>
{% endblock %}
这个模板很简单,只是遍历表单并渲染每个表单。需要注意的是,在模板开头加载了
humanize
,它是一组模板过滤器,用于在模板中格式化数据。我们使用
humanize
中的
intcomma
过滤器来格式化购物车中所有商品的总价,该过滤器将整数或浮点数转换为字符串,并每三位添加一个逗号。
3. 向购物车添加商品
接下来要实现一个视图,用于将商品添加到购物车中。具体步骤如下:
1. 打开
gamestore/main/
目录下的
url.py
文件,在
urlpatterns
列表中添加新的 URL:
path(r'cart/add/<int:game_id>/', views.add_to_cart),
通过这个 URL 可以传递游戏 ID,它将执行名为
add_to_cart
的视图。
-
打开
gamestore/main/目录下的views.py文件,添加导入语句:
from decimal import Decimal
from django.shortcuts import get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
-
打开
gametore/main目录下的models.py文件,在ShoppingCartItemManager类中添加一个新方法:
def get_existing_item(self, cart, game):
try:
return self.get(cart_id=cart.id,
game_id=game.id)
except ShoppingCartItem.DoesNotExist:
return None
get_existing_item
方法用于根据购物车 ID 和游戏 ID 查找
ShoppingCartItem
对象。如果在购物车中未找到该商品,则返回
None
;否则返回该购物车商品。
-
在
views.py文件中添加视图:
@login_required
def add_to_cart(request, game_id):
game = get_object_or_404(Game, pk=game_id)
cart = ShoppingCart.objects.get_by_user(request.user)
existing_item = ShoppingCartItem.objects.get_existing_item(cart, game)
if existing_item is None:
price = (Decimal(0)
if not hasattr(game, 'pricelist')
else game.pricelist.price_per_unit)
new_item = ShoppingCartItem(
game=game,
quantity=1,
price_per_unit=price,
cart=cart
)
new_item.save()
else:
existing_item.quantity = F('quantity') + 1
existing_item.save()
messages.add_message(
request,
messages.INFO,
f'The game {game.name} has been added to your cart.')
return HttpResponseRedirect(reverse_lazy('user-cart'))
这个函数接收一个请求和游戏 ID,首先获取游戏和当前用户的购物车,然后调用
get_existing_item
方法检查该商品是否已在购物车中。如果不在,则创建一个新的
ShoppingCartItem
并保存;如果已存在,则更新商品数量并保存。同时添加一条消息通知用户商品已添加到购物车,最后将用户重定向到购物车页面。
4. 样式调整
为了让购物车视图看起来更好,我们对其进行样式调整。打开
gamestore/static/styles
目录下的
site.css
文件,添加以下样式:
.cart-details h3 {
margin-bottom: 40px;
}
.cart-details .table tbody tr td:nth-child(2) {
width: 10%;
}
.cart-details .table tbody tr td:nth-child(3) {
width: 25%;
}
.cart-details .table tbody tr td:nth-child(4) {
width: 20%;
}
.has-errors input:focus {
border-color: red;
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(255,0,0,1);
webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(255,0,0)
}
.has-errors input {
color: red;
border-color: red;
}
.cart-details .footer {
display:flex;
justify-content: space-between;
}
.cart-details .footer .total-value {
font-size: 1.4em;
font-weight: bold;
margin-left: 10px;
}
5. 添加购物车视图链接
在测试之前,我们需要在顶部菜单中添加购物车视图的链接。打开
gamestore/templates
目录下的
base.html
文件,找到包含
_loginpartial.html
文件的位置,在其之前添加以下代码:
{% if user.is_authenticated%}
<li>
<a href="/cart/">
<i class="fa fa-shopping-cart" aria-hidden="true"></i> CART
</a>
</li>
{% endif %}
现在我们可以进行测试,访问首页并尝试将一些游戏添加到购物车,应该会被重定向到购物车页面。
以下是购物车功能实现的流程图:
graph TD;
A[开始] --> B[创建购物车表单];
B --> C[创建购物车视图];
C --> D[添加商品到购物车];
D --> E[样式调整];
E --> F[添加购物车视图链接];
F --> G[测试];
购物车相关功能的操作步骤总结如下表:
| 操作 | 步骤 |
| ---- | ---- |
| 创建购物车表单 | 1. 打开
forms.py
文件添加表单代码;2. 添加必要的导入语句 |
| 创建购物车视图 | 1. 打开
urls.py
文件添加 URL;2. 创建
cart.html
文件 |
| 向购物车添加商品 | 1. 打开
url.py
文件添加 URL;2. 打开
views.py
文件添加视图;3. 打开
models.py
文件添加方法 |
| 样式调整 | 打开
site.css
文件添加样式 |
| 添加购物车视图链接 | 打开
base.html
文件添加链接代码 |
Django 购物车与订单微服务开发指南
6. 搭建订单微服务环境
在前面的基础上,我们接下来要搭建一个订单微服务,以处理在线游戏商店中的订单相关操作。具体步骤如下:
1. 创建工作目录:
mkdir microservices && cd microservices
-
使用
pipenv创建虚拟环境:
pipenv --python ~/Install/Python3.6/bin/python3.6
如果不熟悉
pipenv
的使用,可以参考相关资料进行入门学习。
3. 安装项目依赖:
pipenv install django djangorestframework requests python-dateutil
我们选择使用 Django 和 Django REST Framework,是因为项目的主要目的是实现关注点分离,创建一个处理订单的微服务。Django 拥有强大且灵活的管理界面,能方便地实现订单列表展示、详情查看和状态更新等功能。
安装完依赖后,
Pipfile
应如下所示:
[[source]]
verify_ssl = true
name = "pypi"
url = "https://pypi.python.org/simple"
[packages]
django = "*"
djangorestframework = "*"
[dev-packages]
[requires]
python_version = "3.6"
- 创建 Django 项目:
django-admin startproject order
- 创建 Django 应用:
cd order
django-admin startapp main
创建完 Django 应用后,项目结构应如下:
.
├── main
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── order
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
7. 创建服务模型
接下来,我们要为订单服务创建模型,用于存储来自在线视频游戏商店的订单数据。具体操作如下:
1. 打开
main
应用目录下的
models.py
文件,添加以下模型:
class OrderCustomer(models.Model):
customer_id = models.IntegerField()
name = models.CharField(max_length=100)
email = models.CharField(max_length=100)
OrderCustomer
类继承自
Model
,定义了三个属性:
customer_id
对应在线游戏商店中的客户 ID,
name
为客户姓名,
email
为客户邮箱。
class Order(models.Model):
ORDER_STATUS = (
(1, 'Received'),
(2, 'Processing'),
(3, 'Payment complete'),
(4, 'Shipping'),
(5, 'Completed'),
(6, 'Cancelled'),
)
order_customer = models.ForeignKey(
OrderCustomer,
on_delete=models.CASCADE
)
total = models.DecimalField(
max_digits=9,
decimal_places=2,
default=0
)
created_at = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
status = models.IntegerField(choices=ORDER_STATUS, default='1')
Order
类同样继承自
Model
,首先定义了一个包含订单状态的元组
ORDER_STATUS
,然后定义了一个外键
order_customer
,用于关联
OrderCustomer
和
Order
。接着定义了
total
表示订单的总购买价值,
created_at
记录订单提交日期,
last_updated
记录订单状态更新日期。当为
DateTimeField
添加
auto_now_add
时,Django 使用
django.utils.timezone.now
函数,该函数返回包含时区信息的当前日期时间对象;而
DateField
使用
datetime.date.today()
,不包含时区信息。
class OrderItems(models.Model):
class Meta:
verbose_name_plural = 'Order items'
product_id = models.IntegerField()
name = models.CharField(max_length=200)
quantity = models.IntegerField()
price_per_unit = models.DecimalField(
max_digits=9,
decimal_places=2,
default=0
)
order = models.ForeignKey(
Order, on_delete=models.CASCADE, related_name='items')
OrderItems
类用于存储订单中的商品信息。通过定义
Meta
类,将
verbose_name_plural
设置为
Order items
,使在 Django 管理界面中显示的名称更规范。然后定义了
product_id
、
name
、
quantity
和
price_per_unit
等属性,分别对应在线视频游戏商店中的游戏信息,最后定义了外键
order
关联订单。
-
编辑
microservices/order/order目录下的settings.py文件,将main应用添加到INSTALLED_APPS中:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'main',
]
- 创建并应用数据库迁移:
python manage.py makemigrations
python manage.py migrate
以下是订单微服务搭建的流程图:
graph TD;
A[开始] --> B[创建工作目录];
B --> C[创建虚拟环境];
C --> D[安装项目依赖];
D --> E[创建 Django 项目];
E --> F[创建 Django 应用];
F --> G[创建服务模型];
G --> H[编辑 settings.py 文件];
H --> I[创建并应用数据库迁移];
订单微服务搭建的操作步骤总结如下表:
| 操作 | 步骤 |
| ---- | ---- |
| 搭建环境 | 1. 创建工作目录;2. 创建虚拟环境;3. 安装项目依赖;4. 创建 Django 项目;5. 创建 Django 应用 |
| 创建服务模型 | 1. 打开
models.py
文件添加模型代码;2. 编辑
settings.py
文件添加应用;3. 创建并应用数据库迁移 |
通过以上步骤,我们完成了购物车功能的实现以及订单微服务的环境搭建和模型创建。后续可以进一步开发订单微服务的 API 接口、编写测试代码以及进行部署等操作。
Django购物车与订单微服务开发指南
超级会员免费看
1176

被折叠的 条评论
为什么被折叠?



