Django路由层、视图层及模板层

Django路由层

URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;

你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对应执行。

简单的路由配置

from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'index/', views.index),
    re_path(r'^$', views.index),  # r'^$'表示无论匹配到什么都会跳转到这个网址 比如www.baidu.com 设置根路径
]

注意:

  • 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号,在视图函数时,会将对应的参数传入。
  • 不需要添加一个前导的反斜杠,因为每个URL 都有。
  • 每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义
  • url不包含正则时直接用path,含正则时可用re_path
'''
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/([0-9]{4})/$', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
/articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数views.month_archive(request, '2005', '03')。
/articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
/articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。请像这样自由插入一些特殊的情况来探测匹配的次序。
/articles/2003 不匹配任何一个模式,因为每个模式要求URL 以一个反斜线结尾。
/articles/2003/03/03/ 将匹配最后一个模式。Django 将调用函数views.article_detail(request, '2003', '03', '03')。
   
    '''

有名分组

上面的示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。

在Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name 是组的名称,pattern 是要匹配的模式。

from django.urls import path,re_path

from app01 import views

urlpatterns = [
    re_path(r'^articles/2003/$', views.special_case_2003),
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

 '''
    /articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')。
    /articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。

    '''

在实际应用中,这意味你的URLconf 会更加明晰且不容易产生参数顺序问题的错误 —— 你可以在你的视图函数定义中重新安排参数的顺序。当然,这些好处是以简洁为代价;

分发

在一个项目中可能存在多个应用,为了方便区分和管理,在项目的总路由urls.py中会进行路由分发:

(1)项目总路由导入from  django.conf.urls  import   url,include


from django.conf.urls import include, url


 


urlpatterns = [


   url(r'^admin/', admin.site.urls),


   url(r'^app02/', include('app02.urls')), 


# 可以包含其他的URLconfs文件,注意不需要写.py后缀


   url(r'^app01/',include('app01.urls')),


 #别忘了要去app01这个应用下创建一个urls.py的文件,现在的意思是凡是以app01开头的路径请求,都让它去找app01下的urls文件中去找对应的视图函数,还要注意一点,此时这个文件里面的那个app01路径不能用$结尾,因为如果写了$,就没办法比配上app01/后面的路径了


]


 


(2)子应用中创建自己的分路由urls.py,进行URLConf配置


App01:


from django.conf.urls import  url


from app01 import views


urlpatters=[


url(r'^index/', views.index),


]


 


App02:


from django.conf.urls import  url


from app02 import views


urlpatters=[


url(r'^index/', views.index),


]

反向解析

在使用Django 项目时,一个常见的需求是获得URL 的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。

人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。

即需求就是反向解析出当前视图的URL。

在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

  • 在模板中:使用url 模板标签。
  • 在Python 代码中:使用from django.urls import reverse()函数 
# urls.py
from django.conf.urls import url

from . import views

urlpatterns = [
    #...
    re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='news-year-archive'),
    #...
]


# xxxx.html
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>

<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>


# views.py
from django.urls import reverse
from django.http import HttpResponseRedirect

def redirect_to_year(request):
    # ...
    year = 2006
    # ...
    return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))   # 同redirect("/path/")

名称空间

命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。

这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。

由于name没有作用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回
我们在开发项目时,会经常使用name属性反解出URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间。
project的urls.py:

urlpatterns = [
    re_path(r'^admin/', admin.site.urls),
    re_path(r'^app01/', include("app01.urls",namespace="app01")),
    re_path(r'^app02/', include("app02.urls",namespace="app02")),
]

app01.urls:
urlpatterns = [
    re_path(r'^index/', index,name="index"),
]

app02.urls:
urlpatterns = [
    re_path(r'^index/', index,name="index"),
]


app01.views 
from django.core.urlresolvers import reverse

def index(request):
    return  HttpResponse(reverse("app01:index"))


app02.views
from django.core.urlresolvers import reverse

def index(request):
    return  HttpResponse(reverse("app02:index"))

django2.0中的path

思考情况如下:

urlpatterns = [ 
re_path('articles/(?P<year>[0-9]{4})/', year_archive), 
re_path('article/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view), 
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view), 
re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view), 
]

考虑下这样的两个问题:

第一个问题,函数 year_archive 中year参数是字符串类型的,因此需要先转化为整数类型的变量值,当然year=int(year) 不会有诸如如TypeError或者ValueError的异常。那么有没有一种方法,在url中,使得这一转化步骤可以由Django自动完成?

