整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

「Django框架」-模板

「Django框架」-模板

源于公众号:Python野路子

一、模板介绍


我们之前学习的,都是在视图函数直接返回文本,在实际中我们更多的是带有样式的HTML代码,这样可以让浏览器渲染出非常漂亮的页面,目前市面上有非常多的模板系统,其中最常用的是DTLJinja2DTL(Django Template Language),也就是Django自带的模板语言,当然也可以配置Django支持Jinja2,但是作为Django内置的模板语言,不会产生一些不兼容的情况,最好还是使用内置的。

1. DTL与普通的HTML文件区别

DTL模板是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化,在编译完成后,生成一个普通的HTML文件,然后发送给客户端。

2. 模板渲染

可以使用render函数进行渲染,将数据渲染到指定的HTML模板中。

我们在之前的基础上,再新建一个articleapp,除了刚开始第一个应用,我们在pycharm创建项目的时候,填了app名字,pycharm会自动帮我们创建应用,后续如果还要再新建应用,我们都要使用命令方式才能新建app


应用创建完成了,接下来我们需要在配置文件settings.py里面注册应用app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'user.apps.UserConfig',   # 使用pycharm创建的应用,pycharm会自动注册
    'article'  # 手工命令方式创建的应用app,需要手工在这注册应用!
]

路由和视图代码:

# 主urls.py
path('article/', include('article.urls', namespace='article'))
    
# 应用urls.py,创建的应用这个文件不会自动生成,需要我们新建

from django.urls import path
from . import views

app_name = 'article'
urlpatterns = [
    path('index/', views.index, name='index')

]

# 视图views.py
from django.shortcuts import render

def index(request):
    return render(request, 'index.html')  # 使用render渲染模板

模板文件,项目根目录下templates目录(若没有自动生成,则手工新建此文件夹)下新建index.html模板文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    <h2>文章首页</h2>
</body>
</html>

查看效果:

使用render函数将模板成功渲染出来了。

3. 模板文件查找路径

在项目的settings.py配置文件中,有一个TEMPLATES配置。

