• Django系列(二):Django的路由层,视图层和模板层


    1、Django的路由层

    URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;我们就是以这种方式告诉Django,对于客户端发来的某个URL该调用哪一段逻辑代码对应执行。

    (1)、简单的路由配置

    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        re_path(r'^$', views.index),  # 含有正则匹配根路径
        path('shopping', views.shopping),  # 不含正则使用path
        re_path(r'^active/(d{4})/$', views.active),  # 正则也可以使用分组 ,匹配任意四位数字  如 active/1234/
    ]

    解释:urlpatterns列表中是访问路径和视图函数的映射关系。

    注意:

    • 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
    • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles
    • 每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义。

    (2)有名分组

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

    在python 正则表达式中, 命名正则表达式组的语法是(?P<name>正则表达式), 其中 name 是组的名字 , 正则表达式 是要匹配的内容。

    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    
    urlpatterns = [
        re_path(r'^articles/2003/$', views.special_case_2003),
        re_path(r'^articles/(?P<year>d{4})/$', views.year_archive),
        re_path(r'^articles/(?P<year>d{4})/(?P<month>d{2})/$', views.month_archive),
        re_path(r'^articles/(?P<year>d{4})/(?P<month>d{2})/(?P<day>d{2})/$', views.article_detail),
        path('admin/', admin.site.urls),
    】

    这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。

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

    (3)path转换器

    from django.contrib import admin
    from django.urls import path, re_path
    from blog import views
    
    urlpatterns = [
        path('articles/2018/', views.special_case_2018),
        path('articles/<int:year>/', views.year_archive),
        path('articles/<int:year>/<int:month>/', views.month_archive),
        path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
    ]

    默认情况下,Django内置下面的路径转换器:

    •     str:匹配任何非空字符串,但不含斜杠/,默认使用;
    •     int:匹配0和正整数,返回一个int类型;
    •     slug:可理解为注释、后缀、附属等概念,是url在最后的一部分解释性字符。 该转换器匹配任何ASCII字符以及连接符和下划线;
    •     uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写。 例0863561d3-9527-633c-b9b6-8a032e1565f0。返回一个UUID对象;
    •     path:匹配任何非空字符串,重点是可以包含路径分隔符/。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。

    (4)、分发

    我们发现,Django生成的项目中只有一个全局的urls.py文件,这就意味着我们需要把所有应用的访问路径与视图函数的映射关系都放到这一个urls.py文件中,但应用较多时会发现这样做不太好,于是我们希望每个应用各自的映射关系能够放到自己的应用目录中。这是我们就要用到分发的功能,也就是在每个app里,各自创建一个urls.py路由模块,然后从根路由出发,将app所属的url请求,全部转发到相应的urls.py模块中。

    项目的urls.py文件:

    from django.contrib import admin
    from django.urls import path, re_path, include
    
    urlpatterns = [
        re_path(r'^admin/', admin.site.urls),
        re_path(r'^blog/', include('blog.urls')),
    ]

    blog的urls.py文件:

    from django.urls import re_path
    from blog import views
    
    urlpatterns = [
        re_path(r'^articles/(d{4})/$', views.article_year),
        re_path(r'^articles/2003/$', views.special_year),
        re_path(r'^articles/(d{4})/(d{2})/$', views.article_month),
        re_path(r'^articles/(?P<year>d{4})/(?P<month>d{2})/(?P<day>d{2})/$', views.article_day),
    ]

    (5)、反向解析

    在使用Django 项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL(简而言之,代码中的URL写死就容易出现问题)。

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

                  1)在模板中的url:使用url 模板标签{% url "别名动态变量名 %}。

                  2)在Python 代码中:使用from django.urls import reverse函数。

    urls.py文件

    from django.urls import re_path
    from blog import views
    
    urlpatterns = [
        re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='year-archive'),
        # 给这个路径起了一个别名year-archive, 注意url匹配中有正则,也就是说url中有动态参数,所以url模板标签有要有动态变量接收
    ]

    在模板中(此示例是url含动态变量的情况,不含动态变量则不写动态变量名即可): 

     <a href="{% url 'year-archive' 2012 %}">2012 Archive</a>
        若上面别名为year-archive的url是:http://www.baidu.com/articles/2008/
        则这个a标签链接为:http://www.baidu.com/articles/2008/2012
        <ul>
            {% for yearvar in year_list %}
              <li><a href="{% url 'year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
            {% endfor %}
        </ul>

    在python代码(视图函数中,此示例url中含动态变量,没有动态变量则不写args=()即可):

    from django.urls import reverse
    from django.http import HttpResponseRedirect
    
    
    def redirect_to_year(request):
        year = 2006
        return HttpResponseRedirect(
            reverse('year-archive', args=(year,)))  # 同redirect("/path/"),   args=(year,)接收url中动态变量

    注意:当命名你的URL 模式时,请确保使用的名称不会与其它应用中名称冲突。如果你的URL 模式叫做comment,而另外一个应用中也有一个同样的名称,当你在模板中使用这个名称的时候不能保证将插入哪个URL。在URL 名称中加上一个前缀,比如应用的名称,将减少冲突的可能。我们建议使用myapp-comment 而不是comment

    (6)、命名空间

     命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。

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

      项目下urls.py文件:

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

           app01下的urls.py: 

        urlpatterns = [
            url(r'^index/', index, name="index"),
        ]

           app02下的urls.py:

        urlpatterns = [
            url(r'^index/', index, name="index"),
        ]

           app01下的views.py:

        from django.core.urlresolvers import reverse
        def index(request):
            return  HttpResponse(reverse("app01:index"))

           app02下的views.py: 

        from django.core.urlresolvers import reverse
        def index(request):
            return  HttpResponse(reverse("app02:index"))

     2、Django的视图层

    (1)、视图函数

    一个视图函数,简称视图,是一个简单的Python函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . ,是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。

    下面是一个返回包含当前日期和时间的HTML文档的视图:

    from django.shortcuts import render, HttpResponse
    import datetime
    
    
    def current_datetime(request):
        now = datetime.datetime.now()
        html = "<html><body>It is now %s.</body></html>" % now  # 这里html页面代码有删减,不应该这么写,为了便于理解这样表示
        return HttpResponse(html)

    分析上面的代码:

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

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

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

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

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

    (2)、HttpRequest对象

    a、request属性

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

    ①、HttpRequest.GET
      一个类似于字典的对象,包含 HTTP GET的所有参数。详情请参考 QueryDict 对象。
    ②、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")。
    ③、HttpRequest.body
      一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。
      但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST。
    ④、HttpRequest.path
      一个字符串,表示请求的路径(不含域名和参数),例如:"/music/bands/the_beatles/"。
    ⑤、HttpRequest.method
      一个字符串,表示请求使用的HTTP 方法,比较时必须使用大写,例如:"GET""POST"。
    ⑥、HttpRequest.encoding
      一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用DEFAULT_CHARSET 的设置,默认为 'utf-8')。
      这个属性是可写的,你可以通过修改它来修改访问表单数据使用的编码。
      接下来对属性的任何访问(例如从GET或POST中读取数据)将使用新的 encoding 值。如果你知道表单数据的编码不是DEFAULT_CHARSET,则使用它。
    ⑦、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 键。
    ⑧、HttpRequest.FILES
      一个类似于字典的对象,包含所有的上传文件信息。
      FILES中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
      注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。
    ⑨、HttpRequest.COOKIES
      一个标准的Python 字典,包含所有的cookie。键和值都为字符串。
    ⑩、HttpRequest.session
      一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。完整的细节参见会话的文档。
    ⑪、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 中间件时才可用。

    b、request常用方法

      ①、HttpRequest.get_full_path()
            返回 path,如果可以将加上查询字符串,例如:"/music/bands/the_beatles/?print=true"。
    ②、HttpRequest.is_ajax()
            如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串'XMLHttpRequest'。
          大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。
       如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware,你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应能够正确地缓存。

    (3)、HttpResponse对象

    响应对象主要有三种形式(其实都是从HttpResponse中抽离出来的,即本质是最后一定响应一个HttpResponse的实例对象):

      1)HttpResponse()

      2)render()

      3)redirect()

           ①、HttpResponse()

                  HttpResponse("字符串")括号内直接跟一个具体的字符串作为响应体,比较直接很简单。

           ②、render()

                  语法:render(request, template_name[, context])

                  结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

                  参数说明:

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

                       template_name:要使用的模板的完整名称;

                      context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它(这里也可以写成locals(),代表将所有变量传递给页面);

        render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体(render有两个功能:一个是读取文件字符串,另一个是渲染变量)。

           ③、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,搜索引擎会抓取新的内容而保存旧的网址。SEO(搜索引擎优化)302好于301。

      2)重定向的原因:

        (1)网站调整(如改变网页目录结构);

        (2)网页被移到一个新地址;

        (3)网页扩展名改变(如应用需要把.php改成.Html或.shtml);

                          这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。

      3)用redirect可以解释APPEND_SLASH的用法!

        Django的seetings.py配置文件中默认没有 APPEND_SLASH这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。作用就是自动在url结尾加'/'。

        默认地,任何不匹配或尾部没有斜杠(/)的申请URL,将被重定向至尾部包含斜杠的相同字眼的URL。当seetings.py设置为 APPEND_SLASH = False 时,访问 http://example.com/hello 返回 404。

    3、Django的模板层

    你可能已经注意到上面视图层的例子中返回文本的方式有点特别,也就是说,HTML被直接硬编码在python代码中,如下:

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

    尽管这种方式便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:

      1)对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多;

      2)Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作;

      3)程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作;

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

    Python的模板:HTML代码 + 模板语法      

    def current_time(req):
        # ===============原始的视图函数===============
        # import datetime
        # now = datetime.datetime.now()
        # html = "<html><body>现在时刻:<h1>%s.</h1></body></html>" %now
        # ==========django模板修改的视图函数==========
        # from django.template import Template,Context
        # now = datetime.datetime.now()
        # t = Template('<html><body>现在时刻是:<h1>{{current_date}}</h1></body></html>')
        # t = get_template('current_datetime.html')
        # c = Context({'current_date':str(now)})
        # html = t.render(c)
        # return HttpResponse(html)
        # 另一种写法(推荐)
        import datetime
        now = datetime.datetime.now()
        return render(req, 'current_datetime.html', {'current_date': str(now)[:19]})

    学习模板层就要学习Django的模板语法,一共有两种:

      1){{ var_name }} --- 渲染变量;

      2){%  %} --- 渲染标签;

    (1)、模板语法之变量 - 深度查询

           在 Django 模板中遍历复杂数据结构的关键是句点字符, 语法:{{var_name}},如下示例:

           views.py文件中代码:

    from django.shortcuts import render
    
    
    def index(request):
        import datetime
        s = "hello"  # 字符串
        lst = [111, 222, 333]  # 列表
        dic = {"name": "yuan", "age": 18}  # 字典
        date = datetime.date(1993, 5, 2)  # 日期对象
    
        class Person(object):
            def __init__(self, name):
                self.name = name
    
        person_yuan = Person("yuan")  # 自定义类对象
        person_egon = Person("egon")
        person_alex = Person("alex")
    
        person_list = [person_yuan, person_egon, person_alex]
    
        return render(request, "index.html", {"s": s, "l": lst, "dic": dic, "date": date, "person_list": person_list}) 

    templates中的index.html模板文件代码:

    <h4>{{ s }}</h4>
    <h4>列表:{{ lst.0 }}</h4> 
    <h4>列表:{{ lst.2 }}</h4>
    <h4>字典:{{ dic.name }}</h4>
    <h4>日期:{{ date.year }}</h4>
    <h4>类对象列表:{{ person_list.0.name }}</h4>

    注意:句点符也可以用来引用对象的方法(因为引用时不加括号,所以只能引用无参数方法)。

    (2)、模板语法之变量 - 过滤器

           语法:{{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" }} 则输出类似2018-10-22日期格式

      ⑤、slice

             如果 value="hello world",则{{ value|slice:"2:-1" }},输出"llo worl"

      ⑥、truncatechars

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

      ⑦、truncatewords

                  截取单词,类似truncatechars

           ⑧、safe

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

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

                  {{ value|safe }}

      这里简单介绍一些常用的模板的过滤器,更多详见

    (3)、模板语法之渲染标签

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

    示例一:for循环遍历一个列表

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

     注:可以利用{% for obj in list reversed %}反向实现循环。

    示例二:for循环遍历一个字典 

    {% 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

    示例三:if控制语句标签 

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

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

    示例四:with语句标签

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

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

    示例五:{{ csrf_token }}    -- 这个标签用于跨站请求伪造保护

    上篇中我们提到了当进行post请求时,会发生forbidden错误,那是当浏览器发送post请求时,除了携带用户想提交的数据之外,服务器还要求浏览器携带一个服务器之前发送的标识作为通行证,用来标识该访问是一个正常用户,所以直接发送post请求而不携带那个特定标识时,会被服务器拒绝,所以我们除了可以采取上篇中提到注释掉settings.py文件中的'django.middleware.csrf.CsrfViewMiddleware',还可以在form表单中添加标签{{ csrf_token }},会自动生成一个唯一标识。

    示例六:for … empty语句标签 

    {% for person in person_list %}
      <p>{{ person.name }}</p>
    {% empty %}
      <p>sorry,no person here</p>
    {% endfor %}

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

    (5)、模板继承

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

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

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <link rel="stylesheet" href="style.css" />
        <title>{% block title %}My amazing site{% 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骨架。“子模版”的工作是用它们的内容填充这个模板中的block。在这个例子中,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 %}标签中的内容总是被用作备选内容。这种方式使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。

    下面是使用继承的一些提示:

      1)如果你在模版中使用{% extends %}标签,它必须是这个模版中的第一个标签。否则,模版继承将无法工作。

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

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

      4)If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }} will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.

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

    {% block content %}
    
    {% endblock content %}

     在大型模版中,这个方法帮你清楚的看到哪一个{% block %}标签被关闭了。

      6)不能在一个模版中定义多个相同名字的 block 标签。

    4、其他

    (1)、表单提交的action问题

    form表单中的action不写值的话默认提交到当前页面路径,写相对路径的话当前url的域名 : 端口号和action中的相对路径拼接就是表单提交的url。

  • 相关阅读:
    selenium+allure测试报告添加测试截图
    selenium pytest_html测试报告添加测试步骤截图
    pytest单元测试框架fixture应用
    unittest单元测试框架教程7-unittest.TestSuite类详解
    unittest单元测试框架教程6-unittest.TestCase类详解
    unittest单元测试框架教程5-使用subTest进行循环测试
    unittest单元测试框架教程3-利用unittest测试原理组织测试套件和用例
    unittest单元测试框架教程2-通过TestLoader运行用例
    Python学习相关链接
    GO语言相关的链接整理
  • 原文地址:https://www.cnblogs.com/felixwang2/p/9884708.html
Copyright © 2020-2023  润新知