第二个问题,三个路由中article_id都是同样的正则表达式,但是你需要写三遍,当之后article_id规则改变后,需要同时修改三处代码,那么有没有一种方法,只需修改一处即可?

在Django2.0中,可以使用 path 解决以上的两个问题。

示例:

from django.urls import path  
from . import views  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<int:year>/', views.year_archive),  
    path('articles/<int:year>/<int:month>/', views.month_archive),  
    path('articles/<int:year>/<int:month>/<slug>/', views.article_detail),  
]  

基本规则:

  • 使用尖括号(<>)从url中捕获值。
  • 捕获值中可以包含一个转化器类型(converter type),比如使用 <int:name> 捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符。
  • 无需添加前导斜杠。

以下是根据 2.0官方文档 而整理的示例分析表:

Django默认支持以下5个转化器:

  • str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  • int,匹配正整数,包含0。
  • slug,匹配字母、数字以及横杠、下划线组成的字符串。
  • uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
  • path,匹配任何非空字符串,包含了路径分隔符

自定义转换器

对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:

  • regex 类属性,字符串类型
  • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
  • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

例子:

class FourDigitYearConverter:  
    regex = '[0-9]{4}'  
    def to_python(self, value):  
        return int(value)  
    def to_url(self, value):  
        return '%04d' % value  

使用register_converter 将其注册到URL配置中:

复制代码
from django.urls import register_converter, path  
from . import converters, views  
register_converter(converters.FourDigitYearConverter, 'yyyy')  
urlpatterns = [  
    path('articles/2003/', views.special_case_2003),  
    path('articles/<yyyy:year>/', views.year_archive),  
    ...  
]  

Django视图层

 视图函数

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

响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。

无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。

示例:

from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
import datetime

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

让我们逐行阅读上面的代码:

  • 首先,我们从 django.shortcuts模块导入了HttpResponse类,以及Python的datetime库。

  • 接着,我们定义了current_datetime函数。它就是视图函数。每个视图函数都使用HttpRequest对象作为第一个参数,并且通常称之为request

    注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。

  • 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse对象。

视图层,熟练掌握两个对象即可:请求对象(request)和响应对象(HttpResponse)

HttpResponse对象

django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。 除了特殊说明的之外,其他均为只读的。

/*

1.HttpRequest.GET

  一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。

2.HttpRequest.POST

  一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。

  POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。
   因此,不应该使用 if request.POST  来检查使用的是否是POST 方法;应该使用 if request.method == "POST"
  另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。
   
   注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用:
        request.POST.getlist("hobby")

3.HttpRequest.body

  一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。
  但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST 。


4.HttpRequest.path

  一个字符串,表示请求的路径组件(不含域名)。
  例如:"/music/bands/the_beatles/"

5.HttpRequest.method

  一个字符串,表示请求使用的HTTP 方法。必须使用大写。
  例如:"GET""POST"


6.HttpRequest.encoding

  一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8')。
   这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。
   接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
   如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。


7.HttpRequest.META

   一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:

    CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
    CONTENT_TYPE —— 请求的正文的MIME 类型。
    HTTP_ACCEPT —— 响应可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
    HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
    HTTP_HOST —— 客服端发送的HTTP Host 头部。
    HTTP_REFERER —— Referring 页面。
    HTTP_USER_AGENT —— 客户端的user-agent 字符串。
    QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
    REMOTE_ADDR —— 客户端的IP 地址。
    REMOTE_HOST —— 客户端的主机名。
    REMOTE_USER —— 服务器认证后的用户。
    REQUEST_METHOD —— 一个字符串,例如"GET""POST"。
    SERVER_NAME —— 服务器的主机名。
    SERVER_PORT —— 服务器的端口(是一个字符串)。
   从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时,
    都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_  前缀。
    所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。

8.HttpRequest.FILES

  一个类似于字典的对象,包含所有的上传文件信息。
   FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
  注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会
   包含数据。否则,FILES 将为一个空的类似于字典的对象。


9.HttpRequest.COOKIES

  一个标准的Python 字典,包含所有的cookie。键和值都为字符串。



10.HttpRequest.session

   一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
    完整的细节参见会话的文档。


11.HttpRequest.user(用户认证组件下使用)

  一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。

  如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。

    例如:

    if request.user.is_authenticated():
        # Do something for logged-in users.
    else:
        # Do something for anonymous users.


       user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。

     -------------------------------------------------------------------------------------

    匿名用户
    class models.AnonymousUser

    django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:

    id 永远为None。
    username 永远为空字符串。
    get_username() 永远返回空字符串。
    is_staff 和 is_superuser 永远为False。
    is_active 永远为 False。
    groups 和 user_permissions 永远为空。
    is_anonymous() 返回True 而不是False。
    is_authenticated() 返回False 而不是True。
    set_password()、check_password()、save() 和delete() 引发 NotImplementedError。
    New in Django 1.8:
    新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。

*/
View Code