TEMPLATES = [
    {
        # 模板引擎,就是Django内置的模板引擎,
        # 若要配置为Jinja2:django.template.backends.jinja2.Jinja2
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # BASE_DIR,项目根目录路径下的templates查找
        #'DIRS': [os.path.join(BASE_DIR, 'templates')] # Django2中使用
        'DIRS': [BASE_DIR / 'templates'],  #  Django3中使用
        ,
        # 是否从app应用下的templates下查找模板文件
        'APP_DIRS': True,
        'OPTIONS': {
            # 模板中间件
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

这个配置包含了模板引擎的配置,这个配置包含了模板引擎的配置,模板查找路径的配置,模板上下文的配置等,模板路径可以2个地方配置。

  1. DIRS:这是一个列表,在这个列表中可以存放所有的模板路径,以后在视图中使用render渲染模板时,就会从这个列表的路径中查找模板。
  2. APP_DIRS:默认为True,这个表示已经在INSTALLED_APPS文件中注册过的应用app下的templates目录下查找模板文件。
  3. 查找顺序:比如代码render(request,'index.html'),先会在DIRS这个列表中依次查找路径下有没有这个模板,如果有,就返回,如果没有,则会先检查当前视图所处的app是否已经安装,那么就先在当前app下的templates目录下查找模板,如果没有找到,就会去其他注册的app中继续查找,如果所有路径下都没有找到,则会抛出TemplateDoesNotExist

二、DTL模板语法

1. 模板变量

我们想要将后台数据渲染到页面上,这就需要用到模板变量。

# 主urls.py
path('article/', include('article.urls', namespace='article'))

# 应用urls.py
path('index/', views.index, name='index')



# 视图views.py
from django.shortcuts import render

class Article:
    def __init__(self, title):
        self.title = title


def index(request):
    context = {
        'msg': '模板变量',    # 字符串,
        'article': Article('Django框架之模板文件'),    # 实例一个对象,
        'author': {'name': 'admin'},    # 一个字典
        'tag': ['python入门', 'python进阶', '数据库']  # 列表或元组
    }

    return render(request, 'index.html', context=context)  # 传入一个字典

模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    <h2>文章首页</h2>
    <p>{{ msg }}</p> <!-- 在模板中使用变量,需要将变量放到 {{ 变量 }} 中 -->
    <h3>{{ article.title }}</h3>  <!-- 要访问对象的属性,通过"对象.属性名" 访问 -->
    <p>作者:{{ author.name }}</p>  <!-- 要访问字典key对应值value,通过"字典.key" 访问,不能[]形式 -->
    <ul>
        <li>{{ tag.0 }}</li>  <!-- 要访问列表或者元组的元素,通过"列表.索引" 访问 -->
        <li>{{ tag.1 }}</li>
        <li>{{ tag.2 }}</li>
    </ul>
</body>
</html>

效果:


在模板中使用变量:语法:{{变量名}} 1)命名由字母和数字以及下划线组成,不能有空格和标点符号。2)不要和python或django关键字重名。原因:如果data是一个字典,那么访问data.items将会访问data这个字典的key名为items的值,而不会访问字典的items方法。

2. 模板标签

标签语法:{% 标签名称 %} {% 结束标签名称 %}

例:{%tag%} {%endtag%}

2.1 if标签

if标签相当于python中的if语句,有elifelse相对应,可以使用and、or、in、not、==、!=、<=、>=、is、is not,来进行判断。

视图views.py

def index(request):
    age = random.randint(6, 20)  # 随机生成6-20的整数

    context = {
        'age': age
    }

    return render(request, 'index.html', context=context)

模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    <p>
        <span>年龄:<strong>{{ age }}</strong>,</span>
        <span>适合读</span>
        <span>
            {% if age >= 18 %}
                【大学】书籍
            {% elif 12 < age and age < 18 %}
                【高中】书籍
            {% else %}
                【小学】书籍
            {% endif %}
        </span>
    </p>
</body>
</html>


再刷新一次:


我们还可以使用ifequal/ifnotequal来比较两个值是否相等,如:

{% ifequal price 0 %}
    <span class="publicimg"><img src="/images/public.png"></span>
{% else %}
    <span class="publicimg"><img src="/images/vip.png"></span>
{% endifequal %}


2.2 for...in...标签

for标签相当于python中的for循环,可以遍历列表、元组、字符串、字典等一切可以遍历的对象。

# 视图views.py
def index(request):
    context = {
        'articles': [              # 列表
            '21天学会Python',
            'C++从入门到入土',
            'Mysql从入门到跑路'
        ],
        'author': {             # 字典
            'name': '老P',
            'age': 18,
            'sex': 'boy'
        },
        'artile_details': [
            {'title': '21天学会Python', 'author': '小P', 'words': 1000},
            {'title': 'C++从入门到入土', 'author': '中P', 'words': 2000},
            {'title': 'Mysql从入门到跑路', 'author': '老P', 'words': 3000}
        ]
    }

    return render(request, 'index.html', context=context)

模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    <ul>
        {% for article in articles %}
            <li>{{ article }}</li>
        {% endfor %}
    </ul>
    <p>获取字典键:</p>
    <ul>
        {% for key in author.keys %}
            <li>{{ key }}</li>
        {% endfor %}
    </ul>
    <p>获取字典值:</p>
    <ul>
        {% for value in author.values %}
            <li>{{ value }}</li>
        {% endfor %}
    </ul>
    <p>获取字典键值对:</p>
    <ul>
        {% for key, value in author.items %}
            <li>{{ key }}:{{ value }}</li>
        {% endfor %}
    </ul>

    <p>文章信息列表:</p>
    <table border="1">
        <thead>
            <tr>
                <th>序号</th>
                <th>标题</th>
                <th>作者</th>
                <th>字数</th>
            </tr>
        </thead>
        <tbody>
            {% for article_detail in artile_details %}
                {% if forloop.first %}   <!-- 是否第一次遍历 -->
                    <tr style="background: red;">
                {% elif forloop.last %}   <!-- 是否最后一次遍历 -->
                    <tr style="background: pink;">
                {% else %}
                    <tr>
                {% endif %}
                    <td>{{ forloop.counter }}</td>  <!-- 当前迭代的次数,下标从1开始。-->
                    <td>{{ article_detail.title }}</td>
                    <td>{{ article_detail.author }}</td>
                    <td>{{ article_detail.words }}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>

</body>
</html>

<!-- 
    forloop.counter:当前迭代的次数,下标从1开始。
    forloop.counter0:当前迭代的次数,下标从0开始。
    forloop.first:返回bool类型,如果是第一次迭代,返回true,否则返回false。
    forloop.last:返回bool类型,如果是最后一次迭代,返回True,否则返回False。

	forloop.revcounter         反向循环位置(列表的最后一位是l ,列表第一位是n )
	forloop.revcounter0        反向循环位置(列表的最后一位是0 , 列表第一位是n- 1 )
-->


还有个for...in...empty,在遍历的时候如果需要遍历的对象没有为空,则执行empty下的操作。

    {% for comment in comments %}  
        {{ comment }}
    {# 在for循环之前检查列表大小是常见的,当列表为空的时候给出特别提示,这是常见的,所以for支持可选empry来当为空时输出 #}    
    {% empty %}   <!-- comments没有或为空,则执行empty下的。-->
        还没有评论~~~
    {% endfor %}

有时候可能在页面上要用上循环几次,可以用:

{% for item in  'x'|ljust:'4' %}  循环四次
{%endfor %}

2.3 with标签

使用一个简单地名字缓存一个复杂的变量,当你需要使用一个代价较大的方法(比如访问数据库)很多次的时候这是非常有用的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    {{ title }}

    {% with t1=title %}  <!-- 使用=-->
        {{ t1 }}
    {% endwith %}

    {% with title as t2%} <!-- 使用as 取别名 -->
        {{ t2 }}
    {% endwith %}
</body> 
</html>


注意:定义的变量只能在with语句块中使用,在with语句块外面使用取不到这个变量。

2.4 url标签

在模板中,经常要写一些超链接,比如a标签中需要定义href属性,当然如果通过硬编码的方式直接将这个url写死也是可以,但是不便于以后维护,比如需要修改url地址,那涉及到的地方都要修改,因此建议使用这种反转的方式来实现,类似于django中的reverse一样。

# 应用urls.py
from django.urls import path
from . import views

app_name = 'article'
urlpatterns = [
    path('index/', views.index, name='index'),
    path('book/', views.book, name='book'),
    path('movie/', views.movie, name='movie'),
    path('city/', views.city, name='city'),
    path('book/hot/<int:book_id>', views.hot_book, name='hot_book')
]

# 视图views.py
from django.shortcuts import render, HttpResponse

def index(request):
    return render(request, 'index.html')

def book(request):
    return HttpResponse('读书页面')

def movie(request):
    return HttpResponse('电影页面')

def city(request):
    return HttpResponse('同城页面')

def hot_book(request, book_id):
    return HttpResponse('最热门文章:%d'%book_id)

模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
    <style>
        li{
            list-style: none;
            display: inline-block;
            margin-right: 20px ;
        }
    </style>
</head>
<body>
    <ul>
        <li><a href="">首页</a></li>
        <li><a href="/article/book/">读书</a></li>  <!-- url硬编码的方式 -->
        <li><a href="{% url 'article:movie' %}">电影</a></li> <!-- url标签 -->
        <li><a href="{% url 'article:city' %}">同城</a></li>
        <li><a href="{% url 'article:hot_book' book_id=1 %}">最火文章</a></li> <!-- url标签 传递参数,这个参数需要于url映射的参数名一样,如果传递多个参数,则空格隔开 -->
    </ul>
</body>
</html>


2.5 autoescape标签

DTL中默认已经开启了自动转义,会将哪些特殊字符进行转义,比如会将<转义成<等。

def index(request):
    comment_info = '个人博客网站:<a href="http://www.qmpython.com"> 全民python</a>'
    context = {
        'info': comment_info
    }
    return render(request, 'index.html', context=context)

模板文件:

 <p>个人博客网站:<a href="http://www.qmpython.com"> 全民python</a></p>
 <p>
      {{ info }}   <!-- DTL默认on开启了自动转义,转换成普通字符串 -->
 </p>
 <p>
     {% autoescape off %}   <!-- off关闭自动转义,解析html标签 -->
        {{ info }}
     {% endautoescape %}
 </p>


如果你不知道自己在干什么,最好是使用自动转义,这样网站才不容易出现XSS漏洞(比如有些网站进行评论的时候,发一些含恶意的js代码,如果不进行自动转义会进行渲染,而不会做成普通的字符串),比如我个人博客的评论就处理了。如果是可信用的,可以关闭自动转义,比如我的文章内容是只允许我个人发表,是可信任的,所以我直接关闭。后面可以通过过滤器safe类似处理。

2.6 verbatim标签

默认在DTL模板中是会去解析那些特殊字符的,比如{% %}{{ }}等,如果我们在某个代码片段不想使用DTL的解析,那么我们可以将这段代码放在verbatim标签中。

# 视图
def index(request):
    return render(request, 'index.html')

# 模板
<body>
    {% if msg %}
        {{ msg }}
    {% else %}
        没消息
    {% endif %}
    <br>
    {% verbatim %}
        {% if msg %}
            {{ msg }}
        {% endif %}
    {% endverbatim %}
</body>


这个主要是用在,有些前端模板语法layui还有vue中也使用{{}}来表示变量,这些与DTL类似,这样就容易引起混淆,我们可以使用这个标签,来屏蔽掉DTL的解析,而让前端模板去解析。

2.7 注释标签

我们在HTML中经常使用<!-- 注释内容 -->来进行注释,在Django模板中,我们还可以使用其他注释。

{# 被注释的内容 #}:将中间的内容注释掉。只能单行注释。
{% comment %}被注释的内容{% endcomment %}:可以多行注释。

这种注释,当我们查看网页元素的时候是看不到的,即没有被渲染,而<!-- -->还是可以看到原样字符串的。

2.8 自定义简单标签

1)首先,需要添加一个templatetags的文件夹, 自定义标签必须处在已经安装了的app(INSTALLED_APPS注册了)中的一个名叫templatetags的包中。

templatetags 文件夹名字不能修改,这是django规定的。

myproject
    |——myproject
     |——__init__.py
     |——asgi.py
     |——settings.py
     |——urls.py
     |——wsgi.py
    |——templates
    |——myapp
     |——migrations
     |——templatetags
      |——__init__.py
      |——custom_tags.py
    |——manage.py

2)在templatetags下添加一个python文件,如我这里创建一个custom_tags.py文件,在文件中添加对应的自定义标签。

from django import template

# register的名字是固定的,不可改变
register = template.Library()

#使用装饰器注册自定义标签  
@register.simple_tag
def curr_date(args):#args可传参数,根据实际需求而定
    return datetime.datetime.now().strftime(args)

3)在要使用自定义标签的HTML模板中导入和使用自定义标签文件:

