自动转义HTML

当从模版中生成HTML文件时,总会存在各种风险 ,比如xss代码注入等恶意攻击。比如下面的模版片段:

Hello, {{ name }}

首先,它看起来像是无害的,用来显示用户的名字 ,但是设想一下,如果用户像下面这样输入他的名字,会发生什么:

<script>alert('hello')</script>

使用这个名字的值 ,模版将会被渲染成这样:

Hello, <script>alert('hello')</script>

这意味着浏览器会弹出一个JavaScript警报框!

显然,用户提交的数据都被不应该被盲目的信任,并且被直接插入到网页中 ,因为一个怀有恶意的用户可能会使用这样的漏洞来做一些坏事 。 这种类型的安全问题被叫做跨站脚本攻击(Cross Site Scripting)(XSS)。

为避免这个问题,有两个选择:

  • 第一,对每个不被信任的值运行escape过滤器 ,这将把潜在的有害的HTML字符转换成无害的字符串。在Django最初的几年里 ,这是默认的解决方案,但问题是它将责任放在开发人员/模板作者身上,以确保转义了所有内容 ,而且很容易忘记转义数据 。
  • 第二,利用Django的自动HTML转义功能。默认情况下,Django中的每个模板会自动转义每个变量。也就是说 ,下面五个字符将被转义:
    • <会转换为<
    • >会转换为>
    • '(单引号)转换为'
    • "(双引号)会转换为"
    • &会转换为&

建议:将第二种功能做为默认打开的设置 。

但是,有时,模板变量含有一些你打算渲染成原始HTML的数据 ,你并不想转义这些内容。 例如,你可能会在数据库中储存一些HTML代码,并且直接在模板中嵌入它们。有以下解决方法:

对于单个变量

使用safe过滤器来关闭变量上的自动转义:

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

对于模板块:

要控制模板上的自动转义 ,将模板(或者模板中的特定区域)包裹在autoescape标签中,像这样:

{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}

autoescape标签接受on或者off作为它的参数 。下面是一个模板的示例:

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

自动转义标签autoescape还会作用于扩展(extend)了当前模板的模板,以及通过include标签包含的模板 ,就像所有block标签那样。 看下面的例子:

# base.html文件

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}

# child.html文件

{% extends "base.html" %}
{% block title %}This &amp; that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

由于自动转义标签在base模板中关闭 ,它也会在child模板中关闭,导致当greeting变量含有Hello!字符串时,会渲染HTML。

过滤器的字符串参数:

过滤器的参数可以是字符串:

{{ data|default:"This is a string literal." }}

要注意 ,所有这种字符串参数在插入模板时都不会进行任何自动转义 。模板的作者可以控制字符串字面值的内容,可以确保在模板编写时文本经过正确转义 。即对自己传递的参数心里要有数。

{{ data|default:"3 &lt; 2" }}  # 正确的做法

{{ data|default:"3 < 2" }}     # 错误的做法

方法调用

大多数对象上的方法调用同样可用于模板中。这意味着模板能够访问到的不仅仅是对象的属性(比如字段名称)和视图中传入的变量,还可以执行对象的方法 。例如:QuerySets提供了count()方法来计算含有对象的总数。因此 ,你可以像这样获取所有关于当前任务的评论总数:

{{ task.comment_set.all.count }}

还可以访问已经显式定义在模型上的方法:

# models.py
class Task(models.Model):
    def foo(self):
        return "bar"
template.html
{{ task.foo }}

由于Django有意限制了模板语言中的处理逻辑,不能够在模板中传递参数来调用方法。数据应该在视图中处理,然后传递给模板用于展示 。

多对多调用

对于如下的模型:

from django.db import models

# Create your models here.

class Student(models.Model):
    name = models.CharField(max_length=128)

class Course(models.Model):
    name = models.CharField(max_length=128)
    students = models.ManyToManyField('Student')

模型Course有一个多对多字段指向Student模型。

正向查询

假设编写了一个如下的视图:

def test(request):
    course = models.Course.objects.get(pk=1)        #pk:有主键的字段
    return render(request, 'course.html', locals())

获取了id为1的course对象 ,并将它传递给course.html模版,模版代码如下:

{% for student in course.students.all %}

<p>{{ student.name }}</p>

{% endfor %}

首先通过course.students.all,查寻到course对象关联的students对象集 ,然后用for标签循环它,获取每个student对象,再用student模型的定义 ,访问其各个字段的属性。

反向查询

对于反向查询 ,从student往course查,假设有如下的视图:

def test2(request):
    student = models.Student.objects.get(pk=1)
    return render(request, 'student.html', locals())

获取了id为1的student对象,并将它传递给student.html模版 ,模版代码如下:

{% for course in  student.course_set.all %}
{{ course.name }}
{% endfor %}

通过student.course_set.all,反向获取到student实例对应的所有course对象,然后再for标签循环每个course ,调用course的各种字段属性 。

对于外键ForeignKey,其用法基本类似。只不过正向是obj.fk,且只有1个对像 ,不是集合。反向则是obj.fk_set,类似多对多 。

定义标签和过滤器

Django为我们提供了自定义的机制,可以通过使用Python代码 ,自定义标签和过滤器来扩展模板引擎,然后使用{% load %}标签。

前置步骤

Django对于自定义标签和过滤器是有前置要求的,首先一条就是代码布局和文件组织:你可以为你的自定义标签和过滤器新开一个app ,也可以在原有的某个app中添加。

  1. 在app中新建一个templatetags名字固定 ,不能变),和views.py、models.py等文件处于同一级别目录下 。不要忘记创建__init__.py文件以使得该目录可以作为Python的

本文版权归QU快排Www.seoGurubLog.com 所有,如有转发请注明来出,竞价开户托管,seo优化请联系QQ▲61910465