之前有在用Django写一些小网站,现在暑假想说再来複习一下之前买的这本书
于是我就把它写成一系列的文章,也方便查语法
而且因为这本书大概是2014年出的,如今Django也已经出到2.多版
有些内容也变得不再支援或适用,而且语法或许也改变了
所以我会以最新版的Python和Django来修正这本书的内容跟程式码
目录:django系列文章-Django学习纪录
15. 模板进阶技巧
15.1 主题1-重複利用模板
DRY(don't repeat yourself,不要重複你自己)
WET(write everything twice,什么事都写两次)
这小节要用django好好地来重複利用已经写过的模板
15.1.1 汇入模板
使用{% include %}
index.html
<html> <head> <title>Index</title> <meta charset="utf-8"> </head> <body> <h2>欢迎来到餐厅王</h2> <p><a href="/accounts/register/">注册</a></p> {% if request.user.is_authenticated %} <p> {{request.user}} 您已经登入啰~ <a href="/accounts/logout/">登出</a> </p> <a href="/restaurants_list/">餐厅列表</a> {% else %} <p>您尚未登入喔~<a href="/accounts/login/">登入</a></p> {% endif %} </body></html>
可以拆解为
index.html
<html> <head> <title>Index</title> <meta charset="utf-8"> </head> <body> {% include 'body.html' %} </body></html>
与
body.html
<h2>欢迎来到餐厅王</h2><p><a href="/accounts/register/">注册</a></p> {% if request.user.is_authenticated %} <p> {{request.user}} 您已经登入啰~ <a href="/accounts/logout/">登出</a> </p> <a href="/restaurants_list/">餐厅列表</a> {% else %} <p>您尚未登入喔~<a href="/accounts/login/">登入</a></p> {% endif %}
include的用法{% include '模版名称(路径)' %}
15.1.2 模板继承
不过其实include有时候没那么好用
一个页面可能需要花上好几个html档,且档案零碎
这时会非常痛苦!
那么就要改用模板的继承了
首先制定一个基础的模板
base.html
<!doctype html><html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> 网页选单 <h1>{% block pagetitle %}{% endblock %}</h1> <h1>说明</h1> <p>{% block content %}{% endblock %}</p> 页尾 </body></html>
在每个需要自定义的地方,使用模板区块
一个模板区块以{% block BLOCKNAME %}
为开头,以{% endblock %}
为结尾
可以填入内容也可以留白,这个区块的内容会让继承此基础模板的子模板覆写,这与物件导向中关于类别的继承与覆写很类似
接着使用{% extends %}
标籤让子模板去继承基础模板
final.html
{% extends 'base.html' %}{% block title %}(页首标题){% endblock %}{% block pagetitle %}(页面标题){% endblock %}{% block content %}(内容){% endblock %}
而{% extends TEMPLATENAME %}
标籤的参数TEMPLATENAME
可以是一个字面的字串,也可以是一个变量,当使用变量的时候,可以动态地更换继承的模板
15.1.3 利用模板继承更新我们的网站
mysite/templates/base.html
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}{% endblock title %}</title> </head> <body> <h2>{% block content %}{% endblock content %}</h2> {% if request.user.is_authenticated %} <p> {{request.user}} 您已经登入啰~ <a href="/accounts/logout/">登出</a> </p> <a href="/restaurants_list/">餐厅列表</a> {% else %} <p>您尚未登入喔~<a href="/accounts/login/">登入</a></p> {% endif %} <p>若您还没有帐号,请<a href="/accounts/register/">注册</a></p> </body></html>
mysite/templates/index.html
{% extends 'base.html' %}{% block title %} 首页 {% endblock%}{% block content %} <p>欢迎来到餐厅王</p>{% endblock %}
15.2 主题2-RequestContext与Context处理器
这边大部分的用法在新版的django中已经不再支援
如果有人知道该如何继续沿用的话麻烦指教一下!
所以只介绍
15.2.3 使用render
从from django.shortcuts import render
汇入
用法 render(HttpRequest, TEMPLATENAME, DICTIONARY)
第一个参数放request
物件
第二个参数放模板
第三个参数放字典
与render_to_response
来比较
从from django.shortcuts import render_to_response
汇入
用法 render_to_response(TEMPLATENAME, DICTIONARY)
第一个参数放模板
第二个参数放字典
不能放request参数
所以如果要取得request资讯只能用render
15.3 主题3-自定义过滤器
15.3.1 前置作业
在restaurants目录下建一个资料夹templatetags
在templatetags资料夹中新增一个__init__.py
这样才会被python视为一个套件
接着再新增一个myfilters.py
15.3.2 过滤器函式
之前的menu.html模板
如果想要自定义一个过滤器yes_no
... <tr> <td> {{ food.name }} </td> <td> {{ food.price }} </td> <td> {{ food.comment }} </td> <td> {{ food.is_spicy|yes_no:"辣/不辣" }} </td> </tr>...
这个过滤器接受一个额外的字串参数Y/N,如果is_spicy为真则输出Y,否则输出N
mysite/restaurants/templatetags/myfilters.py
def yes_no(bool_value, show_str): if bool_value: return show_str.partition('/')[0] else: return show_str.partition('/')[1]
函式的名字不需要跟我们自定义的过滤器名称一样,但是选择相同名称通常是比较好的作法,bool_value是过滤器的第一个参数,他负责接收模板中pipe符号(|)左边的值,而show_str是第二个参数,用来接收过滤器中的额外参数
对照图:
{{ food.is_spicy|yes_no:"辣/不辣" }} ------------- ------ -------- 2. 1. 3. def yes_no(bool_value, show_str): ------ ---------- -------- 1. 2. 3.
要注意的是,过滤器函式我们总是要保证它是对的,也就是说,我们不允许该函式能够抛出例外
因为任何的例外都会导致网站显示错误,我们应该想办法对于不可避免的例外採行捕捉,并且回传一个空白字串
15.3.3 注册过滤器
写好过滤器函式后接着要注册给Django知道
myfilters.py
from django import templatedef yes_no(bool_value, show_str): if bool_value: return show_str.partition('/')[0] else: return show_str.partition('/')[1]register = template.Library()register.filter('yes_no', yes_no)
使用register的filter函式来注册,filter函式的第一个参数是过滤器的名称,第二个参数是它对应的过滤器函式
这就是为什么过滤器函式名称不需要跟过滤器的名称一样
要注意的是,不要这样写:
template.Library().filter('yes_no',yes_no)
或是:
reg = template.Library()reg.filter('yes_no',yes_no)
没有把register(名称也要一样)给建立出来就会出现错误
也可以用装饰器来注册
from django import templateregister = template.Library()@register.filter(name='yes_no')def yes_no(bool_value, show_str): if bool_value: return show_str.partition('/')[0] else: return show_str.partition('/')[1]
该装饰器的参数name
是个可选的参数,用来定义过滤器的名称,如果不提供该参数,Django预设会使用过滤器函式的名称作为过滤器名称
15.3.4 载入过滤器
必须在模板里载入它
menu.html
{% load myfilters %}... <tr> <td> {{ food.name }} </td> <td> {{ food.price }} </td> <td> {{ food.comment }} </td> <td> {{ food.is_spicy|yes_no:"辣/不辣" }} </td> </tr>...
在这里只能载入restaurants app的过滤器
要注意的是前面的步骤必须一模一样
15.4 主题4-使用静态档
网站中会用到的图片、css、js档等被称为静态档(static file)
在上层mysite底下新增两个资料夹static和assets
static资料夹是开发时用来放置静态档的目录
该目录底下可以新增数个子目录来放置不同种类的静态档
比如设置img资料夹来放置图片,或是设置css资料夹来放置css档
而assets是网站真正上线时放置静态档的目录
这两个目录之所以分开,是因为在上线时我们需要将静态档的管理权交给网页伺服器
设定settings.py
...STATIC_URL = '/static/'STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'),)STATIC_ROOT = os.path.join(BASE_DIR, 'assets')...
三个参数的说明:
STATIC_URL
静态档的URL pattern,设为/static/
代表在网页路径中以/static/
开始的便会被视为静态档,如127.0.0.1/static/hello.png、127.0.0.1/static/hi.js,但如果是127.0.0.1/restaurants/static/则会匹配urls.py中的路径而非静态档STATIC_DIRS
开发时放置静态档的资料夹,允许底下设置多个资料夹来指示静态档的位置STATIC_ROOT
上线时放置静态档的资料夹,在部署时Django藉由python manage.py collectstatic
将STATIC_DIRS
下发现的静态档複製至STATIC_ROOT
。由于当DEBUG设为False后,Django预设便不会处理回传静态档了,藉由指令将这些档案集合到一个资料夹下,方便网页伺服器管理及读取假设网站要放一张图片logo.png在mysite/static/img底下那么在模板上要这样写:...<img src='/static/img/logo.png'>...
如果有一天STATIC_URL被更改为/static_file/
就得一个一个更改模板中的路径,这样会超累
解决办法是:
{% load staticfiles %}...<img src="{% static 'img/logo.png' %}">...
这里的{% static %}
标籤就会自动帮我们找到静态档的路径了
记得要{% load staticfiles %}
载入静态档