• Django笔记 —— 模板


      最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过。Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧~

      本篇笔记(其实我的所有笔记都是),并不会过于详细的讲解。因此如果有大家看不明白的地方,欢迎在我正版博客下留言,有时间的时候我很愿意来这里与大家探讨问题。(当然,不能是简简单单就可以百度到的问题-.-)

      我所选用的教材是《The Django Book 2.0》,本节是模板部分,对应书中第四章。

    ------------------------------------------------------------------------------------------------------------------------------------------------

     0、代码示例

      创建一个网站:django-admin.py startproject four

      在网站根目录下创建一个文件夹:templates(网站根目录也就是那个有manage.py的目录,定位网站中任何文件,都默认此目录作为根目录)

      在./templates/下,创建home.html,内容如下:

    1 <html>
    2 <head>
    3 </head>
    4 <body>
    5 Hello~I'm {{name}}^.^<br />
    6 </body>
    7 </html>

      在./templates/下,创建home.txt,内容如下:

    1 name    qiqi

      在./four/下,修改urls.py,内容如下:

    1 from django.conf.urls import url
    2 from four.views import home
    3 
    4 urlpatterns = [
    5     url(r'^$', home),
    6 ]

      在./four/下,创建views.py,内容如下:

     1 from django.http import HttpResponse
     2 from django.template import Template, Context
     3 
     4 def home(request):
     5     tin = open('./templates/home.html')
     6     html = tin.read()
     7     tin.close()
     8 
     9     cin = open('./templates/home.txt')
    10     inf = {}
    11     for line in cin.readlines():
    12         a,b = line.split()
    13         inf[a] = b
    14     cin.close()
    15 
    16     t = Template(html)
    17     c = Context(inf)
    18     return HttpResponse(t.render(c))

      此时,我们运行服务器(python manage.py runserver),便可以访问到以下网页了:

    网址 内容
    http://127.0.0.1:8000/ Hello~I'm qiqi^.^

     1、模板简介

      这一章原文很长,我想,我先向大家大体介绍一下模板,后面再详述细节。

      先说咱们熟悉的urls.py和views.py:

        urls.py中删去了无用代码,只创建了一个home。

        views.py中则利用模板实现了这个home。其中,除了Template、Context和render(即最后三行)属于模板内容之外,其余部分均为python中文件读取的语句。很显然,我把前面创建的home.html读到了html变量里,这是一个字符串;又把home.txt读到了inf变量里,这是一个字典。

      好的,知道了这些,下面咱们开始介绍模板:

        home.html就是一个模板,大家可以看到,这就是一段html代码,唯一不同的是,其中多了一个"{{name}}"。这是模板里“变量”的写法,变量名是name。这样写出一段可以包含变量的html代码,就算是写出了一个最基础的模板。

        而home.txt中的内容,显然就是把name赋值成qiqi。这个东西就是为模板填写的内容了。

        最后我们在views.py中,用Template函数把html代码转换成模板,用Context函数把字典转换成内容(书中有时翻译成“上下文”,但我更喜欢翻译成“内容”),而render函数则利用这个模板渲染了这段内容,生成一个网页。这就是传说中的模板了。

        这里说明一个细节:render所产生的是Unicode字符串(大家可以自己在python交互界面下试试)!

      看到这里,大家应该已经知道什么是模板,并且可以使用Django中的模板做一些有变量的网页了。但我更希望,大家能感到,这过程非常简单,各位同学完全可以自己用任何语言来完成这个任务。因为暂时来看,模板无非就是段变量替换的代码而已,随便一个会编程的人都可以做的。

    2、模板基础语法

      模板当然不只有替换变量这一个东西,那么我们现在开始介绍模板的各种用法:

    引用/语句 示例 解释 备注
    变量 {{ name }} name是变量名,

    如果这个变量不存在,模板中对应位置则会写成一个空字符串

    (下面各种变量也都一样,方法还没有实验!!!

    字典变量

    {{ person.name }}

    {{ person.age }}

    person是字典,

    有name和age两个key

     
    某个对象

    {{ d.year }}

    {{ d.month }}

    {{ d.day }}

    import datetime

    d=datetime.date(2015, 5, 21)

     
    自定义类

    {{ person.name }}

    {{ person.age }}

    person是自己定义的类,

    name和age是里面的成员变量

     
    成员方法

    {{ str.upper }}

    {{ str.isdigit }}

    str是string类型,

    string类型有upper和isdigit函数

    1. 只能调用不需传参数的方法。

    2. 如果方法中出现了错误,按说应该报错。

    但如果这个错误中有下面这个属性,则不会报错:

    silent_variable_failure = True,

    此时,模板中对应位置会被写成一个空字符串。

    3. 模板系统不会执行任何以下列方式标记的函数,

    即使调用了这个函数,也会默默退出,什么都不做:

    设置函数属性alters_date = True。

    列表索引

    {{ items.0 }}

    {{ items.1 }}

    {{ items.2 }}

    items=['go','phone','computer']

    不能使用负数列表索引:

    例如{{ items.-1 }},会触发TemplateSyntexError。

    if

    语句

    {% if value %} 

      # aaa

    {% else %}

      # bbb

    {% endif %}

    value是变量名,

    else是可选的

    1. 变量为真,则会显示aaa处内容;否则,显示bbb处内容。

    所谓真,即:变量存在、非空、非布尔值False。只有以下值为假:

    [], (), {}, '', 0, None, False, 自定义对象中定义布尔值属性为False。

    2. 可以使用and,or,not进行逻辑运算,也可以用not对变量取反,

    但一句if中不可以同时出现and和or,以免造成逻辑混乱。

    注意,不可以使用括号来提示优先级,not优先级高于and和or。

    3. 可嵌套(也可与for相互嵌套)

    4. 不支持别的if语法(例如elif)

    ifequal

    语句

    {% ifequal x y %}

      # aaa

    {% else %}

      # bbb

    {% endifequal %}

    如果x与y两变量相等,

    那么显示aaa的内容,

    否则显示bbb的内容。

    x与y只能是变量、字符串、整型常数和浮点型常数。

    其它类型都不能用,例如:字典、列表、布尔。

    因此,判断变量值的真假,应当使用if语句。

    for

    语句

    {% for x in Y %} 

      # ...

    {% empty %}

      # ...

    {% endfor %}

    Y是要循环(又称迭代)的序列

    x是每次循环中使用的变量名称

    empty是可选的,

    是Y为空时所显示的代码

    1. 可以使用reversed反向迭代列表

    2. 在每个循环中都有一个模板变量,其中含有一些提示循环进度的属性

      forloop.counter:当前循环次数,从1开始计数

      forloop.counter0:当前循环次数,从0开始计数

      forloop.revcounter:当前剩余循环次数,从N开始,最后为1

      forloop.revcounter0:当前剩余循环次数,从N-1开始,最后为0

      forloop.first:布尔值,仅第一次迭代时为True

      forloop.last:布尔值,仅最后一次迭代时为True

      forloop.parentloop:指向上一级循环的forloop对象的引用

     如果我们自己定义了一个forloop变量,Django模板也不会报错,

    但我并不想介绍其语法,只是希望大家记住变量别叫这个名字就好。

    2. 可嵌套(也可与if相互嵌套)

    3. 不支持别的for语法(例如break,continue)

    过滤器

    (filter)

    {{ name|lower }}

    {{ mylist|first|upper }}

    {{ s|truncatewords:"30" }}

    利用Unix的管道符'|'表示过滤器

    左面举了4个例子:

    1. 把name小写输出

    2. 把mylist中第一个字符大写输出

    3. 把s前30个字符输出

    这里仅仅介绍几种常用过滤器,全部的介绍参见本书的附录F。

    0. 左面例子中那几种

    1. addslashes,添加反斜杠到任何反斜杠、单引号、双引号前面。

    这在处理包含JavaScript的文本时非常有用。

    2. date,按指定的格式字符串参数格式化date或datetime对象。

    例如:{{ now|date:"F j, Y" }},含义详见附录。

    3. length,返回变量的长度(针对有__len__()方法的对象)。

    这里为新手说一句,列表、字符串就有__len__()方法,用来测长度。

    单行注释 {# something #}  something是被注释的内容   
    多行注释

    {% comment %} 

    # ...

    {% endcomment %}

       

      2.1 '.'的优先级

        大家可能已经注意到了,在Django的模板中,各种复杂变量都是使用一个点'.'来引用的。那么,这必然会有优先级问题:

    查找顺序 类型 代码实例
    1 字典 qiqi["hello"]
    2 属性 qiqi.hello
    3 方法 qiqi.hello()
    4 列表索引 qiqi[0]

        啰嗦一句,查找时采用的是“短路逻辑”,即找到一个匹配的就不继续往下找了。

      2.2 '.'可以多级嵌套

      2.3 报错信息

        当遇到模板语法错误时,那么在调用Template函数时,会抛出TemplateSyntexError异常。所谓错误,有下列几种情况: 

    情况 备注
    无效的标签(tags)  
    标签的参数无效  
    无效的过滤器(filter)  
    过滤器的参数无效  
    无效的模板语法  
    未封闭的块标签 显然是对需要封闭的块标签而言的

      2.4 Context修改

        Context是可以像字典一样添加、删除条目的(但字典别的操作我就不知道Context有没有了!!!)

    3、如何使用模板

      开头的代码,的确已经比较简洁地用上了模板。但其中,读取文件的操作显然又是要不断重复书写的代码。因此,Django进一步做了优化。

      3.1 模板加载(get_template函数)

        打开settings.py,你会看到一个TEMPLATES,其中有一个DIRS列表,这便是Django查找模板的地址了。

        因此,在此处加上你的模板路径,例如咱们的templates文件夹,即:'./templates/'。

        然后,修改views.py如下(仔细看,代码不只函数内容变了,import处也改了两行):

     1 from django.http import HttpResponse
     2 from django.template import Context
     3 from django.template.loader import get_template
     4 
     5 def home(request):
     6     cin = open('./templates/home.txt')
     7     inf = {}
     8     for line in cin.readlines():
     9         a,b = line.split()
    10         inf[a] = b
    11     cin.close()
    12 
    13     t = get_template('home.html')
    14     c = Context(inf)
    15     return HttpResponse(t.render(c))

         如此,你会发现网页已经正常运行,而你的代码简洁了不少;如果你的路径写错了,则会出现报错页面,告诉你发生了TemplateDoesNotExist错误。

        get_template函数内的路径是可以有子目录的。

        在此提醒一句,我的笔记只针对Linux用户,因为我用的就是Linux(在第一篇Django笔记中就已声明过了)。关于Windows用户路径是反斜杠的问题,请去查阅原书,其中有详细介绍。

      3.2 模板加载并渲染(render_to_response函数)

        我们代码还可以进一步简洁,如下:

     1 from django.shortcuts import render_to_response
     2 
     3 def home(request):
     4     cin = open('./templates/home.txt')
     5     inf = {}
     6     for line in cin.readlines():
     7         a,b = line.split()
     8         inf[a] = b
     9     cin.close()
    10 
    11     return render_to_response('home.html', inf)

        这个函数很简单,根据给定的字符串找到模板,再把给定的字典转换成内容,然后用模板渲染内容,最后生成Http response。

        如果仅仅给出一个参数,字典没给,则默认是导入一个空字典。

        render_to_response函数第一个参数的路径依旧可以有子目录,因为这个函数仅仅是对get_template函数的简单封装。

      3.3 一个偷懒的小方法(locals函数)

        前面用文件home.txt存变量,适合信息比较多的页面。那如果我们的页面中变量很少呢?

        前面的做法适合多人协作,写网页的和写网站的分开。那如果我们是一个人做这两件事,而home.txt信息又很少,存个文件岂不是多此一举?

        这时候,我们可以考虑使用python内建的函数locals(),这函数返回值中,包括了其所在函数从开头运行到现在所有的局部变量。

    1 from django.shortcuts import render_to_response
    2 
    3 def home(request):
    4     name = 'qiqi'
    5     return render_to_response('home.html', locals())

        代码的确很简单,但需要注意的是,locals()不只有name,还包含了request。代码简洁,实际却多传了变量。如何取舍呢?看你自己了。

    4、模板包含(include模板标签)

    代码 解释
    {% include 'sub.html' %} 这是相对路径,路径起点就是上面搜索模板的路径
    {% include "sub.html" %} 除了单引号,双引号字符串也是支持的
    {% include 'includes/sub.html' %} 可以有子目录
    {% include template_name %} 也可以使用字符串变量

      如果你的路径错误,那么Django会查看你settings.py中的DEBUG参数:

        若参数为Ture,那么你会在Django的报错页面中看到TemplateDoesNotExist错误;

        若参数为False,那么该标签不会引发错误,其位置上不显示任何东西。

      这里提醒一点,假如你的home.html模板,包含了一个sub.html模板。然后你用home.html模板渲染了一段内容,这内容应当同时包含两个模板所需的变量,这样sub.html方能有你所想要的内容。

    5、啰嗦两句(没兴趣的直接跳过)

      有些同学很喜欢猜谜,喜欢猜出作者所想的共鸣感。那么我现在给出一句话,随着下面的学习,相信在我最终解释之前,你会提前明白这句话的:模板继承与模板包含,思路是相反的。

      但就我个人来说,非常反感猜谜。例如,很多书中都会先给出概念,然后隔很久才给出其实例(教材中屡见不鲜)。对这种做法,我简直是深恶痛绝。这会把我本来觉得很有意思的东西变得艰涩难懂,让我失去阅读的兴趣。因此,我向来都喜欢把文章尽我所能写得深入浅出,至少能让自己隔一段时间依旧很容易看懂。

      又忍不住啰嗦了几句,没兴趣的读者请见谅,忽略这些就好……咱们回到正题,来说模板的继承。

    6、模板继承代码

      这段最好还是直接看代码比较好理解,下面让我们来重新建一个网站,名字叫FOUR。

      首先按照咱们前面学的,把settings.py里面TEMPLATES参数的DIRS部分,加上一个路径:'./template/'。

      然后,在网站根目录建一个文件夹template,里面写出三个模板,内容如下:

    base.html

     1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
     2 <html lang="en">
     3 <head>
     4     <title>{% block title %}{% endblock %}</title>
     5 </head>
     6 <body>
     7     <h1>My helpful timestamp site</h1>
     8     {% block content %}{% endblock %}
     9     {% block footer %}
    10     <hr>
    11     <p>Thanks for visiting my site.</p>
    12     {% endblock %}
    13 </body>
    14 </html>

     current_datetime.html

    1 {% extends "base.html" %}
    2 
    3 {% block title %}The current time{% endblock %}
    4 
    5 {% block content %}
    6 <p>It is now {{ current_date }}.</p>
    7 {% endblock %}

     hours_ahead.html

    1 {% extends "base.html" %}
    2 
    3 {% block title %}Future time{% endblock %}
    4 
    5 {% block content %}
    6 <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
    7 {% endblock %}

      最后,在'./FOUR/'下,修改urls.py并且创建views.py,内容如下:

    urls.py

    1 from django.conf.urls import url
    2 from FOUR.views import *
    3 
    4 urlpatterns = [
    5     url(r'^time/$', current_datetime),
    6     url(r'^time/(d{1,2})/$', hours_ahead)
    7 ]

     views.py

     1 from django.http import Http404
     2 from django.shortcuts import render_to_response
     3 import datetime
     4 
     5 def current_datetime(request):
     6     now = datetime.datetime.now()
     7     return render_to_response('current_datetime.html', { 'current_date': now })
     8 
     9 def hours_ahead(request, offset):
    10     try:
    11         offset = int(offset)
    12     except ValueError:
    13         raise Http404()
    14     dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
    15     return render_to_response('hours_ahead.html', { 'hour_offset': offset, 'next_time': dt })

      至此,网站建立完成。运行后,便可进入以下网页:

    网址 内容
    http://127.0.0.1/time/ 显示当前时间(UTC时间)
    http://127.0.0.1/time/num/ 显示当前时间+num小时(num=8则得到中国标准时间)

    7、模板继承讲解

      其实,模板继承部分,大家仔细看上面代码,就应该能够看个八九不离十。就是A模板继承了B模板,把B模板中供替换的块选几个替换了,形成一个新的模板。

      下面,讲解其各种小细节,请结合代码学习。

    标签代码 解释 备注

    {% block name %}

      #...

    {% endblock %}

    供继承的部分

    注意,name不能重名

    如果继承后重写了,则显示重写内容;

    如果没有重写,则显示此处本来内容。

    {% extends name %}

    继承name模板

    name可以是常量,也可以是变量

    此标签必须是模板的第一个标记!否则,视为没进行模板继承。

    此标签的路径也是上文中模板的路径,即TEMPLATES中的DIRS

    {{ block.super name }}

    这是个变量,是父模板中的name标签的内容

    往往用于并不打算完全重写,而是添加几句话的情况

       在此说明一点,继承是可以多层嵌套的,即A模板继承B模板,B模板又继承了C模板

    8、模板包含与继承的比较

      模板包含,是在基础模板中写了大家都有的内容,然后后面都包含它;而模板继承,则是在基础模板中写了大家都有的部分,又标出了大家不同的部分,而后进行填充。

      因此,我认为继承更强大和顺手,而包含则更适用于比较简单的网页结构,因为太简单就没必要搞继承了。

    9、小吐槽

      书中认为,模板继承可以理解为模板包含的逆向思维。因为包含是对相同的代码段进行定义,而继承则是对那些不同的代码段进行定义。

      但个人认为,此处理解并不恰当,继承是比包含更加强大的,而非单纯的相反思路。

      当然,我这样是断章取义,书中自有其思路。另外,我看的是中文译本,也说不定和英文原版的意思有偏差呢~

      故而此处仅仅小小吐槽一下,目的只为理清读者思路。 

    ------------------------------------------------------------------------------------------------------------------------------------------------

      至此,“模板”一章笔记完成,下一章是与数据库打交道——“模型”。

  • 相关阅读:
    MySQL中内存分为全局内存和线程内存
    mysql 线程级别的缓冲区
    MySQL Handler变量解析
    HANDLER命令与实现
    MySQL Spatial Extensions 地理信息
    C++中临时对象的学习笔记
    HDC与CDC相互转换
    B树
    基于R-Tree的最近邻查询
    地图发布的几种服务的区别
  • 原文地址:https://www.cnblogs.com/icedream61/p/4518958.html
Copyright © 2020-2023  润新知