Django框架—视图函数和模板系统

一、django视图函数

1.什么是视图函数

一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。

响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片;无论视图本身包含什么逻辑,都需要返回响应。

一般大家约定俗成的放在django项目或应用程序app中名为views.py文件中。

视图函数实例

from django.http import HttpResponse
import datetime
​
def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

当浏览器向服务端请求一个页面时,Django封装了一个HttpRequest请求对象,该对象包含关于请求的元数据。然后,Django加载相应的视图,将这个HttpRequest请求对象作为第一个参数传递给视图函数,供视图函数使用相关的请求数据。

每个视图负责根据不同的访问请求,执行不同的业务逻辑,最后返回一个HttpResponse响应对象,通过模板渲染后发送给前端页面。

2.HttpRequest对象

当一个页面被请求时,Django就会创建一个包含本次请求元信息(请求报文中的请求行、首部信息、内容主体等)的HttpRequest请求对象。

Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象。

官方文档

httprequest对象的常用属性和方法

  • path_info:返回用户访问url,不包括域,如:/home/

  • method:请求中使用的HTTP方法的字符串表示,全大写表示。e如:POST

  • GET:包含所有HTTP GET参数的类字典对象,可以通过键取值

  • POST:包含所有HTTP POST参数的类字典对象,可以通过键取值

  • body:请求体,byte类型 request.POST的数据就是从body里面提取到的

其他属性(了解)

1.HttpRequest.META:包含所有HTTP请求头信息的python字典。
2.HttpRequest.COOKIES:一个标准的Python 字典,包含所有的cookie。键和值都为字符串。
3.HttpRequest.FILES:一个类似于字典的对象,包含所有的上传文件信息。
  # FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。
4.HttpRequest.user:一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。
5.HttpRequest.session:一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django,启用会话的支持时才可用。

其他方法

1.HttpRequest.get_full_path()  # 返回带查询字符串或get请求数据的路径path,这里的请求数据就是(query_params),如:/home/?name=ryxiong

3.HttpResponse对象

HttpRequest对象是由django自动创建封装的,而HttpResponse对象是我们需要处理返回的,每一个视图函数都需要实例化,填充和返回一个HttpResponse对象

HttpResponse类位于django.http模块中。

HttpResponse使用

HttpResponse对象可以传递字符串给前端页面

from django.http import HttpResponse
def index():
    response = HttpResponse("Here's the text of the Web page.")
    response = HttpResponse("Text only, please.", content_type="text/plain")

HttpResponse对象属性

  • HttpResponse.content:响应内容

  • HttpResponse.charset:响应内容的编码

  • HttpResponse.status_code:响应的状态码

HttpResponse响应对象的三种形式

在视图函数中需要先导入使用的类,才能使用

from django.http import HttpResponse,render,redirect,JsonResponse  # JsonResponse是HttpResponse的子集

1.HttpResponse方法

语法:HttpResponse("字符串文本")

直接跟一个具体的字符串作为响应体,不多概述。

2.render方法

语法:render(request,"模板",替换参数);

将模板文件根据一个给定的上下文字典,渲染后(字符串替换)返回给前端页面。

参数介绍

  • request: 用于生成响应的请求对象

  • template_name:要使用的模板的完整名称,可选的参数

  • context:添加到模板上下文的一个字典。默认是一个空字典

  • content_type:生成的文档要使用的MIME类型

  • status:响应的状态码。默认为200

  • useing: 用于加载模板的模板引擎的名称

扩展:render_to_response("模板",替换参数)效果一直,不需要传递request对象。推荐使用render方法。

from django.shortcuts import render
def my_view(request):
    # 视图的代码写在这里
    return render(request, 'myapp/index.html', {'foo': 'bar'})

3.redirect方法

语法:redirect(路径)跳转页面

给浏览器返回一个3xx状态码,告诉浏览器重新请求一个指定的页面。

参数可以是如下:

  • 一个模型:将调用模型的get_absolute_url() 函数

  • 一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称

  • 一个绝对的或相对的URL,将原封不动的作为重定向的位置。

def my_view(request):
    具体逻辑代码
    return redirect('/url/')

render和redirect的区别

