Django學習之十三:提高頁面開發效率減少冗餘的模板系統

来源:https://www.cnblogs.com/ZJiQi/archive/2019/04/20/10739600.html
-Advertisement-
Play Games

Django 模板 === [toc] 模板按照我的理解,就是讓html中內容不固定,讓html內容已後端的方式動態起來(雖然前端mvvm框架也也開始有模板概念,所以廣義說模板概念不限於後端)。但是html基礎的內容還是是固定的。模板通過類編程的模板語法,可以將html模板中的動態內容,通過後端程式 ...


目錄

Django 模板

模板按照我的理解,就是讓html中內容不固定,讓html內容已後端的方式動態起來(雖然前端mvvm框架也也開始有模板概念,所以廣義說模板概念不限於後端)。但是html基礎的內容還是是固定的。模板通過類編程的模板語法,可以將html模板中的動態內容,通過後端程式的計算傳入核心數據,最後通過模板語法得到一個完整的html。模板的構造核心就是:模板語法和上下文數據(渲染引擎的全局數據和後端代碼傳入的數據);模板的驅動就是模板引擎(如Jinja2,django內置的DTL)。模板語法的數據來自於上下文數據,使得模板可以動態的生成html內容,關鍵讓類似內容的構造更加高效,如for迴圈渲染列表。模板語法還提供模板與模板間存在關係:繼承關係和包含關係。模板間的關係時的開髮網頁減少大量的冗餘內容。
後端使用模板,多用於開發訪問量較小的後臺管理系統。

模板語法

模版文件中使用的variables會被上下文字典中的對應的key的值所替代。
模版文件中使用的tags會被引擎執行一段相應的邏輯。

邏輯語法

  • {% if condition %} {% else %} 條件
  • {% for %} {% endfor%} 迭代

函數式過濾器

  • 說明: filter是用於轉變 variable 和 tag's arguments 的
  • 例如: {{ django|title }} 其上下文 {'django': 'the web framework'}
  • 渲染後:'The Web Framework'
  • filter參數:有些filter是可以傳入參數的,參數傳入方式是filter:argument;
  • 例如: {{ datetime|date:"Y-m-d" }}

內置filter

內置filter: 參見官檔索引

功能tag

  • 語法: {% tag %}
  • tag用法: 不同tag有自己的處理邏輯,包括用戶自定義的tag,並且tag可以接受參數,有些tag有開始和結束tag對。
  • {% csrf_token %} 無參數
  • {% cycle 'odd' 'even' %} 有參數
  • {% if user.is_authenticated %} Hello, {{ user.username }}. {% endif %} 有開始和結束tag對的。
  • 說明:tag有內置,也可自定義。

