我们今天简单带一点Django Template继承的概念,也就是当你有每一页面具备无论在哪一样都有相同的内容片段时,可采用这个方式把相同的部份拆出来,而具体不同的功能页面只要先继承後,再实作有差异的程序段,和程序码把共用的地方设计成父类别,子类别先继承後再进行override是一样的概念。
先在Django App根目录下,建立一个templates的目录,然後建立一个base.html
记得在settings.py中,TEMPLATES底下的DIRS,改成如下:
'DIRS': [os.path.join(BASE_DIR, 'templates')],
注:前面记得import os
{% load bootstrap5 %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="favicon.ico">
{% bootstrap_css %}
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">库米狗屋 ● KummyShop</a>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#navbarsExample02" aria-controls="navbarsExample02"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExample02">
<ul class="navbar-nav me-auto">
<li class="nav-item active">
<a class="nav-link" href="{% url 'order_create_entrance' %}"><span
class="sr-only">回狗屋</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'my_orders' %}">我的订单</a>
</li>
</ul>
</div>
</div>
</nav>
<section class="pt-5 pb-5">
<div class="jumbotron ">
<div class="container">
{% block body %}{% endblock %}
</div>
</div>
</section>
{% bootstrap_javascript %}
<script src="https://unpkg.com/vue@next"></script>
{% block script %}{% endblock %}
</body>
</html>
把你到时候子页面想换掉的地方都透{% block blockname %}{% endblock %}
来挖洞设置,以上面为例,我先设定以下的block区:
另外我引入了Vue.js
的CDN进去,让前端页面可支援Vue的使用,当然你也可以使用其他的前端框架。
有了刚刚的模版Base後,我们就可以很轻松的开设继承的子页面,然後只要把上面三个挖洞的区域重新填入我们要的html区段即可。
def order_create_entrance(request):
context = {
"title": "下好离手,准备付款",
"desc": "以下是我们帮你随机挑选好的订单,如果有不喜欢的商品请顺心接受,偶尔这样也不是坏事。接着请选择你的付款方式罗!"
}
return render(request, 'order/order_create_entrance.html', context)
如同昨天的greetings test示范,我们新增两个context变数title
和desc
,将他传递至template中。
接下来我们就要看重头戏了
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block body %}
<div id="app" class="col-xs-12 col-md-10 align-self-center">
<h1 class="display-4 text-center mb-3 mt-5">[[ title ]]</h1>
<p class="lead text-center">[[ desc ]]</p>
<div class="justify-content-center d-flex mt-3 mb-1">
</div>
</div>
{% endblock %}
{% block script %}
<script>
const EventHandling = {
data() {
return {
title: "{{ title }}",
desc: "{{ desc }}"
}
},
methods: {
},
delimiters: ['[[', ']]'],
}
Vue.createApp(EventHandling).mount('#app')
</script>
{% endblock %}
最前面先使用{% extends "base.html" %}
告知我们的这一页,是继承自另一个作为共同页面Template而来。再来,就将先前定义的三个block都先长起来,然後把中间的部份填入我们所需要的内容,到时候生成最终版页面的时候,就会连同base.html的内容一起整合运算後,才render给client端。
接着要解释前,先Run起来看一下结果。
上面的程序码如果有先看过昨天的文章的话,会觉得我们是不是写错了,怎麽里面有些Django的变数绑定不是使用{{ var }}
却变成[[ var ]]
呢!?
原来是我们把这个前端页面也套用了Vue了的关系。
由於Vue和Django Template都使用{{ var }}
的方式来bind,所以会有所冲突,因此在下面的Vue宣当时重新定义了Vue的delimiters为[[
和]]
。
你可以注意到,我恰好是将Django context的2个变数,和Vue的data绑定的2个变数都取一样的名称。如果你尝试的将上面的页面的[[
和]]
改成{{
与}}
,再跑一次页面会发现页面都一样呀!哪有你说的冲突呢!?
如果你看一下原始码就会发现他们的不同之处,为了解释给各位理解,我就只换一个。
把Body区段的[[ title ]]
换成{{ title }}
,而[[ desc ]]
保留原样。
<div id="app" class="col-xs-12 col-md-10 align-self-center">
<h1 class="display-4 text-center mb-3 mt-5">下好离手,准备付款</h1>
<p class="lead text-center">[[ desc ]]</p>
<div class="justify-content-center d-flex mt-3 mb-1">
</div>
<!-- 省略掉其他部份 -->
<script>
const EventHandling = {
data() {
return {
title: "下好离手,准备付款",
desc: "以下是我们帮你随机挑选好的订单,如果有不喜欢的商品请顺心接受,偶尔这样也不是坏事。接着请选择你的付款方式罗!"
}
},
methods: {
},
delimiters: ['[[', ']]'],
}
Vue.createApp(EventHandling).mount('#app')
</script>
上面的意思就是说,只有[[ desc ]]
是真正被Vue的data binding起了作用,是在前端网页被执行时才运算完成。但{{ title }}
的部份,是被Django Template的context变数绑定在後端产生前就被替换了,因此HTML传递到前端Browser时看到就已经是静态结果了。
没听懂也没关系,总之,如果你有混用Django Template以及使用Vue的朋友,要记得作delimiters的重新定义。然後习惯另一个新的重定义语法,一样可以让两套机制顺利运行。
{% extends "base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block body %}
<div id="app" class="row g-3 align-self-center">
<h1 class="display-4 text-center mb-3 mt-5">{{ title }}</h1>
<p class="lead text-center">{{ desc }}</p>
<hr/>
<div class="">
<form action="{% url 'order_create_next' %}" method="POST">
<span>以下是这次订单的商品,最後一次确认喔!</span>
<div class="form-check " v-for="(item, index) in cart_items">
<input class="form-check-input" type="checkbox" :value="item.price" :id="item.id" v-model="checked_items">
<label class="form-check-label" :for="item.id">
[[ item.item_name ]] / NT$ [[ item.price ]]
</label>
</div>
<div>Total NT$ <span class="fs-3 text-primary">[[ sum_amount() ]]</span></div>
<input type="hidden" name="amount" v-model="amount"></hidden>
<hr/>
<br />
<div>选择付款方式:</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="payment" id="payment_atm" value="atm">
<label class="form-check-label" for="payment_atm">
使用永丰ATM虚拟帐号转帐
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="payment" id="payment_card" value="card">
<label class="form-check-label" for="payment_card">
使用永丰线上信用卡刷卡支付
</label>
</div>
<br />
<button type="submit" class="btn btn-primary">付款去!</button>
</form>
</div>
</div>
{% endblock %}
{% block script %}
<script>
const EventHandling = {
data() {
return {
cart_items: [
{"id": 1, "item_name": "抹茶狗骨头", "price": 750},
{"id": 2, "item_name": "腊肠狗专用晚宴西装", "price": 4900},
{"id": 3, "item_name": "潮流嘻哈喝水碗", "price": 1200},
{"id": 4, "item_name": "小型屁孩犬鸡肉饲料(10KG装)", "price": 3500},
{"id": 5, "item_name": "冬凉夏热毛孩专用万用毯", "price": 1799}
],
checked_items: [],
amount: 0
}
},
methods: {
sum_amount: function() {
this.amount = this.checked_items.reduce(function(a, b){
return a + b;
}, 0);
return this.amount;
}
},
delimiters: ['[[', ']]']
}
Vue.createApp(EventHandling).mount('#app')
</script>
{% endblock %}
我们简化购物车流程,直接使用Vue里面先把5个库米狗屋严选好物直接帮顾客列好好了,最後只需要勾选确认想要的产品就可以进入最终流程了!是不是快速又方便呢。
前面为了示范Vue与Django Template的混用,但实际title与desc两个部份不太需要使用到Vue来绑定,因此把那段改成纯使用Django Template的变数取代,甚至直接在这页输入明确文字其实也不是不行。但如果未来是有需要使用i18n多国语系的话,还是会将UI上的文字部份以变数方式取代,只是其内容不是用context的方式传递,可参考这篇文章进行i18n设定。
在这个页面里,Form里面会使用POST方式,准备了2个主要的变数要往action页面(order_create_next
)传递,这两个数值的内容,一个是由checkbox勾选商品时会计算累积金额,放到hidden input amount
栏位中。
这里使用了Javascript Array的一个特殊的方法reduce()
,可放入累加函数,用这个方式很精简的可快速计算所勾选的商品的总金额。有兴趣了解进阶用法可参考这篇文章。
另一个就是我们的radio buttons选择付款方式,名称为payment
。
完成付款前确认UI画面如下:
所以我们还需要准备一个新的View def order_create_next
来接收这一个POST後的页面,待接收完後我们就可以继续呼叫後面的API流程了!
请明天继续看下去罗。
今天这个,真的是插班车,因为今天作完弱扫,总共八份的测报。 我自已看得都要吐了。 在思考,这个月,因...
前言 第二次参加铁人赛,在决定参赛时,就又让人想起连续30天不间断发文的痛苦,但是要用什麽主题来做为...
本节来说明AR中虚拟讯息的种类和它的好处,前面有稍微提到,AR是将虚拟讯息/物件叠加在真实世界中,而...
回呼函式,或简称回调、回呼(Callback 即call then back 被主函式呼叫运算後会返...
那些具有使用API经验的人将听说过并熟悉无处不在的API测试和开发工具。但是,Nutanix De...