# 模块层之标签、自定义标签

# 自带的标签

  • 标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。一些标签需要开始和结束标签 (例如{% tag %} ...标签 内容 ... {% endtag %})。

  • for 遍列每个元素:(可以是字典、列表、类对象(通过.属性查询))

  • 遍历一个对象列表/列表嵌套的字典 (不支持break)

    #index视图函数
    def index(request):
          person_list = [{'name':'赵丽颖'},{'name':'宋茜'},{'name':'霍思燕'}]
          return render(request, "index.html", {"person_list": person_list})
    
    ------------------------------------------------
    #修改index.html
    {% for person in person_list %}
    	<p>{{ person.name }}</p>
    {% endfor %}
    
    
  • # 遍历一个列表
    #修改index视图函数
      def index(request):
          li = [123, 456, 567]
          return render(request, "index.html", {"li": li}) 
    #render必须传字典参数   
    ------------------------------------    
    
    #修改index.html,修改body部分
      #还可以用反序 {% for obj in list reversed %}
      <p>
          {% for item in li %}
          <span>{{ item }}</span>  //标签
          {% endfor %}
      </p>
    
  • 遍历一个字典

//修改index.html,(在视图函数中,只要传入一个字典就可以了,名字必须为dic) {% for key,val in dic.items %}

:

{% endfor %} ```

  • # 总结
    for循环,循环一个列表
    <ul>
        {% for name in name_list reversed %}
            <li>{{ forloop.counter }}  {{ name }}</li>
        {% endfor %}
    </ul>
    循环一个字典
    <ol>
        {% for key,value in name_dict.items %}
            <li> {{ forloop.counter0 }} {{ key }}  ---  {{ value }} </li>
        {% endfor %}
    
    </ol>
    不支持break等操作
    if判断 结合过滤器的写法
        {% if num|xxx:20 > 2000 %}
        <h2>大于2千</h2>
        {% else %}
            <h2>小于等于2千</h2>
        {% endif %}
    
  • 注意:Django的模板语言中属性的优先级大于方法(了解)

    def xx(request):
        d = {"a": 1, "b": 2, "c": 3, "items": "100"}
        return render(request, "xx.html", {"data": d})
        
    #如上,我们在使用render方法渲染一个页面的时候,传的字典d有一个key是items并且还有默认的 d.items() 方法,此时在模板语言中:    
    {{ data.items }}   #默认会取d的items key的值。
    
  • 总结Django的标签:

    1、if 标签   # 判断标签
    2、for 标签  # 循环标签
    3、with 标签  # 起别名标签
    4、crsf_token 标签  # 防止跨站请求伪造
    
# 自定义过滤器
  • 举例:添加一个乘法过滤器

    第一步

    1、修改settings.py中的INSTALLED_APPS,最后一行添加当前的app。不然找不到app下面的模块。
    2、在app项目目录下新建一个templatetags目录(目录必须是这个,否则找不到)
    3、在新建的templatetags目录下新建一个my_filter_tag.py(这个可以随便起,最好不要使用'_')
    4、自定义过滤器的第一个参数可以是变量/参数,第二个参数必须是参数,不能是变量
    

    对my_filter_tag.py 进行修改,内容如下

    from django import template
    from django.utils.safestring import mark_safe  # 这个相当于django自带的safe,因为自定义的标签不能使用自带的safe,只能用他
    register=template.Library()   #导入模块注册表
    
    @register.filter  #增加@register.filter,是为了将函数转换成过滤器。函数的名字,可以自定义
    def multi_filter(x,y):  #过滤器的函数默认接受两个参数
        return x*y
    
    @register.filter(name=multi)  # 重命名,以后使用就用它multi
    def multi_filter(x,y):
        return x*y
    
    # 第二步:

    在响应使用自定义过滤器的html文件中,开始就应该导入

    #要过滤的html
    {% load my_filter_tag %}  //其实类似于加载导入,p标签中的内容,是执行了multi_filter过滤器。
    <p>{{ num|multi_filter:5 }}</p>
    
    -------------------------------------------------------------
    #views.py文件
    def index1(request):
        num = 100
        return render(request, 'login.html', {'num': num})
    
    --------------------------------------------------------------
    #url.py文件
    urlpatterns =[
        url(r'^app/index/$',views.index1)
        ]
    

    注意:其实此时 就是在执行multi_filter过滤器,且默认此函数接受两个参数,第一个:num(即要被过滤的对象);第二个:5(即要用于参与处理的参数)。(这里的num、5相对于函数x,y). 重启项目即可

    # 注意:
    如果要完成3位乘法呢?过滤器可以增加一个形参,但是index.html怎么加第3个参数呢?
    
    答:是,它不能加第3个参数。所以只能在后端,将参数形式修改列表,元组,字典等方式,就可以了。
    比如:第二个参数,可以传入字符串,然后进行切割,然后再返回。
    
  • 举例:计算4*5*6

    修改index视图函数

    def index(request):
        num1 = 4
        num2 = 5
        num3 = 6
        num_list = [num1,num2,num3]
        return render(request,'index.html',{'num_list':num_list})
    

    修改my_filter_tag.py 中的过滤器

    @register.filter
    def multi_filter(num_list):
        res = 1
        for i in num_list:
            res*=i
        return res
    

    修改index.html, 修改body部分

    {% load my_filter_tag %}
    <p>{{ num_list|multi_filter }}</p>   //默认multi_filter前的num作为第一个参数传入,要是后面有:传入的是第二个参数
    
  • 举例: 通过过滤器返回html标签,按照原来的写法只会返回一个文本(存在的问题:因为防止XSS攻击)

    因为django遇到html或者js标签,会转义。它认为是不安全的!那么如何告诉它,是安全的呢?

    需要在过滤器中导入make_safe模块
    

    修改my_filter_tag.py文件中的link_tag过滤器,完整代码如下:

    #使用make_safe方法,告诉django是安全的,不需要转义
    from django import template
    from django.utils.safestring import mark_safe
    register=template.Library()
    
    @register.filter
    def multi_filter(num_list):
        res = 1
        for i in num_list:
            res*=i
        return res
    
    @register.filter
    def link_tag(href):
        return mark_safe("<a href=%s>click</a>" % href)  #告诉浏览器这个标签安全
    

    修改index.html

    {% load my_filter_tag %}
    <p>{{ link|link_tag }}</p>
    

    重启django项目,因为网页有缓存,懒得清理了

# 自定义标签

  • 标签:是为了做一些功能,把标签当作函数使用

    调用标签,使用的方法:{% 标签过滤器名 参数1,参数2,参数3... %}
    参数不限,但不能放在if for语句中
    

    修改my_filter_tag.py,增加multi_tag函数

    @register.simple_tag   #表示将函数转换为自定义标签
    def multi_tag(x,y,z):
        return x*y*z
    
    @register.simple_tag
    def join_str(*arg,**kwargs):  #要求传入的参数是(位置参数,关键字参数)
        return 'xxx'  
    

    修改index.html,修改body部分

    {% load my_filter_tag %}
    <p>{% multi_tag 7 8 9 %}</p>  //传递参数
    <p>{% join_str 7 8 9 k1=3 k2=4%}</p>  //多种参数
    
  • 总结:

    1、自定义标签和自定义过滤器的区别:
        1. 标签,是为了做一些功能。过滤器,是对斜杠前面的数据做过滤。
        2. 标签可以写任意个形参,而过滤器最大只能写2个形参。如果过滤器需要接收多个参数,需要将参数存放在列表,元组,字典等数据中。
        3. 过滤器可以用在if等语句后,标签不可以
    
    
    #综合案例:
    #自定义模块文件my_tags.py中
    from django import template
    from django.utils.safestring import mark_safe 
    register = template.Library()   #register的名字是固定的,不可改变
     
    @register.filter
    def filter_multi(v1,v2):
        return  v1 * v2
    
    @register.simple_tag  #和自定义filter类似,只不过接收更灵活的参数,没有个数限制。
    def simple_tag_multi(v1,v2):
      return  v1 * v2
    
    @register.simple_tag
    def my_input(id,arg):
        result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
        return mark_safe(result)
    
    # 在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
    {% load my_tags %}
    
    # 使用simple_tag和filter(如何调用)
    {% load xxx %}  
    
        # num=12 假如这里再视图函数中传入了num=12
        {{ num|filter_multi:2 }} # 24
    
        {{ num|filter_multi:"[22,333,4444]" }}
    
        {% simple_tag_multi 2 5 %}  参数不限,但不能放在if for语句中
        {% simple_tag_multi num 5 %}
    
    #注意:filter可以用在if、for等语句后,simple_tag不可以,也就是说自定义不可以使用这些
    {% if num|filter_multi:30 > 100 %}
        {{ num|filter_multi:30 }}
    {% endif %}
    

inclusion_tag: 区别于组件,此方法使某个部件可以动态更换效果,如:分页

  • 多用于返回html代码片段:其实是一个函数标签,可以将加载数据传给装饰器的html文件,然后将渲染后的html文件当作组件返回给加载对象(这个装饰器,register.inslusion_tag会将渲染好的html文件返回给调用者)

    第一步:通过视图函数返回数据给装饰器函数,以便渲染;第二步:通过装饰器函数的html进行渲染;第三步:返回渲染好的html片段,插入到最终的html代码中。(其实就是返回了个组件)

    在templatetags/my_inclusion.py中 (过滤器需要触发的标签)

    from django import template   # 可以写成 from django.template import Library
    register = template.Library() 
    
    @register.inclusion_tag('result.html')  #将result.html里面的内容用下面函数的返回值渲染,然后作为一个组件一样,加载到使用这个函数的html文件里面
    def show_results(n): #参数可以传多个进来
        n = 1 if n < 1 else int(n)
        data = ["第{}项".format(i) for i in range(1, n+1)]
        return {"data": data}    #这里可以穿多个值,和render的感觉是一样的{'data1':data1,'data2':data2....}
    

    第二步:在templates/snippets/result.html (函数提交数据进行渲染的html,其实就是返回了一个组件给调用标签的位置)

    <ul>
      {% for choice in data %}
        <li>{{ choice }}</li>
      {% endfor %}
    </ul>
    

    在templates/index.html中(调用标签的html)

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="x-ua-compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>inclusion_tag test</title>
    </head>
    <body>
    
    {% load my_include %}  // 先加载自定义文件名
    
    {% show_results 10 %}   //调用函数返回组件
    </body>
    </html>
    
  • 总结:

    {% load 函数名 %}   #用于加载视图函数
    {% extend 'xxx.html' %}  #用于加载模块
    {% include 'xxx.html' %}  #用于加载组件文件
    

# 练习:

1、使用过滤器完成加减乘除的操作:

{{a|add:b}}  #加法、减法可以将b传入负数,但是不能-b

#乘法
@register.filter
def multiy(x,y):
    return x*y

{{a|multiy:b}}

#给一个网站和文字,使它能是一个可以跳转的标签
#法一:
@register.filter
def show_a(x,y):
    return '<a href="{}">{}</a>'.format(x,y)
 
{{a|show_a:'百度'|safe}} 

2、面试题:模块中使用{% sqr_list 3 %},生成如下dropdown list控件(下拉菜单)

 # 在 templatetags/my_tag.py文件中
from django import template
register = template.Library()

@register.inclusion_tag('result.html')
def show_a(n):
    dic = {}
    for num in range(n):
        dic.update(dict([(num,num*num),]))
        
    return {'data':dic} 
    
---------------------------------------------
 #result.html文件中
<div class="dropdown">
  <button id="dLabel" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Dropdown trigger
    <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" aria-labelledby="dLabel">
    <li> key&nbsp&nbsp&nbsp&nbsptext</li>
    {% for key,values in data.items %}
        <li> {{ key }}&nbsp&nbsp&nbsp&nbsp&nbsp{{ key }}的平方是{{ values }}</li>
    {% endfor %}
  </ul>
</div>

----------------------------------------------
# views.py文件中
def show_a(request):
    return render(request,'base.html')
    
-----------------------------------------------
#base.html文件中
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
    <link rel="stylesheet" href="{% get_static_prefix %}bootstrap-3.3.7-dist/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    {% load my_tags %}
    {% show_a 10 %}
</div>
<script src="{% get_static_prefix %}js/jquery-3.4.1.js"></script>
<script src="{% get_static_prefix %}bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</body>
</html>