使用render返回页面,如果页面需要模板语言渲染,那么所有要渲染的这部分数据,除了要写在开始的输入函数中,还需要写在后面提交的函数中,代码重复,没有解耦。

而且使用render,url连接并没有发生变化,还是停在原url上。

JsonResponse对象

JsonResponse是HttpResponse的子类,用来生成JSON编码格式的响应数据。

from django.http import JsonResponse

response = JsonResponse({'name': 'ryxiong'})  # 处理成JSON编码格式的bytes数据
print(response.content)  # b'{"name": "ryxiong"}'

JsonResponse类详细参数:class JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dumps_params=None,**kwargs)

和父类HttpResponse的区别:

  1. 默认Content-Type为:application/json
  2. data应该是一个字典类型
  3. safe:为False时,data可以为任意可转换为Json格式的对象,如list,dict等;为True时,data只能为字典类型,否则会报错。
  4. json_dumps_params参数是一个字典,它将调用json.dumps()方法并将字典中的参数传入给该方法。

JsonResponse返回实例

#views.py
return HttpResponse(json.dumps({"msg":"ok!"}))  # 方式一
return JsonResponse({"msg":"ok!"})  # 方式二

#index.html
var data=json.parse(data)  # 前端拿到需要通过JSON.parse来解析
console.log(data.msg);

4.CBV和FBV模式

FBV模式

FBV(function base views)在视图文件中使用函数来处理请求返回响应数据。

CBV模式

CBV(class base views)在视图文件中使用类来处理请求,返回响应数据。

Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。

使用视图函数的好处

  1. 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)

  2. 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性

CBV模式使用

使用class base view实现返回响应的视图类,在类中可以重写请求方法,来响应不同请求。

视图函数定义视图类示例

# views.py
from django.http import HttpResponse
from django.views import View
  
class MyView(View):
    
    #重写dispatch方法的写法
    def dispatch(self,request,*args,**kwargs)
        # 因为响应对象是通过父类的dispatch调用不同的方法获得响应对象返回,所以重写dispatch方法时要执行父类的dispatch方法,并拿到返回值返回
        ret = super().dispatch(request,*args,**kwargs)
        return ret  #别忘了return值
    
    # 重写get方法,处理get请求
    def get(self, request):
        return HttpResponse('OK')
views.py文件

Django的url是将一个请求分配给可调用的函数的,而不是一个class。所以在url配置中,class-based view提供了一个as_view()静态方法,调用这个方法,会实例化一个对象,对象调用dispatch()方法,dispatch方法根据request的method的不同来调用不同的方法来处理request请求,最终统一将HttpResponse响应对象交由dispatch方法来返回给客户端。

# urls.py
from django.conf.urls import url
from myapp.views import MyView #引入我们在views.py里面创建的类
  
urlpatterns = [
     url(r'^index/$', MyView.as_view()),
]
urls.py文件

5.视图函数装饰器

给视图函数添加装饰器

FBV本身就是通过函数实现,所以给视图函数添加装饰器和普通装饰器一样。

# 定义装饰器函数
def wrapper(func):
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = func(*args, **kwargs)
        end_time = time.time()
        print("used:", end_time-start_time)
        return ret
    return inner
​
# FBV版添加班级
@wrapper
def add_class(request):
    if request.method == "POST":
        class_name = request.POST.get("class_name")
        models.Classes.objects.create(name=class_name)
        return redirect("/class_list/")
    return render(request, "add_class.html")

给视图类添加装饰器

类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。

在Django中,Django提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。

视图类装饰器分两种形式

1.给所有方法添加装饰器

因为所有方法都是通过dispatch方法来调用,所以给所有函数添加装饰器,可以通过为dispatch添加装饰器实现。

from django.views import View
from django.utils.decorators import method_decorator
class Myview(view):
    @method_decorator(wrapper)
    def dispatch(self,request,*args,**kwargs):
        ret = super().dispatch(request,*args,**kwargs)  # 执行父类dispatch方法
        return ret  # 返回响应对象

2.给某一个方法添加装饰器

  • 直接给类中重写的具体方法添加装饰器

from django.views import View
from django.utils.decorators import method_decorator
class Myview(view):   
    
    @method_decorator(wrapper)  # 单独装饰get方法
    def get(self,request): #根据用户的请求方法,找到对应的方法
        return render(request,'login.html')
  • 在类上方指定具体方法添加装饰器