{# 加载自定义标签所在的文件名,由于templatetags的文件名是固定的,django可以直接找到过自定义标签文件所在的位置 #}
{% load custom_tags %}

<h3>自定义标签</h3>
时间日期:{% curr_date "%Y-%m-%d"%}

我们再来定义简单标签article/templatetags/menu_tags.py

from django import template

# register的名字是固定的,不可改变
register = template.Library()
@register.simple_tag
def my_menu():
    menu_list = ['首页','项目实战','每日一练']  
  
    return menu_list

模板文件templates/article/my_menu.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义简单标签</title>
</head>
<body>
<h1>-----------菜单栏--------------</h1>
<br/>
{% load menu_tags %}	<!-- 导入自定义的标签文件 -->

{% my_menu as menus %}  <!-- 注意:这里需要使用as将标签文件返回的值赋给一个变量,然后再引用!!!-->

<ul>
    {% for menu in menus %}  {# 企图使用 {% for menu in my_menu %} 是没效果的 #}
        <li>{{menu}}</li>
    {% endfor %}
</ul> 

</body>
</html>

视图渲染views.py

def test(request):
    return render(request, 'article/my_menu.html')


2.9 自定义包含标签inclusion_tag

一种比较普遍的tag类型只是渲染其他模块显示下的内容,这样的类型叫做inclusion_tag,常用的模板标签是通过渲染其他模板显示数据的。

inclusion_tag作用:创建一个动态页面文件a.html,这个页面可以在另外一个页面b.html中被调用,实现这个页面a中有的功能。

例如,上面一个导航页面my_menu.html,基本在每个页面中都要包含导航,如我的个人博客一样:



那要怎么样呢?

例如,我们在首页模板中,先试下用include来引入导航模板看看(include这个后面具体学习,这里只演示作用)。

与上面自定义简单标签类似,在自定义标签文件里面自定义包含标签:

from django import template

from article.models import Column

# register的名字是固定的,不可改变
register = template.Library()
#使用装饰器注册自定义包含标签
@register.inclusion_tag('article/my_menu.html') #将返回值传给my_menu.html去,可以定义name,否则使用函数名
def my_menu():
    # menus = ['首页','项目实战','每日一练']
    menus = Column.objects.all()
    
    return {'menus':menus}  

my_menu.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义包含标签</title>
</head>
<body>
<ul>
    {% for menu in menus %}
      <li> {{menu}} </li>
    {% endfor %}
</ul>
</body>
</html>

调用的html页面文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

{% load menu_tags %}   <!-- 必须要先加载创建标签的文件-->

{% my_menu %}    <!-- 调用my_menu.html页面文件,这里使用该标签的函数名来调用-->

<h1>首页</h1>

</body>
</html>

视图渲染:

def test(request):

    return render(request, 'article/index.html')

2.10 人性化语义标签

除了上述功能性标签外, Django 还提供了很多辅助性标签,这些标签只是为了使变量输 出变得更加可读,下面对这些标签进行简单介绍。首先为了使用这些标签,需要在INSTALLED_APPS 中注册django .contrib.humanize, 然后在模板中引用humanize:{% raw %}{% load humanize % }{% endraw %}

apnumber

将数字1 ~ 9 转换为英文单词,但是其他数字不转换,如数字10 将被原样输出。示例:

数字1 被转换为one ;
数字2 被转换为two ;
数字10 仍显示10 ;

如果当前工程语言是中文的话,数字将会被转换为对应的汉字,例如:

{% raw %}
{{ 1|apnumber }}
{{ 2|apnumber }}
{{ 5|apnurnber }}
{% endraw %}

如果当前工程语言是中文的话,数字将会被转换为对应的汉字,例如:

输出:

一
二
五

intcomma

输出以逗号分隔的数字,如4500 输出4,500, 4500.2 输出4,500.2 。

intword

以文字形式输出数字,如1000000 输出“ 1.0 million ”, 1200000 输出“ 1,2 Million ” 。对于中文系统,将会输出对应的中文,如1200000 输出" 1.2 百万” 。

naturalday

将当前日期以及前后一天输出为today 、yesterday 和tomorrow ,而中文系统分别输出 “今天”“昨天”和“明天” 。

naturaltime

对于日期时间格式,时间值与系统当前时间比较,然后输出结果。如当前时间输出 “ now ”, 29 秒前输出“ 29 sec onds ago ” 。如果使用naturaltime 输出今天、昨天、明天的话, 就会变成“现在”“ 23 小时·以后”“ 1 日之前” 。

ordinal

将数字转换为序数,如l 输出“ 1 st ”;2 输出“ 2nd ”;3 输出“ 3rd ” 。注意此时中文与 英文的输出一样。

3. 模板过滤器

在模板中,有时候需要对一些数据进行处理以后才能使用,一般在Python中我们是通过函数的形式来完成的,因为在DTL中,不支持函数的调用形式(),因此在模板中,则是通过过滤器来实现的,过滤器使用的是|来使用。语法:

{{ 变量名|过滤器名:传给过滤器的参数}}

3.1 常用过滤器

比如使用date过滤器。

# 视图
from datetime import datetime

def index(request):
    now_time = datetime.now()
    context = {
        'now_time': now_time
    }
    return render(request, 'index.html', context=context)


# 模板文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    {{ now_time }} <!-- 原始渲染 -->
    <br>
    {{ now_time | date:'Y-m-d H:i:s' }}  <!-- 通过过滤器,先将日期格式化,再渲染出-->
</body>
</html>


date时间过滤器格式:

格式字符描述示例Y4位数的年1999y2位数的年99m2位数的月01,09n1位数的月1,9,12d2位数的日01,09,31j1位数的日1,9,31g12小时制的一位数的小时1,9,12G24小时制的一位数小时0,8,23h12小时制的两位数的小时01,09,12H24小时制的两位数的小时01,13,24i分钟00-59s秒00-59

django常用过滤器:

add:字符串相加,数字相加,列表相加,如果失败,将会返回一个空字符串。default:提供一个默认值,在这个值被django认为是False的时候使用。比如:空字符串、None、[]、{}等。区别于default_if_none,这个只有在变量为None的时候才使用默认值。first:返回列表中的第一个值。last:返回列表中的最后一个值。date:格式化日期和时间。time:格式化时间。join:跟python中的join一样的用法。length:返回字符串或者是数组的长度。length_is:字符串或者是数组的长度是否是指定的值。lower:把所有字符串都编程小写。truncatechars:根据后面给的参数,截断字符,如果超过了用表示。{{value|truncatechars:5}},如果value是等于北京欢迎您,那么输出的结果是北京...,为啥不是北京欢迎您...呢?因为三个点也占了三个字符,所以北京 + 三个点的字符长度就是5。truncatewords:类似truncatechars,不过不会切割html标签,{{value|truncatewords:5}},如果value是等于<p>北京欢迎您</p>,那么输出的结果是<p>北京...</p>capfirst:首字母大写。slice:切割列表。用法跟python中的切片操作是一样的,区间是前闭合后开放。striptags:去掉所有的html标签。safe:关闭变量的自动转义,解析html标签。floatformat:浮点数格式化。更多可以查询官方文档:https://yiyibooks.cn/xx/Django_1.11.6/ref/templates/builtins.html英文:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/

过滤器总结:1、作用:对变量进行过滤。在真正渲染出来之前,过滤器会根据功能处理好变量,然后得出结果后再替换掉原来的变量展示出来。2、语法:{{greeting|lower}},变量和过滤器中间使用管道符号”|”进行使用。3、可以通过管道符号进行链式调用,比如实现一个功能,先把所有字符变成小写,把第一个字符转换成大写,如{{message|lower|capfirst}}

4、过滤器可以使用参数,在过滤器名称后面使用冒号”:”再加上参数,比如要把一个字符串中所有的空格去掉,则可以使用cut过滤器,代码如下{{message|cut:" "}},冒号和参数之间不能有任何空格,一定要紧挨着。

3.2 自定义过滤器

虽然DTL给我们内置了许多好用的过滤器,但是有些时候还是不能满足我们的需求,因此Django给我们提供了一个接口,可以让我们自定义过滤器,实现自己的需求。

步骤:

1、首先在某个应用app中,创建一个python包,叫做templatetags,注意名字不能改动,不然找不到。

2、在这个templatetags包下创建一个python文件用来存储过滤器。

3、在新建的python文件中,定义过滤器(也就是函数),这个函数的第一个参数永远是被过滤器的那个值,并且如果在使用过滤器的时候传递参数,那么还可以定义另外一个参数,但是过滤器最多只能有2个参数。

4、自定义完过滤器,要使用django.template.Library.filter进行注册。

5、过滤器所在app所在app需要在settings.py中进行注册。

6、在模板中使用load标签加载过滤器所在的python包。

7、在模板中使用自定义的过滤器。

我们来实现一个朋友圈,或者文章发表时间显示的过滤器:

# 自定义过滤器 date_filter.py
from django import template

from datetime import datetime, timedelta

# 将注册类实例化为register对象
# register=template.Library() 创建一个全局register变量,它是用来注册你自定义标签和过滤器的,只有向系统注册过的tags,系统才认得你。
# register 不能做任何修改,一旦修改,该包就无法引用
register = template.Library()

# 使用装饰器注册自定义过滤器
@register.filter
def date_filter(value):
    # 判断一下,是不是时间日期类型
    if not isinstance(value, datetime):
        return value

    now = datetime.now() + timedelta(hours=8)  # 将UTC时间转为本地时间

    # total_seconds()是获取两个时间之间的总差
    timestamp = (now - value).total_seconds()

    if timestamp < 60:
        return '刚刚'
    elif 60 <= timestamp < 60*60:   # sec >= 60 and sec < 60*60
        mint = int(timestamp / 60)
        return '{}分钟前'.format(mint)
    elif 60*60 <= timestamp < 60*60*24:
        hour = int(timestamp / 60 / 60)
        return '{}小时前'.format(hour)
    elif 60*60*24 <= timestamp < 60*60*24*2:
        return '昨天'
    else:
        return timestamp.strftime('%Y-%m-%d %H:%M:%S')


# 视图views.py
from django.shortcuts import render, HttpResponse

from datetime import datetime

def index(request):
    context = {
        'public_time': datetime(year=2020, month=3, day=23, hour=23, minute=0, second=0)
    }
    print(context)
    return render(request, 'index.html', context=context)



# 模板文件
{% load date_filter %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
</head>
<body>
    {{ public_time|date_filter }}

</body>
</html>


4. 模板结构化优化

4.1 引入模板

有时候一些代码是在许多模板中都用到的,如果每次都重复的去拷贝代码那肯定不符合项目的规范,一般我们可以把这些重复性的代码抽取出来,就类似于Python的函数一样,以后想要使用这些代码的时候,就通过include包含进来,这个标签就是include。比如大多数网页都有顶部,中间,底部,可能进入任何页面顶部和底部都是一样的,只有中间部分内容不同,这个时候顶部和底部,我们就可以通过include包含起来。


# 顶部,header.html
<header>
    <ul>
        <li>首页</li>
        <li>Python教程</li>
        <li>Python Web开发</li>
        <li>Python爬虫</li>
    </ul>
    <p>用户名:{{ username }}</p> <!-- 用于测试是否传参 -->
</header>

# 底部,footer.html
<footer>
    <div>?2019<a href="/">全民python</a> | 
        <a href="http://beian.miit.gov.cn" rel="nofollow">粤ICP备18143893号</a> | 
    </div>
</footer>

# 首页,index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文章首页</title>
    <style>
        div{
            margin: 10px;
        }
    </style>
</head>
<body>
  {% include 'header.html' with username='全民python' %}  <!-- 引入模板,并用with传递参数 -->
  <div>
    这是中间内容
  </div>
  {% include 'footer.html' %}
</body>
</html>

视图:

def index(request):
    return render(request, 'index.html')


我们使用include标签时,如果引入的html代码中需要传入变量,则需要在所引入的view视图中获取变量,如果引入的html代码中的变量是一个公共变量,则需要重复获取N次,使用自定义包含标签inclusion_tag,可以在定义文件中获取一次即可。

templates/article/my_menu.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义简单标签</title>
</head>
<body>
<h1>-----------菜单栏--------------</h1>
<br/>
{% load menu_tags %}	<!-- 导入自定义的标签文件 -->

{% my_menu as menus %}  <!-- 注意:这里需要使用as将标签文件返回的值赋给一个变量,然后再引用!!!-->

<ul> 
    {% for menu in menus %} {# 企图使用 {% for menu in my_menu %} 是没效果的 #}
        <li>{{menu}}</li>
    {% endfor %}
</ul> 

</body>
</html>

article/templatetags/menu_tags.py

from django import template
from article.models import Column

# register的名字是固定的,不可改变
register = template.Library()
@register.simple_tag
def my_menu():
    #menu_list = ['首页','项目实战','每日一练']  
    menu_list = Column.objects.all()
  
    return menu_list

templates/article/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

{% include 'article/my_menu.html' %}

<h1>首页</h1>

</body>
</html>


两者感觉效果一样。

4.2 模板继承

对于模板的重复利用,除了使用include标签引入,还有另外种方式来实现,那就是模板继承。模板继承类似于Python中的类,在父类中可以先定义好一些变量和方法,然后在子类中实现,模板继承也可以在父模板中先定义好一些子模板需要用到的代码,然后子模板直接继承就可以了,并且因为子模板肯定有自己的不同代码,因此可以在父模板中定义一个block接口,然后子模板再去实现。

我们将上面那个例子,使用模板继承改写下,将固定不变的顶部和底部,放在父模板中,中间变动的部分,子模板去实现。

<!-- 父模板-->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}全民python{% endblock %}</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        header,footer{
            background: black;
            height: 60px;
            width: 100%;
            color: white;
            text-align: center;
            line-height: 40px;
        }
        footer{
            height: 90px;
            line-height: 90px;
            position: absolute;
            bottom: 0;
        }
        ul li{
            list-style: none;
            float: left;
            margin: 20px;
        }
        #loginbox{
            width: 400px;
            height: 200px;
            border: 1px solid red;
            margin: 90px auto;
            text-align: center;
        }
    </style>
</head>
<body>
    <header>
       <ul>
           <li>首页</li>
           <li>编程语言</li>
           <li>项目实战</li>
           <li>每日一学</li>
        </ul>
    </header>
    <div class="content">
        {% block content %}
            这是子模板自己实现的部分
        {% endblock %}
    </div>
    <footer >
        这是footer部分
    </footer>
</body>
</html>


<!-- 子模板-->
{% extends 'base.html' %}  <!-- extends必须是模板中的第一个出现的标签 -->
{% block title %}{{ block.super }}-登录界面{% endblock %} <!-- block.super继承父类的内容,否则会覆盖父类内容 -->
{% block content %}
    <div id="loginbox">
        <br/>
        <h2>登录界面</h2>
        <br/>
        <form>
            帐 号:<input type="text">
            <br/>
            <br/>
            密 码:<input type="password">
            <br/>
            <br/>
            <input type="submit" value="登录">
                
            <input type="button" value="注册">
        </form>
    </div>
{% endblock %}

视图函数:

def index(request):
    return render(request, 'index.html')

效果展示:


总结:

  1. 模板继承使用extends标签实现,通过使用block来给子模板开放接口;
  2. extends必须是模板中第一个出现的标签;
  3. 子模板中的内容必须放在父模板定义好的block中,否则将不会被渲染;
  4. 如果在某个block中需要使用父模板的内容,那么可以使用{{ block.super }}来继承;
  5. 子模板决定替换的block块,无须关注其它部分,没有定义的块即不替换,直接使用父模板的block块;
  6. 除了在开始的地方定义名字,我们还可以在结束的时候定义名字,如{% block title %}{% endblock %},这样在大型模板中显得尤其有用,能让我们快速看到block所包含的开始和结束。

模板引入与自定义inclusion_tag的区别:模板导入的页面内容是静态的、不变的,而通过自定义inclusion_tag导入的页面文件可以是动态的,可动性自己掌控。

5. 加载静态文件

在一个网页中,不仅仅只有一个html,还需要css样式,js脚本以及一些图片,因此在DTL中加载静态文件是一个必须要解决的问题,在DTL中,使用static标签来加载静态文件,要使用static标签,首先需要{% load static %}

1、首先,settings.py文件中,确保django.contrib.staticfiles已经添加到INSTALLED_APPS中。

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    ...
]

2、设置DEBUG调试模式。

# 在settings.py中,默认值是DEBUG = True
DEBUG = True

当我们在开发django应用时如果设置了 DEBUG=True,那么django便会自动帮我们对静态文件进行路由;但是当我们设置DEBUG=False后,静态文件找不到了,「img、css、js」都提示404,无法准确的访问 static 静态文件。

3、在开发模式下(Debug=True)时,访问静态文件有下面两种情况:1)在已注册的应用app下创建一个静态文件夹名称static,然后再再这个static目录下创建于app同名的 文件夹用于存放静态文件(避免不同应用app,静态文件名字一样,造成冲突)。