响应对象

响应对象主要有三种形式:

  • HttpResponse()
  • render()
  • redirect()

1.HttpResponse()括号内直接跟一个具体的字符串作为响应体,比较直接很简单,所以这里主要介绍后面两种形式。

2.render()

render(request, template_name[, context])
 
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
参数:
     request: 用于生成响应的请求对象。

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

     context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。

3.redirect() 重定向

传递要重定向的一个硬编码的URL

def my_view(request):
    ...
    return redirect('/some/url/')


也可以是一个完整的URL:
def my_view(request):
    ...
    return redirect('http://example.com/') 

过程中发送两次请求

1)301和302的区别。

  301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取
  (用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。

  他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;

  302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301

 

2)重定向原因:
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
        这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的
    网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。

request常用属性及方法

1.属性介绍
(1) request.scheme

请求的协议种类(http/https)

(2) request.path

请求页面的完整路径(get_full_path()在path的基础上还有查询条件)

(3) request.method

请求使用的http方法(POST,GET,DELETE,PUT...)

(4) request.content_type

请求的MIME类型 

(5) request.GET/request.POST

get/post请求中的所有参数,类似于字典的对象

(6) request.COOKIES

请求包含的cookie字典

(7) request.FILES

上载文件的类字典对象

(8) request.headers(Django2.2新功能)

获取请求headers里面的内容,不区分大小写,类似字典的对象

request.headers["Cotent-Length"]  请求报文中实体主体的字节大小

request.headers["Content-Type"]  请求的MIME类型 

request.headers["User-Agent"]  请求的用户代理

(9) request.META

包含所有HTTP头部信息的字典

CONTENT_LENGTH –请求正文的长度(以字符串形式) 
CONTENT_TYPE –请求正文的MIME类型
HTTP_ACCEPT –响应可接受的内容类型
HTTP_ACCEPT_ENCODING –响应的可接受编码
HTTP_ACCEPT_LANGUAGE –可接受的响应语言。
HTTP_HOST –客户端发送的HTTP Host标头
HTTP_USER_AGENT –客户端的用户代理字符串。
REMOTE_ADDR –客户端的IP地址。
REMOTE_HOST –客户端的主机名
SERVER_NAME –服务器的主机名
SERVER_PORT –服务器的端口(以字符串形式)
SERVER_PROTOCOL –http协议版本(HTTP/1.1)
(10) request.user

当前登录用户的实例

(11) request.session

代表当前会话的可读可写,类似于字典的对象(删除用户登录的session,request.session.delete(session_key) )
2.方法介绍
(1) request.get_host()

获取主机域名或者IP加端口号



(2) request.get_port()

请求端口号



(3) request.is_secure()

如果使用的是Https,则返回True,表示连接是安全的



 (4) request.get_full_path()

带有请求参数的完整path

Django模板层

对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。

设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。

基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。
我们可以使用 Django的 模板系统 (Template System)来实现这种模式。

模板语法之变量 

views.py

from django.shortcuts import render, HttpResponse

# Create your views here.
"""
模板语法有两种
    渲染变量{{ }}
    渲染标签{% %}
"""
def index(request):
    a = 'alex'
    b = 1
    c = ['mjj', 'egon', 'wupeiqi']
    d = {'name': 'yuan', 'age': 22}
    num = 100
    i = 10

    class Person_list:
        def __init__(self, name, age):
            self.name = name
            self.age = age

    alex = Person_list('alex', 232)
    egon = Person_list('egon', 223)
    person_list = [alex, egon]

    return render(request, 'index.html', locals())  # 默认传入的键值名和变量名相等传入


def login(request):
    if request.method =='POST':
        return HttpResponse('OK')
    return render(request, 'login.html')

def order(request):
    return render(request, 'order.html')

对于各种数据类型,都可以通过render()函数渲染到指定的模板上,render的第三个参数可以以{}的形式传入,键为模板中变量的名字,值为当前视图函数中变量的名字,

为了使用方便一般键和值名称相同。