from django.views import View
from django.utils.decorators import method_decorator
​
@method_decorator(wrapper,name="get")  # 单独执行get方法装饰,name固定写法
class Myview(view):   
    def get(self,request): #根据用户的请求方法,找到对应的方法
        return render(request,'login.html')

二、django模板系统

1.模板系统介绍

如果我们需要在html页面中放入可变化的数据,那么就需要将html页面嵌入在python代码中。

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    # 在python代码嵌入了html代码,耦合高,不利于开发
    return HttpResponse(html)

要知道将html页面嵌入到视图函数不是一个好主意。原因如下:

  1. 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改,牵一发而动全身;

  2. Python 代码编写和 HTML 设计是两项不同的工作,前端开发和后端开发无法并行;

所以我们需要将HTML页面的设计和后端逻辑设计分离,会更简洁、容易维护开发我们的WEB应用。

而Django的模板系统(Template System)能够帮助我们实现这种解耦合。

2.模板系统语法

Django自带的模板语言就是HTML代码+逻辑控制代码

注意:模板不是html页面,而是html代码和特殊标记共同组成的内容。

Django模板语言的语法主要分为2种: {{变量}} 和 {% Tag %}

  • {{ 变量 }} 主要用于和视图变量做替换

  • {% tag %} 主要用于做逻辑判断和实现某些功能

  • {# 注释内容 #}用于模板文件中的注释

正因有了数据+逻辑才构成了模板语言;

3.Template和Context对象

模板语法的实现本质上是通过template和context对象来完成的。

>>> python manange.py shell  (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')  # 根据html页面生成一个template对象
>>> c = Context({'name': 'Stephane'})  # 根据字典内容生成一个替换文本context对象。
>>> t.render(c)  # 通过调用模板对象的render方法,渲染模板,完成字符串替换
'My name is Stephane.'

在视图函数中的render方法,也是基于template对象和context对象实现的。

高效渲染模板的例子

# Low
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')  # 多次实例化模板对象
    print t.render(Context({'name': name}))
​
# Good
t = Template('Hello, {{ name }}')  # 只实例化一次模板对象
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))

4.模板系统使用

变量的使用

在Django的模板语言中按此语法使用:{{ 变量名 }}

当模版引擎遇到一个变量,它将计算这个变量,然后用结果替换掉它本身。变量的命名包括任何字母数字以及下划线 ("_")的组合。 变量名称中不能有空格或标点符号。

深度变量查询—万能句点号

在 Django 模板中遍历复杂数据结构的关键是句点字符 (.),可以用来遍历复杂的数据结构。

  • 字典查询(Dictionary lookup)

  • 属性或方法查询(Attribute or method lookup)

  • 数字索引查询(Numeric index lookup)

注意:

  1. 如果计算结果的值是可调用的,它将被无参数的调用。 调用的结果将成为模版的值。

  2. 如果使用的变量不存在, 模版系统将插入 string_if_invalid 选项的值, 它被默认设置为'' (空字符串) 。

实例

# views.py
def index(request):
    import datetime
    s = "hello"
    l = [111, 222, 333]  # 列表
    dic = {"name": "yuan", "age": 18}  # 字典
    date = datetime.date(1993, 5, 2)  # 日期对象
class Person(object):
        def __init__(self, name):
            self.name = name
        def dream(self):
            return 'dreamer'
    person1 = Person("chao")  # 自定义类对象
    person2 = Person("alex")  
    person_list = [person1,person2]
    
    return render(request, "index.html", {"l": l, "dic": dic, "date": date, "person": person1, "person_list":person_list})
    # return render(reqeust,"index.html",locals())
    # locals()获取函数内容所有的变量,然后通过render方法给了index.html文件进行模板渲染,如果你图省事,你可以用它,但是很多多余的变量也被传进去了,效率低.

模板中支持的语法