Django将通过 django.contrib.staticfiles在每个appstatic文件夹中为我们自动查找这些静态文件。

2)如果有些静态文件和应用没什么太大的关系,我们可以在项目根目录下再创建static目录,这个时候我们还需要在settings.py文件配置添加STATICFILES_DIRS,以后DTL就会在这个列表的路径中查找静态文件。

# django2.x方式
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]  # 这里指静态文件保存哪个目录下,这个“static”指目录名

# django3.x方式
# 一般用来设置通用的静态资源,对应的目录不放在APP下,而是放在Project下,例如:
STATICFILES_DIRS = [BASE_DIR / 'static']

然后再像上面一样加载静态文件使用,一般直接在根项目下新建static,然后在下面新建不同以应用名称命名的目录。

STATICFILES_DIRS告诉django,首先到STATICFILES_DIRS里面寻找静态文件,其次再到各个appstatic文件夹里面找(注意,django查找静态文件是惰性查找,查找到第一个,就停止查找了)。

4、只有在生产模式(Debug=False)时,STATIC_ROOT设置才生效。

STATIC_ROOT 是在部署静态文件时(pyhton manage.py collectstatic)所有的静态文静聚合的目录,STATIC_ROOT要写成绝对地址,如下例子STATIC_ROOT设为根目录下的static_new文件,而STATICFILES_DIRS使用根目录下的static目录。

#STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]  # 这里指静态文件保存哪个路径,这个“static”指目录名
STATICFILES_DIRS = [BASE_DIR / 'static']

if not DEBUG:
    #STATIC_ROOT = os.path.join(BASE_DIR, "static_new") #使用 collectstatic后收集的静态文件的存放绝对路径
    STATIC_ROOT = [BASE_DIR / 'static_new']

当部署项目时,在终端输入:

# django会把所有的static文件都复制到STATIC_ROOT文件夹下
python manage.py collectstatic


然后可以看到根目录下有创建了一个static_new文件夹,里面有STATICFILES_DIRS里设置的文件夹里的静态文件,同时也有各appstatic文件夹里的静态文件。

在部署模式(Debug=False)时,Django会从STATIC_ROOT设置的文件夹读取静态文件,而不再从STATICFILES_DIRS设置的文件夹或app下的static文件夹。所以在(Debug=False)时需要先python manage.py collectstatic同步一下静态文件。

说明:真实上生产,静态文件可能就交给nginx了,配置好即可,就不会再从django里面读取静态文件了。具体后续我们会细说

5、上面我们已经配置好静态文件了,那怎么样访问呢?

如果在浏览器是访问,不可能输入你的静态文件的本地绝对地址吧,比如我的一种图片的本地地址为 /root/src/project/QmpythonBlog/static/image/article/article_cover.png,那我们不可能

