24、Django 购物车与订单微服务开发指南

Django购物车与订单微服务开发指南

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 我们不希望其可见,所以将其定义为隐藏输入框,这样在提交表单时它会被发送回服务器。

  1. 在同一文件中添加必要的导入语句:
from django.forms import inlineformset_factory
from .models import ShoppingCartItem
from .models import ShoppingCart
  1. 打开 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 定义了一个名称以方便使用。

  1. 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 的视图。

  1. 打开 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
  1. 打开 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 ;否则返回该购物车商品。

  1. 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
  1. 使用 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"
  1. 创建 Django 项目:
django-admin startproject order
  1. 创建 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 关联订单。

  1. 编辑 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',
]
  1. 创建并应用数据库迁移:
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 接口、编写测试代码以及进行部署等操作。

基于遗传算法的微电网调度(风、光、蓄电池、微型燃气轮机)(Matlab代码实现)内容概要:本文档介绍了基于遗传算法的微电网调度模型,涵盖风能、太阳能、蓄电池和微型燃气轮机等多种能源形式,并通过Matlab代码实现系统优化调度。该模型旨在解决微电网中多能源协调运行的问题,优化能源分配,降低运行成本,提高可再生能源利用率,同时考虑系统稳定性经济性。文中详细阐述了遗传算法在求解微电网多目标优化问题中的应用,包括编码方式、适应度函数设计、约束处理及算法流程,并提供了完整的仿真代码供复现学习。此外,文档还列举了大量相关电力系统优化案例,如负荷预测、储能配置、潮流计算等,展示了广泛的应用背景和技术支撑。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事微电网、智能电网优化研究的工程技术人员。; 使用场景及目标:①学习遗传算法在微电网调度中的具体实现方法;②掌握多能源系统建模优化调度的技术路线;③为科研项目、毕业设计或实际工程提供可复用的代码框架算法参考; 阅读建议:建议结合Matlab代码逐段理解算法实现细节,重点关注目标函数构建约束条件处理,同时可参考文档中提供的其他优化案例进行拓展学习,以提升综合应用能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值