<h4>{{s}}</h4>
<h4>列表:{{ l.0 }}</h4>  <!--获取列表索引为0的元素-->
<h4>列表:{{ l.2 }}</h4>
<h4>字典:{{ dic.name }}</h4>  <!--获取字典键位name对应的值-->
<h4>日期:{{ date.year }}</h4>  <!--获取时间对象的年份year-->
<h4>对象属性name:{{ person.name }}</h4><!--取列表的第1个对象的name属性的值-->
<h4>类对象列表:{{ person_list.0.name }}</h4>
<!--取列表的第1个对象的dream方法的返回值,如果没有返回值,拿到的是none-->
<h4>类对象列表:{{ person_list.0.dream }}</h4>

注意:调用对象里面的方法的时候,不需要写括号来执行,并且只能执行不需要传参数的方法,如果你的这个方法需要传参数,那么模板语言不支持,不能帮你渲染

5.过滤器使用

在Django的模板语言中,通过使用 过滤器来改变变量的显示。

过滤器的语法: {{ value|filter_name:参数 }};使用管道符应用过滤器

注意事项

  1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。

  2. 过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。

  3. 过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}

  4. '|'左右没有空格!没有空格!没有空格!(重要的事说三遍)

常用过滤器

Django提供的大约60种内置过滤器,这里列举常用过滤器,其余可以查看官方文档了解

  • default:如果变量是false或者空,使用指定默认值,否则使用变量的值。

{{ value|default:"nothing"}}  # value没有传值,显示nothing
  • length:返回值的长度

{{ value|length }}  # 返回value的长度,value='asd',显示3
  • filesizefomat:格式化文件尺寸,使用户可读

{{ value|filesizeformat }}  # value是123456789,输出将会是117.7MB。
  • slice:切片,序列类型数据的切片

{{value|slice:"2:-1"}}  # value为"hello",输出是"ll"
  • date:格式化时间输出

{{ value|date:"Y-m-d H:i:s"}}  # value是一个时间对象。
# value = datetime.datetime.now(),将会显示为:年-月-日 时-分-秒
  • safe:安全显示内容

Django的模板中在进行模板渲染的时候会对HTML标签和JS等语法标签进行自动转义,显示成字符串,原因是为了安全,防止恶意提交html标签和js代码从而影响浏览器页面的渲染。xss攻击就是通过这种方式实现的。

如果我们不想让自己定义的标签或js语言被转义,可以使用safe过滤器告诉浏览器这段代码是安全的的,无需转义。

# value = "<a href='#'>点我</a>" 和 value="<script>alert('123')</script>"
{{ value|safe}}  # 使用safe,模板语法会将value替换成标签类型和js类型代码
  • truncatewords:在一定数量的字后截断字符串,是截多少个单词。

  • truncatechars:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。

{{ value|truncatechars:9}}  # 注意:最后那三个省略号也是9个字符里面的,也就是这个9截断出来的是6个字符+3个省略号,有人会说,怎么展开啊,配合前端的点击事件就行啦
# value="hello girl hi baby yue ma"
{{ value|truncatewords:3}}  # 上面例子得到的结果是 'hello girl h1...'
  • cut:移除value中所有的与给出的变量相同的字符串

{{ value|cut:' ' }}  # 取出给定value中的所有空格,无法指定替换个数
  • join:使用字符串连接列表
{{ list|join:', ' }}  # 就像Python的str.join(list)

6.标签Tags

标签的语法结构:{% tag %}

标签一般用于模板文件中处理变量和文本的逻辑业务,如判断和循环;

标签的使用需要开始标签,且大部分的标签使用需要有结束标签。

for标签

遍历可迭代类型的每一个元素,{% for ... %} ... {% endfor %},使用tab可以快速建立for标签

<!--遍历列表-->
<ul>
    {% for name in name_list %}
        <li>{{ name }}</li>  <!--遍历name_list,为每一个name创建一个p标签-->
    {% endfor %}  <!--结束for标签-->
</ul>

for ... empty使用

for 标签带有一个可选的{% empty %} 从句,在给出的组是空的或者没有被找到时,可以有所操作。

<!--遍历字段,获取计数-->
<ol>
    {% for key,value in name_dic.items %}
        <li>{{ forloop.counter }} {{ key }} : {{ value }}</li><!--    empty用于判断name_dic是否为空    -->
    {% empty %}
        <h1>sorry,no data found</h1>
    {% endfor %}
</ol>