如果视图函数传入的变量过多,可直接用locals(),会将视图函数中所有变量的变量名作为键,变量的值作为键值传入模板。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h4>index</h4>
    <p>{{ a }}</p>
    <p>{{ b }}</p>
    <p>{{ c.1 }}</p>
    <p>{{ d.age }}</p>
    <p>{{ alex }}</p>
    <p>{{ egon }}</p>
{#访问列表元素 字典的值 对象的属性 统一用'.' 比如 c.1 d.name #}

</body>
</html>

模板之过滤器

语法:
{{obj|filter__name:param}}

default
如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:
{{ value|default:"nothing" }}


length
返回值的长度。它对字符串和列表都起作用。例如:
{{ value|length }}
如果 value 是 ['a', 'b', 'c', 'd'],那么输出是 4。


filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。例如:
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB。  


date
如果 value=datetime.datetime.now()
{{ value|date:"Y-m-d" }}  


slice
如果 value="hello world"
{{ value|slice:"2:-1" }}


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


safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,
这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,
比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,
如果自动转义的话显示的就是保护HTML标签的源文件。
为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。比如:

value="<a href="">点击</a>"

{{ value|safe}}
这里简单介绍一些常用的模板的过滤器

模板之标签

标签看起来像是这样的: {%tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。

一些标签需要开始和结束标签 (例如{%tag %} ...标签 内容 ... {% endtag %})。

for标签

遍历每一个元素:

{% for person in person_list %}
    <p>{{ person.name }}</p>
{% endfor %}
可以利用{% for obj in list reversed %}反向完成循环。

遍历一个字典:

{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

注:循环序号可以通过{{forloop}}显示  

forloop.counter           The current iteration of the loop (1-indexed)
forloop.counter0       The current iteration of the loop (0-indexed)
forloop.revcounter      The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0       The number of iterations from the end of the loop (0-indexed)
forloop.first                    True if this is the first time through the loop
forloop.last          True if this is the last time through the loop

for ... empty

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

{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

if标签

{% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

复制代码
{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

with标签

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

例如:

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

csrf_token

这个标签用于跨站请求伪造保护

在render返回页面前,会先解析模板语法,服务器不能直接发送post请求.

加上这句,在发送给服务器时就有添加了一个键值对,相当于是一个身份信息,这样服务器就允许客户端发送post请求。

自定义标签和过滤器

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

2、在app中创建templatetags模块(模块名只能是templatetags)

3、创建任意 .py 文件,如: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
<br>
@register.simple_tag
def simple_tag_multi(v1,v2):
    return  v1 * v2
<br>
@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)

4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py

{% load my_tags %}

5、使用simple_tag和filter(如何调用)

-------------------------------.html
{% load xxx %}  
      
# 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 %}

注意:自定义过滤器可以用在if、for等语句后,自定义标签不可以

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

模板继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。

通过从下面这个例子开始,可以容易的理解模版继承:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My amazing site{%/span> endblock %}</title>
</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>

这个模版,我们把它叫作 base.html, 它定义了一个可以用于两列排版页面的简单HTML骨架。“子模版”的工作是用它们的内容填充空的blocks。

在这个例子中, block 标签定义了三个可以被子模版内容填充的block。 block 告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。

子模版可能看起来是这样的:

{% extends "base.html" %}
 
{% block title %}My amazing blog{% endblock %}
 
{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

extends 标签是这里的关键。它告诉模版引擎,这个模版“继承”了另一个模版。当模版系统处理这个模版时,首先,它将定位父模版——在此例中,就是“base.html”。

那时,模版引擎将注意到 base.html 中的三个 block 标签,并用子模版中的内容来替换这些block。根据 blog_entries 的值,输出可能看起来是这样的:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>My amazing blog</title>
</head>
 
<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>
 
    <div id="content">
        <h2>Entry one</h2>
        <p>This is my first entry.</p>
 
        <h2>Entry two</h2>
        <p>This is my second entry.</p>
    </div>
</body>
</html>

请注意,子模版并没有定义 sidebar block,所以系统使用了父模版中的值。父模版的 {% block %} 标签中的内容总是被用作备选内容(fallback)。

这种方式使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。

这里是使用继承的一些提示:

  • 如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。

  • 在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。

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

  • {{block.super}}获取父模板block的内容,如果你想在父模板添加内容而不是完全覆盖,它将变得非常有用。 

  • 为了更好的可读性,你也可以给你的 {% endblock %} 标签一个名字 。例如:

  • {% block content %}
    ...
    {% endblock content %} 在大型模版中,这个方法帮你清楚的看到哪一个% block %} 标签被关闭了。
  • 不能在一个模版中定义多个相同名字的 block 标签。

 博客源自:https://www.cnblogs.com/yuanchenqi/articles/8876892.html

猜你喜欢

转载自www.cnblogs.com/sxy-blog/p/12983637.html