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:添加到模板上下文的一个字典。默认是一个空字典
-
-
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的区别:
- 默认Content-Type为:application/json
- data应该是一个字典类型
- safe:为False时,data可以为任意可转换为Json格式的对象,如list,dict等;为True时,data只能为字典类型,否则会报错。
- 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。
使用视图函数的好处
-
提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
-
可以用不同的函数针对不同的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')
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()), ]
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页面嵌入到视图函数不是一个好主意。原因如下:
-
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改,牵一发而动全身;
-
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)
注意:
-
如果计算结果的值是可调用的,它将被无参数的调用。 调用的结果将成为模版的值。
-
如果使用的变量不存在, 模版系统将插入 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:参数 }};使用管道符应用过滤器
注意事项
-
过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
-
过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。
-
过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}
-
'|'左右没有空格!没有空格!没有空格!(重要的事说三遍)
常用过滤器
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_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("帐号密码错误")
几个注意事项
-
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自带的过滤器和标签无法满足使用需求时,我们可以根据自己具体业务需求自定义过滤器和标签。
自定义过滤器、标签和组件的前提配置
-
在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
-
在app中创建templatetags文件夹(文件夹名只能是templatetags)
-
创建任意 .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>
组件标签使用方法在下面代码中
自定义过滤器和标签的使用
直接使用{% 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>
继承模板
在继承模板的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页面中直接使用。
<!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对于静态文件的发送,需要做相应的配置。
-
在项目文件夹下创建一个用来存放静态文件的文件夹,名字任意,比如staticfiles,注意不要与别名一致。
-
在项目文件夹下的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来拼接路径-->