註釋

  • 語法:{# this won't be rendered #}
  • 作用:就是註釋,不會被渲染。
  • 多行註釋利用tag: {% comment %} 多行註釋內容 {% endcomment %}
  • 說明: 為什麼不用html的註釋,因為模版語言不止用於html文本,其它文本也可以,所以在模版語言這一層來進行註釋才行。註意,註釋不能嵌套。

內置tag

內置tag:參見官檔索引

導入三方tag and filter(load)

{% load static %} 載入static app中templatetags目錄下的static文件中定義的tag。
load同時也會導入tag和filter

過濾器和功能tag的區別

Tags的功能比filter要複雜的多,因為tag幾乎可以做任何事情,包括最重要的渲染模版inclusion_tag。而filter主要是對數據進行處理。

自定義tag和filter

  1. 前提:最常見是自定義的tag是在對應的app目錄下的創建的,所以要將app註冊到settings的installed_apps列表中。
  2. 在app目錄下創建templatetags目錄,名字必須固定,這個是預設查找載入指定自定義tag和filter文件的地方,併在目錄創建__init__.py文件。(是django預設的finder查找邏輯限制了這個名字)
  3. 創建py文件,自定義tag就放在文件中。文件名一定要小心,不要和其它app註冊的衝突了。自定義filter和tag,如:
# my_tags.py
from django import template

register = template.Library()  # register變數名固定

@register.filter  # 自定義filter
def multi_tag(v1, v2):
    return v1 * v2


@register.simple_tag  # 自定義simple tag
def tag_add(v1, v2):
    return v1 + v2
  1. 自定義好自己的tags和filters,重要的一步就是將他們擴展使用到template引擎中,讓他們生效就要使用{% load %} tag.
  2. 在模板文件中添加load標簽後,需要重啟服務。這點很關鍵啊!還有load後面的自定義tag保存的文件,不用引號,直接load後面跟文件名就行了
  3. 在模版中使用自定義tag和filter。模版間繼承關係,是不會繼承{% load %} 標簽的,所以每個模版中要使用自定義的tag和filter都需要再次{% load %}一次。

上下文數據

  • 語法: {{ variable }}
  • 用法: My first name is {{ first_name }}. My last name is {{ last_name }}.
  • 非全局變數,需要提供的上下文字典(或者叫環境或者叫作用域):{'first_name': 'John', 'last_name': 'Doe'}
  • 通過render後產生的結果:My first name is John. My last name is 'Doe'.
  • 如果變數指向的也是一個字典:那麼使用dotted可以訪問了字典中的值:{{ my_dict.key }} {{ my_object.attribute }} {{ my_list.0 }}

    全局數據

    內置:官檔
  • request對象自動傳入

    傳入數據

  • render('inde.html', {已字典結構傳入})

模板間關係

繼承關係

通過{% extends '父模板.hmtl' %}'

包含關係

通過{% include '插入的模板.html' %} , 說明插入的模板可以使用數據

inclusion_tag關係

inclusion_tag 通過自定義tag形式,tag綁定了一個模板,tag函數處理邏輯放回一個上下文字典供綁定的模板渲染。這種也相當於是模板的包含關係,只不過是通過一個自定義的inclusion_tag進行封裝。且讓插座與插頭對接更為明確,就是提供給tag提供參數,參數就是對接的規範了。由於是tag的形式,tag函數代碼可以訪問後臺的所有數據。

inclusion_tag 示例詳解

      下麵代碼將一個模版通過使用自定義的類型為inclusion tag即進行渲染使用:
      這個自定義tag是怎麼做到的呢?
      首先,我們利用這個tag的場景是:多個url頁面都要用到相同的頁面佈局內容。如:博客系統中的個人站點的用戶文章列表,標簽列表,公告;這些對於這個用戶的站點內容都是一樣的。這些共同的東西要怎麼才能重覆利用呢?對於學習了template繼承知識的同學,可能想到的是使用繼承關係既可以了。繼承是沒錯,但是相同部分的內容,要提供給模版語言的數據還是要給予的,不同的是這些數據在各自的視圖view函數中,要去重覆的獲取數據這些重覆的獲取數據的代碼,在這些視圖之間都是一樣的。雖然模版得到了繼承,但是模版要用到的數據還是造成了重覆代碼。要解決這個問題方式一:可以將獲取數據的代碼,封裝到一個函數代碼塊中,這樣能解決重覆問題。這是利用基於模版的繼承為主要思想的思路,因為模版必須繼承才行。
      上面提到的模版繼承思路,有一個局限性就是耦合度太高,必須繼承模版。有沒有什麼方法不用繼承模版就可以實現相同頁面塊的即插即用(繼承方式無法即插即用)。django的一個自定義tag類型,給我們提供了一種即插即用的思路,這種思路是基於模版語言的tag對應一個python函數邏輯的思想。只需要自定義一個tag,tag就可以在任何的模版中插入使用。tag要做的就是返回一個渲染了的在前面提到的重覆頁面就行了。(這個就類似與include內置tag功能的一樣,不同的是,include的頁面是死的頁面;而這裡自定義的tag是可以利用模版語言結合上下文數據,動態的渲染出這個即插即用的頁面)。這個指定tag就是django的inclusion tags。
inlucsion tags 的使用需要準備兩個東西:一個即插即用的template,另一個就是渲染模版的上下文context data數據了。這個數據就是我們第一種思想中提到的要通過一個封裝函數獲取的數據。其實參數就是通過被插入的主模版能夠給我們提供的數據了。即插即用,解耦的思想很棒!下麵過一個列子來實踐這個即插即用的思想。

友情提醒一句:利用這個思想,首先是目的和使用場景,前端頁面要重覆使用有且並且要重覆的頁面的上下文數據獲取較多比較麻煩且重覆

開始示例:

  1. 模版stuff_list.html:

{# 公告 #}
<div class="panel panel-info">
    <div class="panel-heading">
        <h3 class="panel-title">公告</h3>
    </div>
    <div class="panel-body">
        <ul class="panel-list" type="none">
            <li><span>用戶別名: {{ user_obj.username }}</span></li>
            <li><span>園齡: {{ age_days }} 天</span></li>
            <li><span>隨筆數: {{ user_obj.article_set.count }} 篇</span></li>
        </ul>
    </div>
</div>

{#隨筆分類#}
<div class="panel panel-info">
    <div class="panel-heading">
        <h3 class="panel-title">隨筆分類</h3>
    </div>

    <div class="list-group">
        {% for item in article_category_queryset %}
            <a href="" class="list-group-item">{{ item.category__title }} ({{ item.count }})</a>
        {% endfor %}
    </div>
</div>


{#標簽#}
<div class="panel panel-info">
    <div class="panel-heading">
        <h3 class="panel-title">我的標簽</h3>
    </div>

    <div class="list-group">
        {% for item in article_tag_queryset %}
            <a href="" class="list-group-item">{{ item.title }} ({{ item.count }})</a>
        {% endfor %}
    </div>
</div>


{#歸檔#}
<div class="panel panel-info">
    <div class="panel-heading">
        <h3 class="panel-title">隨筆歸檔</h3>
    </div>

    <div class="list-group">
        {% for item in article_archive_queryset %}
            <a href="" class="list-group-item">{{ item.y_m }} ({{ item.count }})</a>
        {% endfor %}
    </div>
</div>
  1. 自定義的tag文件my_tags_filters.py:

重覆獲取的上下問數據就在該文件中函數中實現,返回一個上下文數據字典。在利用渲染裝飾器來裝飾這個函數,裝飾器函數要帶入要渲染的模版文件。相當於就是裝飾器給我們做渲染模版作用,我們的自定義函數來獲取查詢數據,返回渲染需要的上下文數據就行。

from django import template
from myblog import models
from django.utils import timezone
from django.db.models import Count

register = template.Library()

@register.inclusion_tag('myblog/stuff_list.html')  # 綁定我們的模板了
def stuff_list(user_obj):  # 參數需要一個,這裡就是用戶對象
    # 計算園齡 以天為單位
    curr_time = timezone.now()
    create_time = user_obj.create_time
    interval_timedelta = curr_time - create_time
    age_days = interval_timedelta.days

    user_articles = models.Article.objects.filter(user=user_obj)

    # 分類
    article_category_queryset = user_articles.values('category__pk').annotate(
        count=Count('pk')).values('category__title', 'count')

    # 標簽
    # article_tag_queryset = user_articles.values('tag__pk').annotate(count=Count('pk')).values('tag__title', 'count')  這個計算出來是一個{'None': 6} 把None做了一個分類,下麵分組方式就是一個[]的queryset。兩種看來還是不一樣。
    article_tag_queryset = models.Tag.objects.filter(blog=user_obj.blog).values('pk').annotate(
        count=Count('article__pk')).values('title', 'count')
    print(article_tag_queryset)  ###

    article_archive_queryset = user_articles.extra(select={'y_m': "date_format(create_time, '%%Y-%%m')"}).values(
        'y_m').annotate(count=Count('pk')).values('y_m', 'count')
    print(article_archive_queryset)  ###
    
    # 這就是即插即用模版的上下文數據;通過一個user_obj參數我們就得到了要渲染到綁定模板中的上下文數據了。
    return {'user_obj': user_obj,
            'age_days': age_days,
            'article_category_queryset': article_category_queryset,
            'article_tag_queryset': article_tag_queryset,
            'article_archive_queryset': article_archive_queryset,}
  1. 使用我們的inclusion_tag了,將my_tags_filters.py 放入到app目錄下的templatetags目錄下,然後再要使用的模板中進行{% load my_tags_filters %} 就可以了。然後使用tag {% stuff_list site_user %} 註意要傳遞我們的參數。

小結

  1. 從模板關係可以發散出:其實繼承是非常好的內容重用減少冗餘的設計,同時包含關係,也可以說是可插拔模式,插入即用,還可復用。包含關係多是用在畫面構建方面,如設計圖,html模板,畫作等。
  2. 什麼時候繼承,什麼時候包含:繼承是框架,包含是插拔插件,包含是對繼承的補充。繼承局限更強,包含可跨繼承。比如說,一個父模板,所有的子模版都是在繼承父類的基礎上修改覆蓋。而包含可以在多個父模板中使用,突破了必須在一個父模板下的限制,但是包含不是隨便插入,是類似插座和插頭的概念,被插入模板需要提供給插入模板相關數據的。。
  3. 金字塔結構:通過模板引擎解析模板語法,根據傳入上下文環境渲染出動態內容。模板語法。傳入數據。
    tag和filter。模板關係。全局參數和傳入參數。
    內置tag_filter和自定義tag_filter,tag功能強於filter。繼承,include,inclusion_tag。request/auth。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一. 標準盒模型和IE盒模型 1. w3c標準盒模型 width和height不包括padding和border 2. ie盒模型 width和height包括padding和border 3. css3中的box-sizing content-box w3c標準盒模型 border-box IE盒 ...
  • 里氏替換原則LSP (Liskov Subsituation Principle) 里氏替換原則定義 所有 父類出現 的地方可以使用 子類替換 並不會出現錯誤或異常,但是反之子類出現的地方不一定能用父類替換。 LSP的四層含義 子類必須完全實現父類的方法 子類可以自己的個性(屬性和方法) 覆蓋或實現 ...
  • 策略模式 定義 什麼是策略模式?定義了演算法族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶。 我的理解就是:比如我們接下來要說到的鴨子案例,有的鴨子可以飛,而飛又分為很多種,飛很高,飛得很低各種,我們就會把飛這個行為定義為介面,然後再分別去實現,而我們的鴨子只需要註入 ...
  • 單一職責原則SRP (Single reponsibility principle) BO(Business Object) :業務對象 Biz(Business Logic) :業務邏輯 SRP最簡單的例子:用戶信息維護類 單一職責原則SRP定義 應該有且僅有一個原因引起類的變更。( 一個介面只有 ...
  • [TOC] 在學習Observer觀察者模式時發現它符合敏捷開發中的OCP開放 封閉原則, 本文通過一個場景從差的設計開始, 逐步向Observer模式邁進, 最後的代碼能體現出OCP原則帶來的好處, 最後分享Observer模式在自己的項目中的實現. 場景引入 在一戶人家中, 小孩在睡覺, 小孩睡 ...
  • 1.什麼是spring IOC IOC(Inversion of Control)即控制反轉,在我們以往的編程中如果需要一個bean往往需要去手動去new一個出來。而spring幫我們解決了這個問題,在spring中我們只需要去定義bean,spring就會自動的幫我們實例化並管理Bean。而這些B ...
  • 控制台編程中,使用了滑鼠操作,遇到了控制台無法接收到滑鼠消息的問題,可嘗試一下辦法解決 【win10系統】 在控制台標題欄右鍵-預設值-選項,將一下對勾取消 然後調用如下函數: HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); SetConsoleMode(h ...
  • 一、網路簡介 網路是由節點和連線構成,表示諸多對象及其相互聯繫。 一個人玩: 兩個人玩: 多個人玩: 說明 網路就是一種輔助雙方或者多方能夠連接在一起的工具 如果沒有網路可想單機的世界是多麼的孤單 使用網路的目的 就是為了聯通多方然後進行通信用的,即把數據從一方傳遞給另外一方 前面的學習編寫的程式都 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...