在浏览器上直接输入:http://127.0.0.1:5044/root/src/project/QmpythonBlog/static/image/article/article_cover.png 这样子,浏览器会报错, 没有该页面,那么django是如何让浏览器也可以访问服务器上的静态文件呢?

上面已经说了,直接访问服务器本地的地址是不行的,那就需要一个映射。

django利用STATIC_URL来让浏览器可以直接访问静态文件。

settings.py文件中确保配置了STATIC_URLSTATIC_URL用于引用STATICFILES_DIRSSTATIC_ROOT所指向的静态文件。

当我们创建Django项目的时候,在setting.py中默认就已经设置了。

# 静态文件存储,一般是我们的JS、css、系统的图片文件等。
# 这个“static”指访问静态文件,引入时用的别名
# 通过http://127.0.0.1/static/***就可以访问相关的静态文件了。

STATIC_URL = '/static/'

例如:在应用user下新建static目录,再在static下新建user目录:


视图:

# user应用下的视图views.py

def index(request):
    return render(request, 'user/index.html')

模板文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录首页</title>
</head>
<body>
    <p>用户界面</p>
    <img src="/static/user/img/logo.png">  <!-- 如果配置文件是 STATIC_URL='/abc/',则这里url:/abc/user/img/logo.png-->
</body>
</html>

但是上面要写绝对路径不方便,那我们可以通过static标签来加载静态文件:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录首页</title>
</head>
<body>
    <p>用户界面</p>
{#    <img src="/static/user/img/logo.png">#}

    <img src="{% static '/user/img/logo.png' %}"> <!-- 通过static标签,django会去static目录下寻找 -->
</body>
</html>

{% static %}这个模板变量使用的就是STATIC_URL的值,例如默认的STATIC_URL值为/static/,那么上面模板变量引用后的值便为/static/开头的地址。

那么STATIC_URL又是怎么能正确的找到静态文件地址的呢。

1)在开发模式下(Debug=True)时,使用的是STATICFILES_DIRSapp下的static中的静态文件,Django有默认的对STATIC_URL路由能自动找到放在里面的静态文件。

2)在部署模式(Debug=False)时,使用的是STATIC_ROOT中的静态文件,此时则没有了默认的对STATIC_URL的路由,需要自己在projecturls.py中写一个,将STATIC_URL开头的请求转发到STATIC_ROOT中进行查找。

from django.views.static import serve
from django.conf.urls.static import static
urlpatterns = [
    re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}, name='static'),
]

在部署生产环境时(Debug=False),通常使用另外一种方法就是使用Nginx来实现。

如果你在项目中用到了static这个模板标签,那一定要将nginx(或其他)服务器的/static配置到与STATIC_ROOT一致!(如果执行了收集的话)

