• Django 模板系统


    作为一个 Web 框架,Django 需要一个动态生成 HTML 的便捷方法。最常用的方法就是模板。模板包含所需 HTML 输出的静态部分以及描述动态内容被插入的一些特殊语法。

    Django 项目可以配置一个或多个模板,甚至没有,Django 内置了一套自己的模板系统,叫作 Django template language。

    通过模板,我们可以将后端(变量、对象、数据库数据、时间等)渲染到前端 HTML。

    模板中常用两种特殊符号

    • {{ 变量名 }}
    • {% 逻辑相关 %}

    1. 模板变量

    当模板引擎遇到一个变量时,将计算这个变量,用原有的值替换这个变量。变量命名可以是 字母、数字以及下划线组合,但是不能有空格或标点符号。

    语法:

    {{ 变量名 }}
    

    模板变量中的点【.】

    在模板中点有特殊含义,它可能是:

    • 字典查询
    • 属性或方法查询
    • 数字索引查询

    Tips:
    在 Python 中调用一个方法时,往往都会有小括号,而在模板中是不用带括号的。另外需要注意的是模板中调用方法,不能调用带参数的方法。


    示例

    #views.py
    from django.shortcuts import render
    def index(request):
        data_list = [1, 2, 3]
        dic = {'name': 'rose'}
        tt = datetime.datetime.now()    # 传时间
        return render(request, 'index.html', {'data_list': data_list, 'dic': dic, 'tt': tt})
        # return render(request, 'index.html', locals())	# 用 locals() 一句话即可将所有变量传递
    

    模板中调用:

    <html>
        <body>
            {{ data_list.0}}		<!--调用 data_list 中第一个-->
            {{ dic.name}}
            {{ tt }}
        </body>
    </html>
    

    2. 变量过滤器 Filters

    过滤器可以改变变量的的显示

    语法:

    {{ value|filter_name: 参数}}		# 管道符 | 用来应用过滤器,管道符两侧无空格
    

    注意

    • 一个过滤器的输出结果可以作为另一个的输入
    • 过滤器可以接受参数
    • 过滤器参数包含空格的话,必须用引号包裹

    2.1 Django 内置过滤器

    default

    若一个变量为 False 或空,可以使用 default 给其指定默认值

    {{ value|default: 32}}		# 若变量 value 为 False 或空,默认为 32
    

    length

    返回变量的长度,变量必须为 list 或 str

    # value = [1, 2, 3]
    {{ value|length }}       # 值为 3
    

    filesizeformat

    格式化为人为可读的文件尺寸,如:KB、MB、以及 bytes 等

    # value = 123456789
    {{ value|filesizeformat }}  # 117.7MB
    

    slice

    切片操作

    {{ value|slice:"2:-1" }}
    

    date

    格式化日期时间

    {{ value|date:"Y-m-d H:i:s" }}
    
    

    safe

    Django 模板系统中会自动对 HTML 标签以及 JS 语法进行转义,保护系统安全。但有时我们并不希望它被转义,而是保留原有的意思,那么就可以使用 safe 告诉 Django 这个是安全的。

    <!-- value = "<span>你好</span> -->
    {{ value|safe }}    
    
    <!-- 也可以用 autoescape 标签 -->
    {% autoescape off %}   
        {{ a }}
    {% endautoescape %}    
    
    

    truncatewords

    在指定数量后截断字符串

    <!-- value = "hello" -->
    {{ value|truncatewords:3}}
    
    

    cut

    移除变量中与参数相同的字符

    # value = "hello world"
    {{ value|cut:" "}}     # helloworld
    
    

    join

    字符串连接列表

    # value = 'he'
    {{ v|join:"[1, 2]" }}   # h[1, 2]e 
    
    

    timesince

    将日期格式设为自该日期起的时间

    # create_time 必须是一个日期时间格式,不能是字符串
    # create_time = 2019.04.04 
    # comment_time = 2019.04.08
    {{ create_time|timesince:comment_time }}    # 4 天
    
    

    timeuntil

    测量变量到给定日期或时间的时间间隔

    # create_time = 2019.04.04 01:00
    # comment_time = 2019.04.04 09:00
    {{ create_time|timesince:comment_time }}  # 间隔 8 h
    
    

    其他过滤

    • urlencode:编码中文
    • first:第一个
    • upper:变为大写,对应的还有 lower
    • add:增加
    • addslashes 给变量中的引号前加上斜线
    • capfirst 首字母大写

    更多过滤:https://docs.djangoproject.com/zh-hans/2.1/ref/templates/builtins/#ref-templates-builtins-filters

    2.2 自定义 filter 和 simple_tag

    虽然 Django 给我们提供了丰富的内置过滤器,但有时并不能完全满足需求,这就需要我们自定义过滤器了,自定义过滤器分为以下几个步骤:

    • 在 app 中创建一个名为 templatetags 的模块
    • 在模块中创建一个 .py 文件,如:my_tags.py
    • 在 my_tags.py 中自定义过滤器
    • 在 HTML 中调用 {% load my_tags %}
    1. 在 app 中创建一个 templatetags 模块,该模块中包含一个 my_tags.py 文件:

    1. 自定义过滤器,my_tags.py:
    from django import template
    from django.utils.safestring import mark_safe
    
    # 固定格式
    register = template.Library()
    
    # 自定义一个乘法过滤器
    @register.filter
    def multi(value, arg):
        return value*arg
    
    @register.filter(name="he")
    def add_he(value):
        return "{} SB".format(value)
    
    # simple_tag,name 可以在模板中当做过滤器使用
    @register.simple_tag(name='sm')
    def simple_multi(value, a, b, c):
        return value*a+b-c
    
    
    1. 视图函数 appviews.py:
    def test(request):
        num = 28        # 变量 num,使用 locals() 将变量传给模板
        retutn render(request, 'test.html', locals())
    
    
    1. 模板中使用过滤器 test.html:
    {% load my_tags %}   <!--导入 my_tags -->
    
    {{ num | multi:2}}   <!--28 * 2-->
    
    <!-- 28 * 2 + 3 -4 -->
    {% sm num 2 3 4 %}     
    
    {{ d.name|he }}
    
    

    总结

    • 自定义 filter 有参数限制,最多两个,其中一个为变量本身。
    • simple_tag 标签,没有参数限制,但不能放在 if、for 语句中。
    • 自定义 filter、simple_tag 标签时,可以给它们定义一个 name,在模板中可以替代它们使用。

    3 标签 Tags

    标签 Tags 在模板中有特殊含义,如:for 循环、if 条件语句等。

    一个完整的 Tags 包括开始和结束,必须以 {% endXX %} 结束该标签。

    3.1 for 循环

    语法:

    # 这是一个 for 循环语法格式
    {% for data in data_list %}
        {{ data.username }}
    {% endfor %}
    
    

    for 循环常用参数

    变量 描述 变量 描述
    forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始)
    forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始)
    forloop.first 当前循环是不是第一次循环(布尔值) forloop.last 当前循环是不是最后一次循环(布尔值)
    forloop.parentloop 本层循环的外层循环
    <!-- data_list = [{'name': 'rose'}]  -->
    <!-- empty 下的内容将不会显示 -->
    
    {% for data in data_list %}
        {{ data.name }}
    {% empty %}
        <li>ksksksksk </li>
    {% endfor %}
    
    

    3.2 if 条件语句

    if 条件语句包含 elif、else,同样地也支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断等

    {% if data_list %}
        {{ data_list|length }}
    {% else %}
        {{ data_list|cut:" " }}
    {% endif %}
    
    

    3.3 with

    当变量名比较长的时候,可以用来简写变量名。

    <!-- saadfva = 'dddddddddddddddddaaaaaa' -->
    <!-- 给 saadfva 去别名 s -->
    {% with saadfva as s %}
        {{ s }}     
    {% endwith %}
    
    

    3.4 csrf_token

    生成 csrf_token 标签,用于防止跨站攻击验证,常用于 form 表单提交时,解决 403(forbidden)问题。

    <form>
        {% csrf_token %}
    </form>
    
    

    3.5 {% url 'name' %}

    引用路由配置地址,或叫反向查找视图函数,常用于 form 表单中 action 属性值。

    <!--index 为视图函数名-->
    <form action="{% url 'index'%}">
    </form>
    
    

    3.6 verbatim

    禁止渲染,当想渲染的内容就是 {{ hello }} 时,可以禁止 render。

    {% verbatim %}
        {{ hello }}
    {% endverbatim %}
    
    

    3.7 static

    在页面中加载静态文件

    {% load static %}
    <script scr={% static 'js/jquery.3.3.1.js' %}
    
    

    4. 模板继承

    在使用 Django 开发中,往往会写很多重复的 HTML 内容,这时我们可以写一个 母版,再通过模板继承的方式来继承该母版,以减少重复代码。

    下面是一个母版,我们在母版中定义了 title、header、content、jsblock 块,子页面在继承时,可以通过 block 名来替换相应的母版中的内容。

    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}{% endblock %}</title>
        {% block css %}{% endblock %}
    </head>
    <body>
    <div class="page-header"></div>
    <div class="page-body">
        <!--主要内容区-->
        <div class="contents">
            {% block content %}{% endblock %}
        </div>
    </div>
    {% block js %} {% endblock %}
    
    </body>
    </html>
    
    

    4.1 extend 继承

    使用 extend 标签可以用来继承 母版

    母版 base.html

    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}{% endblock %}</title>
        {% block css %}{% endblock %}
    </head>
    <body>
    <div class="page-header"></div>
    <div class="page-body">
        <!--主要内容区-->
        <div class="contents">
            {% block content %}
                <h1>你好</h1>
            {% endblock %}
        </div>
    </div>
    {% block js %} {% endblock %}
    
    </body>
    </html>
    
    

    子页面 index.html

    <!-- 在子模板中继承基础模板,base.html 为基础模板 -->
    {% extends 'base.html' %}
    
    {% block content %}
        {{ block.super }}
    {% endblock %}
    
    

    要想在子页面中使用母版中 block 包裹的内容,需要使用 {{ block.super }} 将其加载过来

    4.2 include 组件

    可以将常用的页面内容,如:导航条、页脚等组件单独保存在独立的文件中,再通过 include 标签将其加载过来即可。

    子页面 index.html

    <!-- 在子模板中继承基础模板,base.html 为基础模板 -->
    {% extends 'base.html' %}
    
    {% include 'navbar.html' %}
    
    

    4.3 inclusion_tag

    多用于返回 HTML 片段。

    如:开发一个博客系统,左侧栏的标签、分类、日期归档,在博客文章主页和文章详情页是一样的。为了减少代码重复性,且能提高页面的拓展性。我们就用 inclusion_tag 来实现左侧栏。

    1. 在项目中新建一个名为 templatetags 的包
    2. templatetags 包中定义一个 my_tags.py 文件,编辑如下:
    from django import template
    from blog import models
    from django.db.models import Count
    
    register = template.Library()
    
    # 意思是将返回值返回给模板页 left_menu.html
    @register.inclusion_tag('includes/left_menu.html')
    def get_left_menu(username):
        """
        侧边栏:分类、标签、日期归档公共部分
        :param user: 用户对象
        :return:
        """
        user = models.UserInfo.objects.filter(username=username).first()
        blog = user.blog
    
        # 使用 annotate 分组查询所有文章分类
        category_list = models.Category.objects.filter(blog=blog).annotate(c=Count('article')).values('name', 'c')
    
        # 使用 annotate 分组查询所有文章标签
        tag_list = models.Tags.objects.filter(blog=blog).annotate(c=Count('article')).values('name', 'c')
    
        # 日期归档,格式为:2019-03
        archive_list = models.Article.objects.filter(user=user).extra(
            select={'archive_time': 'date_format(publish_time, "%%Y-%%m")'}
        ).values('archive_time').annotate(c=Count('pk')).values('archive_time', 'c')
    
        return {
            "username": username,
            'category_list': category_list,
            'tag_list': tag_list,
            'archive_list': archive_list
        }
    
    

    上面我们从数据库中取出用户的博客文章分类、标签、日期归档列表,并将其传递给 left_menu.html

    1. 创建一个模板页 left_menu.html
    <!--博客文章分类-->
    <div class="panel panel-success">
        <div class="panel-heading">文章分类</div>
        <div class="panel-body">
            {% for category in category_list %}
                <p><a href="/blog/{{ username }}/category/{{ category.name }}">{{ category.name }}({{ category.c }})</a></p>
            {% endfor %}
        </div>
    </div>
    
    <!--博客文章标签-->
    <div class="panel panel-warning">
        <div class="panel-heading">文章标签</div>
        <div class="panel-body">
            {% for tag in tag_list %}
                <p><a href="/blog/{{ username }}/tag/{{ tag.name }}">{{ tag.name }}({{ tag.c }})</a></p>
            {% endfor %}
        </div>
    </div>
    
    <!--博客文章日期归档-->
    <div class="panel panel-info">
        <div class="panel-heading">日期归档</div>
        <div class="panel-body">
            {% for archive in archive_list %}
                <p><a href="/blog/{{ username }}/archive/{{ archive.archive_time }}">{{ archive.archive_time }}({{ archive.c }})</a></p>
            {% endfor %}
        </div>
    </div>
    
    
    1. 在需要增加左侧栏的页面添加 inclusion_tag 即可
    def article_detail(request):
        username = 'rose'
        return render(request, 'article_detail.html', {'username': username})
    
    
    <!-- article_detail.html -->
    
    {% load my_tags %}
    
    {% get_left_menu username %}
    
    

    Tips: username 需要传递







  • 相关阅读:
    <script>标签的加载解析执行
    百度地图API位置偏移的校准算法
    开源实时消息推送系统 MPush
    开源GIS软件 4
    Bootstrap 只读输入框
    javascript中的后退和刷新
    HTML中的文本框textarea标签
    Spring Boot 特性 —— SpringApplication
    SpringMVC使用POST方法传递数据,却出现Request method 'GET' not supported?
    springboot的登录拦截机制
  • 原文地址:https://www.cnblogs.com/midworld/p/11380497.html
Copyright © 2020-2023  润新知