forloop的使用

循环序号可以通过{{forloop}}显示,必须在循环内部用,forloop的其他使用方法;

  • forloop.counter:当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能

  • forloop.counter0:当前循环的索引值(从0开始)

  • forloop.revcounter :当前循环的倒序索引值(从1开始)

  • forloop.revcounter0:当前循环的倒序索引值(从0开始)

  • forloop.first:当前循环是不是第一次循环(布尔值)

  • forloop.last:当前循环是不是最后一次循环(布尔值)

  • forloop.parentloop:本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等

<ul>
    {% for book in book_list %}
        <li>{{ forloop.counter0 }} {{ book }}</li>  <!-- counter默认从1开始,指定0,从0开始 -->
    {% endfor %}
</ul>

if标签

{% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。也可以使用elif和else对条件进行条件控制。

<!--条件判断if标签-->
{% if name_dic.age > 25 %}
    <p>中年男人了</p>
{% elif gender == "male" %}
    <p>是个爷们</p>
{% else %}
    <p>还是小鲜肉</p>
{% endif %}

if标签与过滤器结合使用

{% if user_list|length > 5 %}  <!--结合过滤器来使用-->
    <p>七座豪华SUV</p>
{% else %}
    <p>黄包车</p>
{% endif %}

if语句支持and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。

with标签

使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的

注意

  • 等号两侧不需要加空格

  • 别名只能在with语句中使用

<!-- 第一种写法 -->
{% with age=name_dic.age %}  {# 第一种写法 #}
    <p>年龄是{{ age }}</p>
{% endwith %}
​
<!-- 第二种写法 -->
{% with name_dic.age as age %}  {# 第二种写法 #}
    <p>年龄是{{ age }}</p>
{% endwith %}

csrf_token标签

我们以post方式提交表单的时候,如果没有注销settings设置中的csrf中间件,会报错Forbiden,这是因为在Django中为了保证网页提交数据的安全性,而设置了一个csrf认证,也就是settings里面的csrf.middleware配置。

解决这个问题就需要用到csrf标签,csrf标签用于跨站请求伪造保护。

csrf工作原理

在页面的form表单里面(注意是在form表单里面)任何位置写上{% csrf_token %},这个东西模板渲染的时候替换成了<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">,并且是隐藏的,这个标签的值是个随机字符串,提交的时候,这个东西也被提交了,csrf标签是我们后端渲染的时候给页面加上的。

如果客户端是通过服务端返回的页面提交数据,就会带着这些信息,是合法的,服务器就响应请求;

如果客户端是通过其他方式向服务器提交数据,那么就不会携带这些数据,这时候服务器就不会响应请求,抛出异常。

<form action="/login/" method="post">
    {% csrf_token %}  <!-- 在form表单中的任意位置写上,就可以为form表单加上csrf的数据,从而通过csrf认证 -->
    username <input type="text" name="username">
    password <input type="password" name="password">
    <input type="submit">
</form>

与csrf相关的两个装饰器

使用装饰器前必须导入一个类

from django.views.decorators.csrf import csrf_exempt,csrf_protect
  • @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置csrfToken全局中间件。

from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect  # 给函数强制进行scrf认证,无论是否配置csrf认证
def login(request):
    if request.method =="GET":
        return render(request,"login.html")
    else:
        username = request.POST.get("username")
        password = request.POST.get("password")
        if username == "alex" and password == "alex":
            return HttpResponse("登陆成功")
        else:
            return HttpResponse("帐号密码错误")
csrf_protect使用
  • @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

from django.views.decorators.csrf import csrf_exempt,csrf_protect
​
@csrf_exempt  # 给函数强制增加豁免csrf认证,无论是否配置csrf认证
def login(request):
    if request.method =="GET":
        return render(request,"login.html")
    else:
        username = request.POST.get("username")
        password = request.POST.get("password")
        if username == "alex" and password == "alex":
            return HttpResponse("登陆成功")
        else:
            return HttpResponse("帐号密码错误")
csrf_exempt使用

几个注意事项

  • Django的模板语言不支持连续判断,即不支持以下写法

{% if a > b > c %}
...
{% endif %}
  • Django的模板语言中属性的优先级大于方法(了解)

def func(request):
    d = {"a": 1, "b": 2, "c": 3, "items": "100"}
    return render(request, "xx.html", {"data": d})

如果我们在render渲染一个页面时,字典中有一个键的名字和字典的items方法一直,此时在模板语言中使用

{{ data.items }}  # 这里获取的是字典中键为items对应的值,而不是字典的键值对items().

7.自定义过滤器和标签

为什么自定义过滤器和标签

在项目开发过程中,可能Django自带的过滤器和标签无法满足使用需求时,我们可以根据自己具体业务需求自定义过滤器和标签。

自定义过滤器、标签和组件的前提配置

  1. 在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.

  2. 在app中创建templatetags文件夹(文件夹名只能是templatetags)

  3. 创建任意 .py 文件,如:my_tags.py

自定义过滤器、标签

django中template类提供了一个方法,生成一个注册对象,使用这个对象的方法可以将一个函数装饰成过滤器或者标签。

  • 使用filter方法将一个函数装饰成过滤器

  • 使用simple_tag将一个函数装饰为标签

  • 使用inclusion_tag接受字典参数渲染一个html文件为组件,供调用这个标签的html文件使用

注意:

  • 自定义过滤器至多接受两个参数

  • 自定义标签接受任意参数,无限制

from django import template  # 导入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  # 装饰自定义标签
def simple_tag_multi(v1,v2,v3):
    return v1*v2*v3
​
# mark_safe使用
@register.simple_tag
def safe_input(id,arg):
    result = "<input type='text' id='%s' class='%s'>"%(id,arg)
    return mark_safe(result)  # 使用mark_safe函数接受标签字符串可以识别成标签,直接返回会在html页面中直接生成字符串文本。
​
@register.inclusion_tag("custom_element.html")  # 自定义组件标签
def showdata(name,age):
    data = {'name':name,"age":age}
    return data  # 这里返回字典,内部调用render方法,将custom_element.html页面渲染,并将custom_element.html文件当成组件供其他文件使用
如何自定义过滤器/标签

inclusion_tag用法

inclusion_tag装饰器接受一个html文件参数,根据函数返回的数据渲染html文件(和render一样),渲染完后成为一个自定义组件,可以供调用这个标签的html文件使用它。

比如接受custom_element.html文件做参数

<!--custom_element.html文件-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>姓名是:{{ name }}</p>
<p>年龄是:{{ age }}</p>
</body>
</html>
custom_element.html文件 

组件标签使用方法在下面代码中

自定义过滤器和标签的使用

直接使用{% showdata "alex" 25 %} 调用自定义标签,渲染页面,当成组件在这里使用。

{% load mytag %}  <!--这里加载自定义标签的文件-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
​
{#过滤器使用#}
<p>{{ mum|filter_multi:12 }}</p>
<p>{{ num|filter_multi:"[22,333,444]" }}</p>
​
{#自定义标签使用#}
<p>{% simple_tag_multi num 2 2  %}</p>
{#自定义标签安全输入#}
<p>{% safe_input "user" "user" %}</p>
​
{#自定义组件标签使用#}
{% showdata "alex" 25 %}
​
</body>
</html>
如何使用自定义过滤器和标签 

8.模板继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。

模板继承通过创建一个基本的“骨架”模板,模板包含站点页面的所有元素,并且可以定义可能变动元素为blocks,以便引用模板时根据自己的需求做响应的定制。

创建父级模板

为什么使用模板继承

使用模板的基础条件是有大量的html页面结构相似,只有某些数据需要更换时,我们可以根据相似点提取出模板文件,再将需要更换数据的地方设置blocks块,从而是代码最大程度的复用。

创建模板文件

创建一个html,使用{% block 块名 %} ... {% endblock %} 来创建一个块区域,以供自定义定制。

<!DOCTYPE html>
<html lang="en">
<head>
</head><body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
        {% endblock %}
    </div><div id="content">
        {% block content %} 这里是主页的内容... {% endblock %}
    </div>
</body>
</html>
创建模板文件home.html 

继承模板

在继承模板的html页面中写入,使用 {% extends "模板文件名" %} 告诉模版引擎,这个模版“继承”了另一个模版。

{% extends "home.html" %}  <!--这里继承模板文件-->
​
{% block content %}  <!--这里可以对blocks处的内容个性化定制,注意对哪个block定制就需要引入哪个block的名字。-->
    这里是blog的内容...
{% endblock %}

使用继承后,使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单。

使用模板继承的注意点

  • 在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。

  • 在base模版中设置越多的 {% block %} 标签越好。利于个性化定制,钩子多一点总比少一点好

  • 如果你发现在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。

  • 为了更好的可读性,可以给{% endblock "块名" %} 加上这个块的名字。

{% block content %}
    ...
{% endblock content %} 
  • 一个模板中不能定义多个名字相同的block标签

  • 在子模板的块中使用block.super可以引用到父类模板的block内容

如果你既想定制自己的内容,又想使用父类某个block中的内容,可以使用{{ block.super }}标签获取父类block中的内容。

{% extends "home.html" %}  <!--这里继承模板文件-->
​
{% block content %}  
    {{ block.super }}  <!--这样写可以直接拿到父模板中block为content的内容使用。-->
    这里是blog的内容...
{% endblock %}

9.组件

在html页面中有一些标签内容经常使用,比如页面的导航条和页尾信息,这个时候,可以将他们提取出来放在一个单独的html文件中,在需要使用导航栏的时候,直接引用。

引用语法

{% include 'navbar.html' %}  <!--navbar是我们提取出的导航条文件-->

组件使用

创建一个组件文件如nav.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--给导航栏设置样式-->
    <style>
        .c1{
            background-color: gray;
            height: 40px;
        }
    </style>
</head>
<body><!--创建一个导航栏标签-->
<div class="c1">
    <div>
        <a href="">xx</a>
        <a href="">dd</a>
    </div>
</div>
</body>
</html>
创建组件html文件

创建好的导航栏组件,可以在其他html页面中直接使用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% include 'nav.html' %}  <!--使用组件直接生成导航栏-->
<h1>这是另一个页面</h1>
</body>
</html>
使用组件文件

组件和插件区别

  • 组件:范围最广大,在开发中被复用的部分都可以成为组件。

  • 插件:程序中已经预留接口的组件就是插件,比如某一个js代码效果,弹窗。

10.静态文件相关

js、css、img等都叫做静态文件,在Django对于静态文件的发送,需要做相应的配置。

  1. 在项目文件夹下创建一个用来存放静态文件的文件夹,名字任意,比如staticfiles,注意不要与别名一致。

  2. 在项目文件夹下的settings配置如下内容:

STATIC_URL = '/static/'  # 这里是为你静态文件的路径取得别名,注意在前端页面使用时,使用该路径。
​
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'staticfiles'),  # 注意逗号,第二个参数是应用中存放静态文件的文件夹名称
]

取别名的目的

  • 给静态文件夹去别名也是一种安全机制,浏览器通过调试台能够看到的是别名的名字,无法知道你静态文件夹的真实名字,可以避免别人通过这个路径进行攻击。

  • 通过使用别名,即时你静态文件夹的名称发生改变,也不需要在页面中逐一修改路径。

静态文件引用

在前端页面中引入静态文件,如果我们使用静态文件别名在拼接某个静态文件的名字,如下

<link rel="stylesheet" href="static/css/index.css">

如果某一天我们的静态文件别名也发生了改变,那么是不是所有使用了静态文件别名的路径都需要手动修改呢?同样的问题,我们不希望路径写死。

这个时候我们可使用Django提供的静态文件引用写法:{% load static %}

{% load static %}  <!-- 写在文件开头,注意这里的static不是静态文件别名,只是django中的引用写法,通过这句代码,可以找到settings中静态文件夹的别名,这个例子中就是'static' --><link rel="stylesheet" href="{% static 'css/index.css' %}">  <!--引用路径的写法-->

另一种写法

{% get_static_prefix %}

{% load static %}  # 写在文件开头
{% get_static_prefix as STATIC_PREFIX %}  <!--找到静态文件夹别名,赋值给一个变量STATIC_PREFIX--><img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" />  <!--使用变量STATIC_PREFIX来拼接路径-->

 

posted @ 2019-05-20 18:27  ryxiong728  阅读(2486)  评论(0编辑  收藏  举报