可以参考django部署配置:Centos7中使用Nginx+uWSGI+Django部署Web项目

此时我们在应用中加载静态文件的话就只需要这么来写了,需要将STATIC_URL设为/static/,以img加载为例:

<img src="/static/images/good.png" alt="My image"/>

额外知识点:

1、如果不想在模板文件中每次都通过{% load static %}加载静态文件。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        #'DIRS': [os.path.join(BASE_DIR, 'templates')]  # django2写法
         'DIRS': [BASE_DIR / 'templates'],  # django3写法
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'builtins': ['django.templatetags.static']  # 添加这一行,就会将static当作内置的标签,无需再手动load
        },
    },
]

备注:一般个人倾向于,直接将static文件放到项目根目录下,下面再根据app名字进行分门别类。



#Django#

伙伴们大家好,本期想给大家分享一个模板,就是基于HTML+CSS+JS来模仿京东商城的前端界面。感兴趣的小伙伴可以转发该文,私信小编获取代码。

先看一下主界面的展示效果图,为gif动态图,可点击查看。

主要技术点

1、主界面采用平滑的轮播图动态展示商品,左侧采用侧边导航栏+多个浮动DIV展示商品具体信息,上方为导航栏,右侧用浮动DIV完成公告栏的设计。

2、除了正常看到的界面,在登陆和注册界面中,使用juery完成数据的验证和友好的样式展示。

3、此商城模板主要包括主界面、登陆界面、注册界面、客户问题反馈界面。

主界面

登陆界面

注册界面

客户反馈界面

商品详情界面

最后上一下代码图,感兴趣的小伙伴可以转发+私信小编获取代码。同时欢迎持续关注小编相互交流。

介绍:

art-template 是一个简约、超快的模板引擎。 它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器。

1.1 模板语法:

art-template 同时支持两种模板语法。标准语法可以让模板更容易读写;原始语法具有强大的逻辑处理能力。

标准语法

{{if user}}
<h2>{{user.name}}</h2>
{{/if}}

原始语法

<% if (user) { %>
<h2><%=user.name %></h2>
<% } %>

1.2 核心方法:

// 基于模板名渲染模板
template(filename, data);

// 将模板源代码编译成函数
template.compile(source, options);

// 将模板源代码编译成函数并立即执行
template.render(source, data, options);

2 安装

2.1 安装方法:

  • 通过npm安装: npm install art-template --save
  • 下载安装

2.2 在浏览器中编译

因为浏览器不支持文件系统,所以 template(filename, data) 不支持传入文件路径,它内部使用 document.getElementById(filename).innerHTML 来获取模板,例如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 引入template-web.js -->
    <script src="./node_modules/art-template/lib/template-web.js"></script>
</head>
<body>
    <div id="container"></div>
    <!-- 创建 script 标签创建模板,注意下面几点 -->
    <!-- 1. type="text/该斜杠后可以是 html,template... 不是script即可)" -->
    <!-- 2. 给 script 标签添加 id ,此 id 即为模板 id -->
    <!-- 3.模板 script 标签必须在 template() 方法调用的 script 标签之前 -->
    <script type="text/html" id="tpl">
        {{if user}}
        <h2>{{user.name}}</h2>
        {{/if}}
    </script>
    <script>
        var user={
            name: 'Template username'
        }
        var html=template('tpl', {user: user})
        var container=document.querySelector('#container');
        container.innerHTML=html;
    </script>
</body>
</html>

浏览器打开看到的结果如下:


通过浏览器渲染模板.png

3 语法

art-template 支持标准语法与原始语法。标准语法可以让模板易读写,而原始语法拥有强大的逻辑表达能力。

标准语法支持基本模板语法以及基本 JavaScript 表达式;原始语法支持任意 JavaScript 语句,这和 EJS 一样。

3.1 输出


标准语法

{{value}}
{{data.key}}
{{data['key']}}
{{a ? b : c}}
{{a || b}}
{{a + b}}

原始语法

<%=value %>
<%=data.key %>
<%=data['key'] %>
<%=a ? b : c %>
<%=a || b %>
<%=a + b %>

3.2 原文输出


标准语法

{{@ value}}

原始语法

<%- value %>

3.3 条件输出


标准语法

<!-- 单 if 判断 -->
{{if value}} 
... 
{{/if}}

<!-- if ... else ... 判断 -->
{{if v1}} 
... 
{{else if v2}}
 ... 
{{/if}}

原始语法

<!-- 单 if 判断 -->
<% if (value) { %>
...
<% } %>

