今天这篇是我们实作库米狗屋●KummyShop的情境电商模拟的最终章了!今天我们要把先前建的一堆订单可以在我的订单页面呈现出来。
我们预计,可以在里面看到:
为了专注我们想谈的功能,这边作了很多的简化,觉得和正式电商相比有缺少之处,都请自行脑补。
那麽就可以开始往下看了。
这支程序很简单,我们需要可以取得以id
来排序的降幂排序,最多回传100笔资料。
def get_my_orders(top=100):
return Payment.objects.all().order_by('-id')[:top]
使用Model的objects.all()
取回所有资料,但使用order_by()
依id
反向排序,作法就是在前面加上一个减号。而取排序後的前top
笔资料,参数预设值为100。
在这里我们需要处理的是取得model中的orders资料,以及加上分页的机制。我们可使用Django内建的Paginator
模组来快速产生分页的计算与资料切割,相当方便。
from django.core.paginator import Paginator
def my_orders(request):
page_number = request.GET.get('page', 1)
orders_total = get_my_orders()
paginator = Paginator(orders_total, 10)
page_orders = paginator.page(page_number)
context = {"page_orders": page_orders}
return render(request, 'order/my_orders.html', context)
这里需要使用Paginagor
的类别来装载我们取回的order资料,并设定其分页一页大小,我们设定10笔为一页。
接着就可以从request传入一个page
参数来决定等一下返回给Template资料是第几页的资料内容,这些全部都交给paginator机制来运算。
我们有很多的值都是以代码储存,那显示的时候怎麽办呢?
在上一篇文章有提到一些上次作了一小部份代码转换的想法,这次我们使用了Django Template的TemplateTags的作法来解决。
我们会需要准备两个转换代码的方法,一个来处理付款方式(C或A),一个来处理付款状态(W、S、F)。我们需要在Django 对应的App底下(我们这里是order
App),建立一个名为TemplateTags
的目录,在底下新增一个空白的__init__.py
作为识别用途,以及一个我们要处理的逻辑order_converter.py
。
在我们的Django目录如下所示:
from django import template
register = template.Library()
@register.filter
def convert_pay_status(value):
convert_dict = {"W": "尚未付款", "S": "付款成功", "F": "付款失败"}
return convert_dict.get(value.upper())
@register.filter
def convert_pay_type(value):
convert_dict = {"A": "ATM转帐", "C": "信用卡"}
return convert_dict.get(value.upper())
接着来说明一下这支converter会用到的部份,这个是要给Template使用的,所以我们要先行引入django的template以及使用其Library()方法建立一个filter注册物件。
我们使用属性宣告方式在这两个方法前面加上@register.filter
,接着就使用简单的方式把对应的代码作转换後回传。
这里我们为了不失焦,不特别去处理其他的异常处理作法,但在产品级的系统里,请要额外处理不在我们认知的值的处理机制。而且这样的对应表,通常也会另外准备在外部的properties档或资料库中去定义,不会直接hard code在程序码里面。总之,铁人赛这一系列文章中,仅处理需要的逻辑与简易式写法,不会特别去处理try/catch的异常处理机制,有需要参考者,请自行花时间去撰写具备保护力(咦?)的程序码喔!
{% extends "base.html" %}
{% load bootstrap5 %}
{% load humanize %}
{% load order_converter %}
{% block title %}{{ title }}{% endblock %}
{% block body %}
<nav id="app" class="row g-3 align-self-center">
<h1 class="display-4 text-center mb-3 mt-5">我的订单</h1>
<p class="lead text-center">看看我都买了些什麽呀…</p>
<hr/>
<div class="">
<table class="table">
<thead>
<tr>
<th scope="col">订单号码</th>
<th scope="col">总金额</th>
<th scope="col">订单时间</th>
<th scope="col">付款方式</th>
<th scope="col">付款状态</th>
<th scope="col">动作</th>
</tr>
</thead>
<tbody>
{% for order in page_orders %}
<tr class={% if order.pay_status == 'F' %}"table-danger"{% elif order.pay_status == 'W' %}table-info{% endif %}>
<th scope="row">{{ order.order_no }}</th>
<td>NT$ {{ order.amount|intcomma }}</td>
<td>{{ order.create_time|date:'Y-m-d H:i' }}</td>
<td>{{ order.pay_type|convert_pay_type }}</td>
<td>{{ order.pay_status|convert_pay_status }}</td>
<td>{% if order.pay_status == 'F' %}<a href="{{ order.card_pay_url }}" >重新刷卡</a>{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="text-center">
<span class="current">
第 {{ page_orders.number }} 页 / 共 {{ page_orders.paginator.num_pages }} 页
</span>
</div>
<ul class="pagination justify-content-center">
{% if page_orders.has_previous %}
<li class="page-item"><a class="page-link" href="?page=1">第一页</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_orders.previous_page_number }}">上一页</a></li>
{% endif %}
{% if page_orders.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_orders.next_page_number }}">下一页</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_orders.paginator.num_pages }}">最末页</a></li>
{% endif %}
</ul>
</div>
{% endblock %}
{% block script %}
{% endblock %}
刚前面有提到我们的几个资料显示处理要点,忘记的可以再到上面确认一下。
我们取回了具备已分页完的order资料,因此我们想要在表面上,使用for回圈把资料显示在上面,所以需要在tr
的地方加上所需的回圈逻辑。
我们搭配使用了Bootstrap的Table显示,因此等一下针对不同付款状态
会有不同的bootstrap的颜色,我们会带入相对应的table color CSS。
其中,我们会显示五个栏位的值:订单编号、总金额、付款方式、付款状态、额外资讯或功能
在总金额的地方,我们需要将数字加上千位数逗号,这部份可使用humanize
模组的intcomma
功能,要使用之前,需在settings.py
中的INSTALLED_APPS
加上模组的设定才能使用。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'bootstrap5',
'order',
'greetings',
]
接着就可以很简单在Template中的变数後面加上|
後套入使用,如此一来NT$ 12345
就会变成NT$ 12,345
了。
而时间格式也可以使用filter带入,如下方code所示。
{% load humanize %}
<!-- …略… -->
<td>NT$ {{ order.amount|intcomma }}</td>
<td>{{ order.create_time|date:'Y-m-d H:i' }}</td>
在付款方式、付款状态的地方,我们就可以套用写好的TemplateTags filter,分别是convert_pay_type
和convert_pay_status
,使用方法和上面一样。
{% load order_converter %}
<!-- …略… -->
<td>{{ order.pay_type|convert_pay_type }}</td>
<td>{{ order.pay_status|convert_pay_status }}</td>
如此一来,就可以将付款状态例如S
转换成付款成功
的文字表示。
我们可依据订单的付款状态,来改变Bootstrap的颜色。
所以这里很简单依状态,将付款失败改成table-danger或尚未付款改为table-info。
当订单在付款成功的状态,我们颜色就是维持白色系,额外资讯也会保持空白。
在分页的处理,我们原先从View转至Template时,记得page_orders
物件是怎麽来的吗?
看一下这三行,我们是使用了paginator的page()取回仍具备有分页功能的物件。
orders_total = get_my_orders()
paginator = Paginator(orders_total, 10)
page_orders = paginator.page(page_number)
所以在Template中,我们依旧可使用其相关的功能来作判断或进行分页页数的取得。
number:目前的页数
has_previous:判断是否有上一页 (如果没有,表示就在第一页)
previous_page_number:如果有,则自动取回上一页的页码。
has_next:判断是否有下一页 (如果没有,表示就在最後一页)
next_page_number:如果有,则自动取回下一页的页码。
paginator.num_pages:取回总页数
则我们可以依这些好用的属性,带入我们的URL中进去换页的page参数。
好罗,可以看一下我们的画面了!
这样就把我的订单页面完成了!
明天开始就回顾一下这些日子来,对丰收款的一些使用与设计上的心得,之後剩下几天再来看要谈哪些没谈到的议题,或者是来研究一点Shioaji了。
<<: [Day 24] SQL union / union all
>>: Day24 - Time complexity (DS篇)
前言 在上一篇的HTTP请求走私之後,已经知道HTTP Header也可以被拿来利用,这篇会更直接的...
库存只剩 1 件,但却有 10 个人买到? 网路商城特卖会常常会推出特定商品限量 1 组的抢购活动...
在很多情况下,我们的程序,通过命令终端与用户交互。让用户输入,yes 或 no 是一种很常见的场景。...
软件开发中,最怕遇到的就是前面有新功能的开发在赶,後面有线上的 bug 在等着处理,呈现蜡烛两头烧的...
在 EP13 - 灾难演练,重建你的 VPC, 我们在重建 VPC 之前, 有带着大家怎麽进行单次备...