<!-- if ... else ... 判断 -->
<% if (v1) { %>
...
<% else if (v2) { %>
...
<% } %>

3.4 循环输出


标准语法

{{each target}}
  {{$index}} {{$value}}
{{/each}}

target是一个数组,each用于对数组遍历,$index 是数组的下标, $value是数组的值 原始语法

<% for (var i=0; i < target.length; i++) { %>
<%=i %> <%=target[i] %>
<% } %>

注意:

  1. target 支持 array 与object 的迭代,其默认值为 $data。
    具体看下面的例子:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./node_modules/art-template/lib/template-web.js"></script>
</head>
<body>
    <div id="container"></div>
    <script type="text/html" id="tpl">
        <ul>
            {{each user.arr}}
            <li>
                {{$index + 1}} ---- {{$value.type}} ---- {{$value.price}}
                {{$data}}
            </li>
            {{/each}}
        </ul>
    </script>
    <script>
        var user={
            obj: {
                name: 'Bruce Lee',
                age: 32,
                gender: 'male'
            },
            arr: [
                {type: 1, price: 10},
                {type: 2, price: 12},
                {type: 3, price: 18}
            ] 
        }
        var html=template('tpl', {user: user})
        var container=document.querySelector('#container');
        container.innerHTML=html;
    </script>
</body>
</html>

看输出结果:


each遍历对象target(默认为$data).png

图中可以看出$data其实就是传入模板的总数据对象(原始数据对象)

  1. $value 与 $index 可以自定义:{{each target val key}}。
    具体看下面例子:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./node_modules/art-template/lib/template-web.js"></script>
</head>
<body>
    <div id="container"></div>
    <script type="text/html" id="tpl">
        <h4>each 遍历数组,采用默认的索引 $index 和默认的值 $value</h4>
        <ul>
            <!-- each 遍历数组,采用默认的索引 $index 和默认的值 $value -->
            {{each user.arr}}
            <li>
                {{$index}} ---- {{$value}}
            </li>
            {{/each}}
        </ul>

        <h4>each 遍历数组, 采用自定义的索引 b 和默认的值 a</h4>
        <ul>
            <!-- each 遍历数组, 采用自定义的索引 b 和默认的值 a -->
            {{each user.arr b a}}
            <li>
                {{a}} ---- {{b}}
            </li>
            {{/each}}
        </ul>

        <h4>each 遍历对象, 采用默认的键 $index 和默认的值 $value</h4>
        <ul>
            <!-- each 遍历对象, 采用默认的键 $index 和默认的值 $value  -->
            {{each user.obj}}
            <li>
                {{$index}} ---- {{$value}}
            </li>
            {{/each}}
        </ul>

        <h4>each 遍历对象,采用自定义的键 key 和自定义的值 val</h4>
        <ul>
            <!-- each 遍历对象,采用自定义的键 key 和自定义的值 val -->
            {{each user.obj val key}}
            <li>
                {{key}} ---- {{val}}
            </li>
            {{/each}}
        </ul>
    </script>
    <script>
        var user={
            obj: {
                name: 'Bruce Lee',
                age: 32,
                gender: 'male'
            },
            arr: [
                { type: 1, price: 10 },
                { type: 2, price: 12 },
                { type: 3, price: 18 }
            ]
        }
        var html=template('tpl', { user: user })
        var container=document.querySelector('#container');
        container.innerHTML=html;
    </script>
</body>
</html>

看输出结果:


each遍历数组和对象以及自定义$data和$index.png

3.5 定义变量

标准语法

{{set temp=data.sub.content}}

原始语法

<% var temp=data.sub.content %>

3.6 模板继承

标准语法

{{extend './layout.html'}}
{{block 'head'}}
...
{{/block}}

原始语法

<% extend ('./layout.html') %>
<% block('head', function () { %>
...
<% }) %>

模板继承允许你构建一个包含站点共同元素的基本“模板骨架”,实例:

<!--layout.art-->
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>{{block 'title'}}My Site{{/block}}</title>

    {{block 'head'}}
    <link rel="stylesheet" href="main.css">
    {{/block}}
</head>
<body>
    {{block 'content'}}{{/block}}
</body>
</html>
<!--index.art-->
{{extend './layout.art'}}

{{block 'title'}}{{title}}{{/block}}

{{block 'head'}}
    <link rel="stylesheet" href="custom.css">
{{/block}}

{{block 'content'}}
<p>This is just an awesome page.</p>
{{/block}}

渲染 index.art 后,将自动应用布局骨架。

3.7 子模板

标准语法

{{include './header.art'}}
{{include './header.art' data}}

原始语法

<% include('./header.art') %>
<% include('./header.art', data) %>

看如下例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>art-template-filter</title>
    <!-- 引入 template-web.js -->
    <script src="./node_modules/art-template/lib/template-web.js"></script>
</head>
<body>
    <div class="container"></div>
    <script type="text/html" id="filterTpl">
        <h3>{{date | format 'YYYy-mM-dd' | addQuotationMarks}}</h3>
        <h3><%=$imports.addQuotationMarks($imports.format(date)) %></h3>
    </script>
    <script>
        var data={
            date: Date.now(),
        }
        // 定义日期格式化过滤器 format 方法:
        template.defaults.imports.format=function (date, format) {
            if (!format || format.toLowerCase()==='yyyy-mm-dd') {
                var dt=new Date(date);
                var y=dt.getFullYear();
                var m=(dt.getMonth() + 1).toString().padStart(2, '0');
                var d=dt.getDate().toString().padStart(2, '0');
                return `${y}/${m}/${d}`;
            } else {
                return 'invalid date';
            }
        }
        // 定义给字符串加引号过滤器 addQuotationMarks 方法:
        template.defaults.imports.addQuotationMarks=function (str) {
            return `"${str}"`;
        }
        // 调用 template 方法,渲染模板和数据
        var html=template('filterTpl', data);
        document.querySelector('.container').innerHTML=html;
    </script>
</body>
</html>

注意:

  • {{date | format 'YYYy-mM-dd' | addQuotationMarks}}
    date 默认为 format 过滤器(方法)的第一个参数, 'YYYy-mM-dd' 才是format 过滤器的第二个参数,date 经过 format 过滤器过滤后,得到的结果,又作为
    addQuotationMarks 过滤器的默认参数,如果有更多的过滤器,那么就把前一层过滤器过滤的结果,作为下一个过滤器的参数一层层过滤下去

4 调试

template.defaults.debug art-template 内建调试器,能够捕获到语法与运行错误,并且支持自定义的语法。在 NodeJS 中调试模式会根据环境变量自动开启:process.env.NODE_ENV !=='production' 设置 template.defaults.debug=true 后,等同于:

{
    "cache": false,
    "minimize": false,
    "compileDebug": true
}

5 模板变量

template.defaults.imports 模板通过 $imports 可以访问到模板外部的全局变量和导入的变量。

5.1 导入变量

template.defaults.imports.log=console.log;
<% $imports.log('Hello, template.defaults.imports.log') %>

看下面例子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>template.defaults.imports</title>
    <script src="./node_modules/art-template/lib/template-web.js"></script>
</head>
<body>
    <div class="container">
    </div>
    <script type="text/html" id="importsTpl">
        <% $imports.log('Hello, template.defaults.imports.log') %>
        <%=$imports.date %>
    </script>
    <script>
        var data={};
        template.defaults.imports.log=console.log;
        template.defaults.imports.date=new Date();
        template.defaults.debug=true;
        var html=template('importsTpl', data);
        document.querySelector('.container').innerHTML=html;
    </script>
</body>
</html>

注意:

这些语法必须写在模板中,在模板中才会起作用;

5.2 内置变量清单

  • $data 传入模板的数据;
  • $imports 外部导入的变量以及全局变量;
  • print 字符串输出函数;
  • include 子模板载入函数
  • extend 模板继承模板导入函数
  • block 模板块生命函数

最后,小编想说:我是一名python开发工程师,

整理了一套最新的python系统学习教程,

想要这些资料的可以关注私信小编“01”即可(免费分享哦)希望能对你有所帮助