Django 2.1 - Custom template tags and filters

कस्टम टेम्प्लेट टैग और फ़िल्टर




django

कस्टम टेम्प्लेट टैग और फ़िल्टर

Django की टेम्प्लेट भाषा विभिन्न प्रकार के बिल्ट-इन टैग्स और फिल्टर्स के साथ आती है, जो आपके एप्लिकेशन की प्रस्तुति लॉजिक जरूरतों को पूरा करने के लिए डिज़ाइन किए गए हैं। फिर भी, आप अपने आप को कार्यक्षमता की आवश्यकता पा सकते हैं जो टेम्पलेट प्राइमेटिव के मुख्य सेट द्वारा कवर नहीं किया गया है। आप पायथन का उपयोग करके कस्टम टैग और फ़िल्टर को परिभाषित करके टेम्पलेट इंजन का विस्तार कर सकते हैं, और फिर {% load %} टैग का उपयोग करके उन्हें अपने टेम्पलेट्स को उपलब्ध करा सकते हैं।

कोड लेआउट

कस्टम टेम्पलेट टैग और फ़िल्टर निर्दिष्ट करने के लिए सबसे आम जगह एक Django ऐप के अंदर है। यदि वे एक मौजूदा ऐप से संबंधित हैं, तो उन्हें वहां बंडल करने के लिए समझ में आता है; अन्यथा, उन्हें एक नए ऐप में जोड़ा जा सकता है। जब एक Django ऐप INSTALLED_APPS में जोड़ा जाता है, तो नीचे वर्णित पारंपरिक स्थान में परिभाषित कोई भी टैग स्वचालित रूप से टेम्पलेट्स के भीतर लोड करने के लिए उपलब्ध कराया जाता है।

एप्लिकेशन में एक templatetags निर्देशिका सम्‍मिलित होनी चाहिए, समान स्तर पर जैसे कि models.py , views.py , आदि। यदि यह पहले से मौजूद नहीं है, तो इसे बनाएं - यह सुनिश्चित करने के लिए __init__.py फ़ाइल को न भूलें कि निर्देशिका के रूप में माना जाता है एक पायथन पैकेज।

विकास सर्वर स्वचालित रूप से पुनरारंभ नहीं होगा

templatetags मॉड्यूल को जोड़ने के बाद, आपको templatetags में टैग या फ़िल्टर का उपयोग करने से पहले अपने सर्वर को पुनरारंभ करना होगा।

आपके कस्टम टैग और फ़िल्टर, templatetags निर्देशिका के अंदर एक मॉड्यूल में रहेंगे। मॉड्यूल फ़ाइल का नाम वह नाम है जिसका उपयोग आप बाद में टैग लोड करने के लिए करेंगे, इसलिए ऐसा नाम चुनने के लिए सावधान रहें जो किसी अन्य ऐप में कस्टम टैग और फ़िल्टर के साथ टकराए नहीं।

उदाहरण के लिए, यदि आपके कस्टम टैग / फ़िल्टर poll_extras.py नामक फ़ाइल में हैं, तो आपका ऐप लेआउट इस तरह दिखाई दे सकता है:

polls/
    __init__.py
    models.py
    templatetags/
        __init__.py
        poll_extras.py
    views.py

और अपने टेम्पलेट में आप निम्नलिखित का उपयोग करेंगे:

{% load poll_extras %}

जिस ऐप में कस्टम टैग हैं, वह काम करने के लिए {% load %} टैग के लिए INSTALLED_APPS में होना चाहिए। यह एक सुरक्षा विशेषता है: यह आपको प्रत्येक Django स्थापना के लिए उन सभी तक पहुंच को सक्षम किए बिना एक एकल होस्ट मशीन पर कई टेम्पलेट पुस्तकालयों के लिए पायथन कोड की मेजबानी करने की अनुमति देता है।

आप templatetags पैकेज में कितने मॉड्यूल डालते हैं, इसकी कोई सीमा नहीं है। बस ध्यान रखें कि एक {% load %} स्टेटमेंट दिए गए पायथन मॉड्यूल नाम के लिए टैग / फ़िल्टर लोड करेगा, ऐप का नाम नहीं।

एक वैध टैग लाइब्रेरी होने के लिए, मॉड्यूल में register नाम का एक मॉड्यूल-स्तरीय चर होना चाहिए, जो एक template.Library उदाहरण के लिए, जिसमें सभी टैग और फ़िल्टर पंजीकृत हैं। तो, अपने मॉड्यूल के शीर्ष के पास, निम्नलिखित डालें:

from django import template

register = template.Library()

वैकल्पिक रूप से, टेम्पलेट टैग मॉड्यूल को 'libraries' तर्क के माध्यम से DjangoTemplates पंजीकृत किया जा सकता है। यदि आप टेम्पलेट टैग लोड करते समय टेम्पलेट टैग मॉड्यूल नाम से एक अलग लेबल का उपयोग करना चाहते हैं तो यह उपयोगी है। यह आपको एप्लिकेशन इंस्टॉल किए बिना टैग पंजीकृत करने में भी सक्षम बनाता है।

परदे के पीछे

एक उदाहरण के लिए, Django के डिफ़ॉल्ट फ़िल्टर और टैग के लिए स्रोत कोड पढ़ें। वे क्रमशः django/template/defaultfilters.py और django/template/defaulttags.py में हैं।

load टैग के बारे में अधिक जानकारी के लिए, इसके प्रलेखन को पढ़ें।

कस्टम टेम्पलेट फ़िल्टर लिखना

कस्टम फ़िल्टर केवल पायथन कार्य हैं जो एक या दो तर्क लेते हैं:

  • चर (इनपुट) का मूल्य - जरूरी नहीं कि एक स्ट्रिंग।
  • तर्क का मूल्य - इसका एक डिफ़ॉल्ट मूल्य हो सकता है, या पूरी तरह से छोड़ दिया जा सकता है।

उदाहरण के लिए, फ़िल्टर {{ var|foo:"bar" }} , फ़िल्टर foo को चर var और तर्क "bar"

चूंकि टेम्पलेट भाषा अपवाद हैंडलिंग प्रदान नहीं करती है, इसलिए टेम्पलेट फ़िल्टर से उठाया गया कोई भी अपवाद सर्वर त्रुटि के रूप में सामने आएगा। इस प्रकार, फ़िल्टर फ़ंक्शंस को अपवादों को उठाने से बचना चाहिए, यदि वापसी करने के लिए एक उचित फ़ॉलबैक मूल्य है। इनपुट के मामले में जो टेम्पलेट में एक स्पष्ट बग का प्रतिनिधित्व करता है, अपवाद को उठाना अभी भी मूक विफलता से बेहतर हो सकता है जो बग को छुपाता है।

यहाँ एक उदाहरण फ़िल्टर परिभाषा है:

def cut(value, arg):
    """Removes all values of arg from the given string"""
    return value.replace(arg, '')

और यहां एक उदाहरण दिया गया है कि उस फ़िल्टर का उपयोग कैसे किया जाएगा:

{{ somevariable|cut:"0" }}

अधिकांश फ़िल्टर तर्क नहीं लेते हैं। इस स्थिति में, अपने फ़ंक्शन से तर्क को छोड़ दें। उदाहरण:

def lower(value): # Only one argument.
    """Converts a string into all lowercase"""
    return value.lower()

कस्टम फ़िल्टर पंजीकृत करना

django.template.Library.filter()

एक बार जब आप अपनी फ़िल्टर परिभाषा लिख ​​लेते हैं, तो आपको इसे अपनी Library उदाहरण के साथ पंजीकृत करने की आवश्यकता होती है, ताकि इसे Django की टेम्पलेट भाषा में उपलब्ध कराया जा सके:

register.filter('cut', cut)
register.filter('lower', lower)

Library.filter() विधि में दो तर्क हैं:

  1. फिल्टर का नाम - एक स्ट्रिंग।
  2. संकलन समारोह - एक पायथन फ़ंक्शन (स्ट्रिंग के रूप में फ़ंक्शन का नाम नहीं)।

आप इसके बजाय डेकोरेटर के रूप में register.filter() उपयोग कर सकते हैं:

@register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')

@register.filter
def lower(value):
    return value.lower()

यदि आप name तर्क को छोड़ देते हैं, जैसा कि ऊपर दिए गए दूसरे उदाहरण में, Django फ़ंक्शन के नाम को फ़िल्टर नाम के रूप में उपयोग करेगा।

अंत में, register.filter() भी तीन कीवर्ड तर्क, is_safe , needs_autoescape और expects_localtime स्वीकार करता है। इन तर्कों को नीचे दिए गए फ़िल्टर और ऑटो-एस्केपिंग और फ़िल्टर और टाइम ज़ोन में वर्णित किया गया है।

टेम्प्लेट फ़िल्टर जो स्ट्रिंग की अपेक्षा करते हैं

django.template.defaultfilters.stringfilter()

यदि आप एक टेम्पलेट फ़िल्टर लिख रहे हैं जो केवल पहले तर्क के रूप में एक स्ट्रिंग की उम्मीद करता है, तो आपको डेकोरेटर stringfilter उपयोग करना चाहिए। यह आपके फ़ंक्शन में दिए जाने से पहले ऑब्जेक्ट को इसके स्ट्रिंग मान में बदल देगा:

from django import template
from django.template.defaultfilters import stringfilter

register = template.Library()

@register.filter
@stringfilter
def lower(value):
    return value.lower()

इस तरह, आप इस फ़िल्टर के पूर्णांक को पास, कहने, और सक्षम करने में सक्षम होंगे, और इससे AttributeError नहीं होगा (क्योंकि पूर्णांकों में lower() विधियाँ नहीं हैं)।

फिल्टर और ऑटो-भागने

कस्टम फ़िल्टर लिखते समय, कुछ विचार दें कि फ़िल्टर कैसे Django के ऑटो-एस्केप व्यवहार के साथ बातचीत करेगा। ध्यान दें कि टेम्पलेट कोड के अंदर दो प्रकार के तार पास किए जा सकते हैं:

  • कच्चे तार देशी पायथन तार हैं। आउटपुट पर, वे बच गए हैं यदि ऑटो-भागने प्रभाव में है और अपरिवर्तित प्रस्तुत किया गया है, अन्यथा।
  • सुरक्षित तार वे तार होते हैं जिन्हें आउटपुट समय पर आगे भागने से सुरक्षित चिह्नित किया गया है। कोई भी आवश्यक भागने पहले ही किया जा चुका है। वे आमतौर पर ऐसे आउटपुट के लिए उपयोग किए जाते हैं जिनमें कच्चा HTML होता है, जिसका अर्थ है कि ग्राहक की तरफ से व्याख्या की जाती है।

    आंतरिक रूप से, ये तार SafeText प्रकार के SafeText । आप कोड का उपयोग करके उनके लिए परीक्षण कर सकते हैं जैसे:

    from django.utils.safestring import SafeText
    
    if isinstance(value, SafeText):
        # Do something with the "safe" string.
        ...
    

टेम्पलेट फ़िल्टर कोड दो स्थितियों में से एक में आता है:

  1. आपका फ़िल्टर किसी भी HTML-असुरक्षित वर्णों ( < , > , ' , " या) को उस परिणाम में प्रस्तुत नहीं करता है जो पहले से मौजूद नहीं थे। इस मामले में, आप Django को आपके लिए सभी ऑटो-एस्केप हैंडलिंग का ध्यान रख सकते हैं। जब आपको अपना फ़िल्टर फ़ंक्शन पंजीकृत करना हो, तो आपको is_safe ध्वज को True सेट करना होगा:

    @register.filter(is_safe=True)
    def myfilter(value):
        return value
    

    यह ध्वज Django को बताता है कि यदि आपके फ़िल्टर में "सुरक्षित" स्ट्रिंग पास की जाती है, तो परिणाम अभी भी "सुरक्षित" होगा और यदि गैर-सुरक्षित स्ट्रिंग में पारित किया जाता है, तो यदि आवश्यक हो, तो Django स्वचालित रूप से इसे से बच जाएगा।

    आप इसका अर्थ यह मान सकते हैं कि "यह फ़िल्टर सुरक्षित है - यह असुरक्षित HTML की किसी भी संभावना का परिचय नहीं देता है।"

    इसका कारण is_safe आवश्यक है क्योंकि बहुत सारे सामान्य स्ट्रिंग ऑपरेशन हैं जो SafeData ऑब्जेक्ट को एक सामान्य str ऑब्जेक्ट में बदल देगा और, उन सभी को पकड़ने की कोशिश करने के बजाय, जो बहुत मुश्किल होगा, Django फ़िल्टर के बाद क्षति की मरम्मत करता है पूरा कर लिया है।

    उदाहरण के लिए, मान लें कि आपके पास एक फ़िल्टर है जो किसी भी इनपुट के अंत में स्ट्रिंग xx जोड़ता है। चूंकि यह परिणाम के लिए कोई खतरनाक HTML वर्ण पेश नहीं करता है (किसी भी तरफ पहले से मौजूद थे), आपको अपने फ़िल्टर को is_safe साथ चिह्नित करना चाहिए:

    @register.filter(is_safe=True)
    def add_xx(value):
        return '%sxx' % value
    

    जब यह फ़िल्टर एक टेम्पलेट में उपयोग किया जाता है जहां ऑटो-एसेडिंग सक्षम है, तो Django आउटपुट से बच जाएगा जब इनपुट "सुरक्षित" के रूप में पहले से ही चिह्नित नहीं है।

    डिफ़ॉल्ट रूप से, is_safe False , और आप इसे किसी भी फ़िल्टर से छोड़ सकते हैं जहाँ इसकी आवश्यकता नहीं है।

    यदि आपका फ़िल्टर वास्तव में सुरक्षित तारों को सुरक्षित छोड़ देता है, तो निर्णय लेते समय सावधान रहें। यदि आप वर्ण हटा रहे हैं, तो आप अनजाने में परिणाम में असंतुलित HTML टैग या इकाइयां छोड़ सकते हैं। उदाहरण के लिए, इनपुट से एक हटाने से <a में बदल सकता <a , जिससे समस्या उत्पन्न होने से बचने के लिए आउटपुट पर बच जाना होगा। इसी तरह, एक अर्धविराम ( ; ) को हटाकर &amp; &amp , जो अब एक मान्य इकाई नहीं है और इस प्रकार आगे भागने की आवश्यकता है। अधिकांश मामले लगभग इस मुश्किल नहीं होंगे, लेकिन अपने कोड की समीक्षा करते समय इस तरह की किसी भी समस्या पर नज़र रखें।

    एक फिल्टर is_safe को चिह्नित करना फ़िल्टर के रिटर्न मान को एक स्ट्रिंग में ले जाएगा। यदि आपके फ़िल्टर को एक बूलियन या अन्य गैर-स्ट्रिंग मान वापस करना चाहिए, तो is_safe चिह्नित करना संभवतः अनपेक्षित परिणाम होगा (जैसे कि बूलियन गलत को स्ट्रिंग में परिवर्तित करना 'गलत')।

  2. वैकल्पिक रूप से, आपका फ़िल्टर कोड मैन्युअल रूप से किसी भी आवश्यक बचने का ध्यान रख सकता है। जब आप परिणाम में नया HTML मार्कअप पेश कर रहे हों तो यह आवश्यक है। आप आउटपुट को आगे भागने से सुरक्षित चिह्नित करना चाहते हैं ताकि आपका HTML मार्कअप आगे बच न जाए, इसलिए आपको इनपुट को स्वयं संभालना होगा।

    आउटपुट को एक सुरक्षित स्ट्रिंग के रूप में चिह्नित करने के लिए, django.utils.safestring.mark_safe() उपयोग करें।

    हालांकि सावधान रहें। आपको आउटपुट को केवल सुरक्षित के रूप में चिह्नित करने की आवश्यकता है। आपको यह सुनिश्चित करने की आवश्यकता है कि यह वास्तव में सुरक्षित है, और आप जो करते हैं वह इस बात पर निर्भर करता है कि क्या ऑटो-भागने प्रभाव में है। विचार उन फिल्टर को लिखना है जो उन टेम्प्लेटों में काम कर सकते हैं जहां आपके टेम्पलेट लेखकों के लिए चीजों को आसान बनाने के लिए ऑटो-एस्केपिंग चालू या बंद है।

    अपने फ़िल्टर के लिए वर्तमान ऑटो- needs_autoescape स्थिति को जानने के लिए, जब आप अपना फ़िल्टर फ़ंक्शन पंजीकृत करते हैं, तो needs_autoescape फ़्लैग को True सेट करें। (यदि आप इस ध्वज को निर्दिष्ट नहीं करते हैं, तो यह False ) यह ध्वज Django को बताता है कि आपका फ़िल्टर फ़ंक्शन एक अतिरिक्त कीवर्ड तर्क पारित करना चाहता है, जिसे autoescape कहा जाता है, यह True यदि ऑटो-भागने प्रभाव में है और अन्यथा False है। यह autoescape पैरामीटर के डिफॉल्ट को True पर सेट करने की सिफारिश की जाती है, ताकि यदि आप पायथन कोड से फ़ंक्शन को कॉल करते हैं, तो यह डिफ़ॉल्ट रूप से सक्षम होने से बच जाएगा।

    उदाहरण के लिए, आइए एक फिल्टर लिखें जो एक स्ट्रिंग के पहले वर्ण पर जोर देता है:

    from django import template
    from django.utils.html import conditional_escape
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    @register.filter(needs_autoescape=True)
    def initial_letter_filter(text, autoescape=True):
        first, other = text[0], text[1:]
        if autoescape:
            esc = conditional_escape
        else:
            esc = lambda x: x
        result = '<strong>%s</strong>%s' % (esc(first), esc(other))
        return mark_safe(result)
    

    needs_autoescape फ़्लैग और autoescape कीवर्ड तर्क का मतलब है कि हमारे फ़ंक्शन को पता चल जाएगा कि फ़िल्टर को कॉल करने पर ऑटोमैटिक autoescape प्रभावी है या नहीं। हम यह तय करने के लिए autoescape का उपयोग करते हैं कि इनपुट डेटा को django.utils.html.conditional_escape माध्यम से पारित करने की आवश्यकता है या नहीं। (बाद वाले मामले में, हम पहचान फ़ंक्शन को "एस्केप" फ़ंक्शन के रूप में उपयोग करते हैं। SafeData escape() फ़ंक्शन escape() की तरह escape() सिवाय इसके कि केवल इनपुट से बच जाता है जो कि SafeData उदाहरण नहीं है। यदि SafeData उदाहरण SafeData conditional_escape() , तो डेटा अपरिवर्तित वापस आ जाता है।

    अंत में, उपरोक्त उदाहरण में, हम परिणाम को सुरक्षित के रूप में चिह्नित करना याद करते हैं ताकि हमारे HTML को आगे भागने के बिना सीधे टेम्पलेट में डाला जाए।

    इस मामले में is_safe ध्वज के बारे में चिंता करने की कोई आवश्यकता नहीं है (हालांकि इसमें कुछ भी शामिल नहीं होगा)। जब भी आप मैन्युअल रूप से ऑटो-भागने के मुद्दों को संभालते हैं और एक सुरक्षित स्ट्रिंग is_safe , is_safe झंडा किसी भी तरह से कुछ भी नहीं बदलेगा।

चेतावनी

अंतर्निहित फ़िल्टर का पुन: उपयोग करते समय XSS भेद्यता से बचना

Django के बिल्ट-इन फिल्टर्स में autoescape=True रूप से डिफॉल्ट रूप से होता है ताकि उचित ऑटोसोस्टिंग व्यवहार प्राप्त कर सके और क्रॉस-साइट स्क्रिप्ट भेद्यता से बच सकें।

Django के पुराने संस्करणों में, जब Django के अंतर्निहित फ़िल्टर को पुन: उपयोग करने से सावधान रहें, तो autoescape डिफॉल्ट के रूप में None । आपको autoescape=True प्राप्त करने के लिए ऑटोसस्केप autoescape=True पास करना होगा।

उदाहरण के लिए, यदि आप urlize_and_linebreaks नामक एक कस्टम फ़िल्टर लिखना चाहते हैं, जो urlize और linebreaksbr फ़िल्टर को संयोजित करता है, तो फ़िल्टर ऐसा दिखेगा:

from django.template.defaultfilters import linebreaksbr, urlize

@register.filter(needs_autoescape=True)
def urlize_and_linebreaks(text, autoescape=True):
    return linebreaksbr(
        urlize(text, autoescape=autoescape),
        autoescape=autoescape
    )

फिर:

{{ comment|urlize_and_linebreaks }}

इसके बराबर होगा:

{{ comment|urlize|linebreaksbr }}

फिल्टर और समय क्षेत्र

यदि आप एक कस्टम फ़िल्टर लिखते हैं, जो expects_localtime ऑब्जेक्ट्स पर काम करता है, तो आप आमतौर पर इसे True सेट होने वाले expects_localtime फ़्लैग के साथ पंजीकृत करेंगे:

@register.filter(expects_localtime=True)
def businesshours(value):
    try:
        return 9 <= value.hour < 17
    except AttributeError:
        return ''

जब यह फ़्लैग सेट किया जाता है, यदि आपके फ़िल्टर का पहला तर्क टाइम ज़ोन अवगत डेटाइम है, तो टैम्पो में टाइम ज़ोन के रूपांतरणों के नियमों के अनुसार, जब भी उचित होगा, Django इसे आपके फ़िल्टर्स में पास करने से पहले वर्तमान समय क्षेत्र में बदल देगा।

कस्टम टेम्पलेट टैग लिखना

टैग फ़िल्टर से अधिक जटिल हैं, क्योंकि टैग कुछ भी कर सकते हैं। Django कई प्रकार के शॉर्टकट प्रदान करता है जो अधिकांश प्रकार के टैग लिखना आसान बनाते हैं। पहले हम उन शॉर्टकटों का पता लगाएंगे, फिर समझाएंगे कि उन मामलों के लिए स्क्रैच से टैग कैसे लिखें जब शॉर्टकट पर्याप्त शक्तिशाली नहीं हैं।

सरल टैग

django.template.Library.simple_tag()

कई टेम्प्लेट टैग कई तरह के तर्क देते हैं - स्ट्रिंग्स या टेम्प्लेट वैरिएबल - और इनपुट तर्कों और कुछ बाहरी जानकारी के आधार पर कुछ प्रसंस्करण करने के बाद एक परिणाम लौटाते हैं। उदाहरण के लिए, एक current_time टैग एक प्रारूप स्ट्रिंग को स्वीकार कर सकता है और एक स्ट्रिंग के रूप में समय को तदनुसार स्वरूपित करता है।

इस प्रकार के टैग के निर्माण को आसान बनाने के लिए, Django एक सहायक फ़ंक्शन, simple_tag प्रदान करता है। यह फ़ंक्शन, जो django.template.Library की एक विधि है, एक फ़ंक्शन लेता है जो किसी भी संख्या में तर्कों को स्वीकार करता है, इसे एक render फ़ंक्शन में लपेटता है और ऊपर वर्णित अन्य आवश्यक बिट्स इसे टेम्पलेट सिस्टम के साथ पंजीकृत करता है।

हमारा current_time फ़ंक्शन इस प्रकार लिखा जा सकता है:

import datetime
from django import template

register = template.Library()

@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

simple_tag सहायक फ़ंक्शन के बारे में ध्यान देने योग्य कुछ बातें:

  • तर्कों की आवश्यक संख्या के लिए जाँच करना, आदि पहले से ही हमारे फ़ंक्शन को बुलाया जाता है, इसलिए हमें ऐसा करने की आवश्यकता नहीं है।
  • तर्क के आसपास के उद्धरण (यदि कोई हो) पहले ही छीन लिया गया है, तो हम सिर्फ एक सादे स्ट्रिंग प्राप्त करते हैं।
  • यदि तर्क टेम्पलेट चर था, तो हमारा फ़ंक्शन चर का वर्तमान मान पारित किया गया है, न कि चर।

अन्य टैग उपयोगिताओं के विपरीत, simple_tag अपना आउटपुट simple_tag conditional_escape() माध्यम से गुजरता है यदि टेम्पलेट संदर्भ ऑटोटस्केप मोड में है, सही HTML सुनिश्चित करने और आपको XSS कमजोरियों से बचाने के लिए।

यदि अतिरिक्त पलायन वांछित नहीं है, तो आपको django.utils.safestring.mark_safe() का उपयोग करने की आवश्यकता होगी यदि आप पूरी तरह से सुनिश्चित हैं कि आपके कोड में XSS कमजोरियां नहीं हैं। छोटे HTML स्निपेट्स के निर्माण के लिए, format_html() बजाय format_html() उपयोग की जोरदार सिफारिश की जाती है।

यदि आपके टेम्प्लेट टैग को वर्तमान संदर्भ तक पहुंचने की आवश्यकता है, तो आप अपने टैग को पंजीकृत करते समय takes_context तर्क का उपयोग कर सकते हैं:

@register.simple_tag(takes_context=True)
def current_time(context, format_string):
    timezone = context['timezone']
    return your_get_current_time_method(timezone, format_string)

ध्यान दें कि पहले तर्क को context कहा जाना चाहिए।

takes_context विकल्प कैसे काम करता है, इसके बारे में अधिक जानकारी के लिए समावेश टैग पर अनुभाग देखें।

यदि आपको अपना टैग नाम बदलने की आवश्यकता है, तो आप इसके लिए एक कस्टम नाम प्रदान कर सकते हैं:

register.simple_tag(lambda x: x - 1, name='minusone')

@register.simple_tag(name='minustwo')
def some_function(value):
    return value - 2

simple_tag फ़ंक्शंस किसी भी संख्या में स्थितीय या कीवर्ड तर्क स्वीकार कर सकते हैं। उदाहरण के लिए:

@register.simple_tag
def my_tag(a, b, *args, **kwargs):
    warning = kwargs['warning']
    profile = kwargs['profile']
    ...
    return ...

फिर टेम्पलेट में रिक्त स्थान द्वारा अलग किए गए किसी भी तर्क को टेम्पलेट टैग में पारित किया जा सकता है। पाइथन की तरह, कीवर्ड तर्क के लिए मान समान चिह्न (" = ") का उपयोग करके सेट किए गए हैं और पोजिशनिंग तर्क के बाद प्रदान किए जाने चाहिए। उदाहरण के लिए:

{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

टेम्प्लेट चर में टैग परिणामों को सीधे आउटपुट करने के बजाय इसे स्टोर करना संभव है। यह चर नाम के बाद तर्क के as उपयोग किया जाता है। ऐसा करने से आप अपने आप को उस सामग्री का उत्पादन करने में सक्षम बनाते हैं जहां आप फिट हैं

{% current_time "%Y-%m-%d %I:%M %p" as the_time %}
<p>The time is {{ the_time }}.</p>

समावेश टैग

django.template.Library.inclusion_tag()

एक अन्य सामान्य प्रकार का टेम्प्लेट टैग वह प्रकार है जो किसी अन्य टेम्प्लेट को रेंडर करके कुछ डेटा प्रदर्शित करता है। उदाहरण के लिए, Django के व्यवस्थापक इंटरफ़ेस कस्टम टेम्पलेट टैग का उपयोग "ऐड / चेंज" फॉर्म पेजों के नीचे बटन दिखाने के लिए करते हैं। वे बटन हमेशा समान दिखते हैं, लेकिन लिंक किए गए ऑब्जेक्ट के आधार पर लिंक लक्ष्य बदल जाते हैं - इसलिए वे एक छोटे टेम्पलेट का उपयोग करने के लिए एक सही मामला है जो वर्तमान ऑब्जेक्ट के विवरण से भरा हुआ है। (व्यवस्थापक के मामले में, यह submit_row टैग है।)

इस प्रकार के टैग "समावेश टैग" कहलाते हैं।

समावेश टैग लिखना संभवतः सबसे अच्छा उदाहरण द्वारा प्रदर्शित किया जाता है। आइए एक टैग लिखें जो किसी दिए गए Poll ऑब्जेक्ट के लिए विकल्पों की सूची को आउटपुट करता है, जैसे कि tutorials में बनाया गया था। हम इस तरह टैग का उपयोग करेंगे:

{% show_results poll %}

... और आउटपुट कुछ इस तरह होगा:

<ul>
  <li>First choice</li>
  <li>Second choice</li>
  <li>Third choice</li>
</ul>

सबसे पहले, उस फ़ंक्शन को परिभाषित करें जो तर्क लेता है और परिणाम के लिए डेटा का शब्दकोश बनाता है। यहां महत्वपूर्ण बिंदु यह है कि हमें केवल एक शब्दकोश वापस करने की आवश्यकता है, न कि अधिक जटिल। यह टेम्पलेट के टुकड़े के लिए एक टेम्पलेट संदर्भ के रूप में उपयोग किया जाएगा। उदाहरण:

def show_results(poll):
    choices = poll.choice_set.all()
    return {'choices': choices}

अगला, टैग के आउटपुट को रेंडर करने के लिए उपयोग किए गए टेम्पलेट को बनाएं। यह टेम्प्लेट टैग की एक निश्चित विशेषता है: टैग लेखक इसे निर्दिष्ट करता है, न कि टेम्प्लेट डिज़ाइनर। हमारे उदाहरण के बाद, टेम्पलेट बहुत सरल है:

<ul>
{% for choice in choices %}
    <li> {{ choice }} </li>
{% endfor %}
</ul>

अब, किसी Library ऑब्जेक्ट पर inclusion_tag() विधि को कॉल करके समावेशन टैग बनाएं और पंजीकृत करें। हमारे उदाहरण के बाद, यदि उपरोक्त टेम्प्लेट एक फ़ाइल में results.html नामक एक निर्देशिका में है जो कि टेम्प्लेट लोडर द्वारा खोजा गया है, तो हम इस पर टैग पंजीकृत करेंगे:

# Here, register is a django.template.Library instance, as before
@register.inclusion_tag('results.html')
def show_results(poll):
    ...

वैकल्पिक रूप से django.template.Template उदाहरण का उपयोग करके समावेश टैग को पंजीकृत करना संभव है:

from django.template.loader import get_template
t = get_template('results.html')
register.inclusion_tag(t)(show_results)

... जब पहली बार समारोह बना रहे हैं।

कभी-कभी, आपके समावेशन टैग में बड़ी संख्या में तर्कों की आवश्यकता हो सकती है, जिससे टेम्पलेट लेखकों को सभी तर्कों में पास होने और उनके आदेश को याद रखने के लिए दर्द होता है। इसे हल करने के लिए, Django समावेश टैग के लिए एक takes_context विकल्प प्रदान करता है। यदि आप टेम्प्लेट टैग बनाने में takes_context को निर्दिष्ट करते हैं, तो टैग में कोई आवश्यक तर्क नहीं होगा, और अंतर्निहित पायथन फ़ंक्शन में एक तर्क होगा - टेम्प्लेट संदर्भ जब टैग कहा जाता था।

उदाहरण के लिए, home_link कि आप एक समावेशन टैग लिख रहे हैं जिसका उपयोग हमेशा ऐसे संदर्भ में किया जाएगा, जिसमें मुख्य पृष्ठ पर वापस इंगित करने वाले home_link और home_title चर हों। यहाँ पायथन फ़ंक्शन कैसा दिखेगा:

@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
    return {
        'link': context['home_link'],
        'title': context['home_title'],
    }

ध्यान दें कि फ़ंक्शन के पहले पैरामीटर को context कहा जाना चाहिए।

उस register.inclusion_tag() लाइन में, हमने निर्दिष्ट किया है takes_context=True और टेम्पलेट का नाम। यहां बताया गया है कि टेम्पलेट link.html कैसा दिख सकता है:

Jump directly to <a href="{{ link }}">{{ title }}</a>.

फिर, किसी भी समय आप उस कस्टम टैग का उपयोग करना चाहते हैं, उसके पुस्तकालय को लोड करते हैं और बिना किसी तर्क के उसे कॉल करते हैं, जैसे:

{% jump_link %}

ध्यान दें कि जब आप takes_context=True का उपयोग कर रहे हों, तो टेम्प्लेट टैग में तर्क पारित करने की कोई आवश्यकता नहीं है। यह स्वचालित रूप से संदर्भ तक पहुँच प्राप्त करता है।

takes_context पैरामीटर False करने के लिए चूक करता है। जब यह True सेट हो जाता है, तो टैग को संदर्भ ऑब्जेक्ट पास कर दिया जाता है, जैसा कि इस उदाहरण में है। इस मामले और पिछले inclusion_tag उदाहरण के बीच केवल यही अंतर है।

inclusion_tag फ़ंक्शन किसी भी स्थिति या कीवर्ड तर्क को स्वीकार कर सकते हैं। उदाहरण के लिए:

@register.inclusion_tag('my_template.html')
def my_tag(a, b, *args, **kwargs):
    warning = kwargs['warning']
    profile = kwargs['profile']
    ...
    return ...

फिर टेम्पलेट में रिक्त स्थान द्वारा अलग किए गए किसी भी तर्क को टेम्पलेट टैग में पारित किया जा सकता है। पाइथन की तरह, कीवर्ड तर्क के लिए मान समान चिह्न (" = ") का उपयोग करके सेट किए गए हैं और पोजिशनिंग तर्क के बाद प्रदान किए जाने चाहिए। उदाहरण के लिए:

{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

उन्नत कस्टम टेम्पलेट टैग

कभी-कभी कस्टम टेम्पलेट टैग निर्माण के लिए बुनियादी सुविधाएँ पर्याप्त नहीं होती हैं। चिंता मत करो, Django आपको जमीन से एक टेम्प्लेट टैग बनाने के लिए आवश्यक इंटर्नल तक पूरी पहुंच प्रदान करता है।

एक त्वरित अवलोकन

टेम्पलेट सिस्टम दो-चरण प्रक्रिया में काम करता है: संकलन और प्रतिपादन। कस्टम टेम्प्लेट टैग को परिभाषित करने के लिए, आप यह निर्दिष्ट करते हैं कि संकलन कैसे काम करता है और प्रतिपादन कैसे काम करता है।

जब Django एक टेम्पलेट संकलित करता है, तो यह कच्चे टेम्पलेट टेक्स्ट को '' नोड्स '' में विभाजित करता है। प्रत्येक नोड django.template.Node का एक उदाहरण है और एक render() विधि है। संकलित टेम्पलेट, बस, Node ऑब्जेक्ट्स की एक सूची है। जब आप एक संकलित टेम्पलेट ऑब्जेक्ट पर render() कॉल करते हैं, तो टेम्प्लेट दिए गए संदर्भ के साथ, इसकी नोड सूची में प्रत्येक Node पर render() कॉल करता है। नतीजे टेम्पलेट के आउटपुट को बनाने के लिए सभी को एक साथ समेटा जाता है।

इस प्रकार, एक कस्टम टेम्पलेट टैग को परिभाषित करने के लिए, आप निर्दिष्ट करते हैं कि कैसे कच्चे टेम्पलेट टैग को Node (संकलन फ़ंक्शन) में परिवर्तित किया जाता है, और नोड का render() विधि क्या करती है।

संकलन समारोह लिखना

प्रत्येक टेम्पलेट के लिए टेम्प्लेट पार्सर मुठभेड़ों को टैग करता है, यह टैग सामग्री और पार्सर ऑब्जेक्ट के साथ पायथन फ़ंक्शन को कॉल करता है। यह फ़ंक्शन टैग की सामग्री के आधार पर Node उदाहरण को वापस करने के लिए जिम्मेदार है।

उदाहरण के लिए, चलिए हमारे सरल टेम्प्लेट टैग का पूर्ण कार्यान्वयन लिखते हैं, {% current_time %} , जो वर्तमान तिथि / समय को प्रदर्शित करता है, टैग में दिए गए पैरामीटर के अनुसार, strftime() वाक्य रचना में। किसी अन्य चीज़ से पहले टैग सिंटैक्स तय करना एक अच्छा विचार है। हमारे मामले में, मान लें कि टैग का उपयोग इस तरह किया जाना चाहिए:

<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>

इस फ़ंक्शन के लिए पार्सर पैरामीटर को पकड़ना चाहिए और एक Node ऑब्जेक्ट बनाना चाहिए:

from django import template

def do_current_time(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, format_string = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError(
            "%r tag requires a single argument" % token.contents.split()[0]
        )
    if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
        raise template.TemplateSyntaxError(
            "%r tag's argument should be in quotes" % tag_name
        )
    return CurrentTimeNode(format_string[1:-1])

टिप्पणियाँ:

  • parser टेम्प्लेट पार्सर ऑब्जेक्ट है। हमें इस उदाहरण में इसकी आवश्यकता नहीं है।
  • token.contents टैग के कच्चे माल की एक स्ट्रिंग है। हमारे उदाहरण में, यह 'current_time "%Y-%m-%d %I:%M %p"'
  • token.split_contents() विधि उद्धृत स्ट्रिंग्स को एक साथ रखते हुए रिक्त स्थान पर तर्कों को अलग करती है। अधिक सीधा token.contents.split() उतना मजबूत नहीं होगा, क्योंकि यह उद्धृत स्ट्रिंग्स के भीतर सभी स्थानों पर समान रूप से विभाजित होगा। हमेशा token.split_contents() उपयोग करना एक अच्छा विचार है।
  • यह फ़ंक्शन किसी भी सिंटैक्स त्रुटि के लिए django.template.TemplateSyntaxError , सहायक संदेशों के साथ बढ़ाने के लिए ज़िम्मेदार है।
  • TemplateSyntaxError अपवाद tag_name चर का उपयोग करते हैं। अपने त्रुटि संदेशों में टैग का नाम हार्ड-कोड न करें, क्योंकि यह आपके फ़ंक्शन के टैग का नाम जोड़े। token.contents.split()[0] होगा '' हमेशा '' आपके टैग का नाम हो - भले ही टैग में कोई तर्क न हो।
  • फ़ंक्शन इस टैग के बारे में जानने के लिए नोड को सब कुछ के साथ एक CurrentTimeNode देता है। इस मामले में, यह सिर्फ तर्क देता है - "%Y-%m-%d %I:%M %p" । टेम्प्लेट टैग से प्रमुख और अनुगामी उद्धरण को format_string[1:-1] में हटा दिया जाता है।
  • पार्सिंग बहुत निम्न स्तर का है। Django डेवलपर्स ने EBNF व्याकरण जैसी तकनीकों का उपयोग करके, इस पार्सिंग सिस्टम के शीर्ष पर छोटे फ्रेमवर्क लिखने के साथ प्रयोग किया है, लेकिन उन प्रयोगों ने टेम्पलेट इंजन को बहुत धीमा कर दिया। यह निम्न-स्तर है क्योंकि यह सबसे तेज़ है।

रेंडर लिखना

कस्टम टैग लिखने में दूसरा चरण एक Node सबक्लास को परिभाषित करना है जिसमें एक render() विधि है।

उपरोक्त उदाहरण को जारी रखते हुए, हमें CurrentTimeNode को परिभाषित करने की आवश्यकता है:

import datetime
from django import template

class CurrentTimeNode(template.Node):
    def __init__(self, format_string):
        self.format_string = format_string

    def render(self, context):
        return datetime.datetime.now().strftime(self.format_string)

टिप्पणियाँ:

  • __init__() do_current_time() से do_current_time() प्राप्त do_current_time() । हमेशा अपने __init__() माध्यम से किसी भी विकल्प / पैरामीटर / तर्कों को एक Node पास करें।
  • render() विधि वह जगह है जहां काम वास्तव में होता है।
  • render() आम तौर पर चुपचाप विफल होना चाहिए, खासकर एक उत्पादन वातावरण में। हालाँकि, कुछ मामलों में, विशेष रूप से यदि context.template.engine.debug True , तो यह विधि डिबगिंग को आसान बनाने के लिए अपवाद को बढ़ा सकती है। उदाहरण के लिए, कई कोर टैग django.template.TemplateSyntaxError यदि वे गलत संख्या या तर्क प्राप्त करते हैं।

अंततः, एक कुशल टेम्पलेट सिस्टम में संकलन और रेंडरिंग के इस डिकम्प्लिंग के परिणामस्वरूप, क्योंकि एक टेम्पलेट कई बार पार्स किए बिना कई संदर्भों को प्रस्तुत कर सकता है।

ऑटो से बचने के विचार

टेम्प्लेट टैग से आउटपुट स्वतः-बचने वाले फ़िल्टर ( simple_tag() के अपवाद के साथ simple_tag() जैसा कि ऊपर वर्णित है) के माध्यम से नहीं चलता है। हालाँकि, अभी भी कुछ चीजें हैं जिन्हें आपको टेम्प्लेट टैग लिखते समय ध्यान में रखना चाहिए।

यदि आपके टेम्प्लेट का render() फ़ंक्शन परिणाम को संदर्भ चर (स्ट्रिंग में परिणाम वापस करने के बजाय render() संग्रहीत करता है, तो यह उचित होने पर mark_safe() को कॉल करने के लिए ध्यान रखना चाहिए। जब चर अंततः प्रदान किया जाता है, तो यह समय पर प्रभाव से ऑटो-एस्केप सेटिंग से प्रभावित होगा, इसलिए ऐसी सामग्री जो आगे भागने से सुरक्षित होनी चाहिए जैसे कि चिह्नित की जानी चाहिए।

इसके अलावा, यदि आपका टेम्प्लेट टैग कुछ उप-प्रतिपादन करने के लिए एक नया संदर्भ बनाता है, तो ऑटो-एस्केप विशेषता को वर्तमान संदर्भ मान पर सेट करें। Context वर्ग के लिए __init__ विधि autoescape नामक एक पैरामीटर लेता है जिसे आप इस उद्देश्य के लिए उपयोग कर सकते हैं। उदाहरण के लिए:

from django.template import Context

def render(self, context):
    # ...
    new_context = Context({'var': obj}, autoescape=context.autoescape)
    # ... Do something with new_context ...

यह एक बहुत ही सामान्य स्थिति नहीं है, लेकिन यह उपयोगी है यदि आप स्वयं एक टेम्पलेट प्रदान कर रहे हैं। उदाहरण के लिए:

def render(self, context):
    t = context.template.engine.get_template('small_fragment.html')
    return t.render(Context({'var': obj}, autoescape=context.autoescape))

यदि हमने इस Context में अपने नए Context में वर्तमान context.autoescape मान को पास करने की उपेक्षा की है, तो परिणाम हमेशा स्वचालित रूप से बच गए होंगे, जो कि वांछित व्यवहार नहीं हो सकता है यदि टेम्पलेट टैग का उपयोग {% autoescape off %} अंदर किया {% autoescape off %} ब्लॉक करें।

धागा-सुरक्षा विचार

एक बार एक नोड पार्स होने के बाद, इसकी render विधि को किसी भी समय कहा जा सकता है। चूंकि Django को कभी-कभी बहु-थ्रेडेड वातावरण में चलाया जाता है, इसलिए एक एकल नोड एक साथ दो अलग-अलग अनुरोधों के जवाब में विभिन्न संदर्भों के साथ प्रदान कर सकता है। इसलिए, यह सुनिश्चित करना महत्वपूर्ण है कि आपके टेम्पलेट टैग थ्रेड सुरक्षित हैं।

यह सुनिश्चित करने के लिए कि आपके टेम्पलेट टैग थ्रेड सुरक्षित हैं, आपको कभी भी नोड पर राज्य की जानकारी संग्रहीत नहीं करनी चाहिए। उदाहरण के लिए, Django एक अंतर्निहित cycle टेम्प्लेट टैग प्रदान करता है जो हर बार दिए गए तार की सूची के बीच चक्र करता है:

{% for o in some_list %}
    <tr class="{% cycle 'row1' 'row2' %}">
        ...
    </tr>
{% endfor %}

CycleNode का एक भोली कार्यान्वयन कुछ इस तरह CycleNode सकता है:

import itertools
from django import template

class CycleNode(template.Node):
    def __init__(self, cyclevars):
        self.cycle_iter = itertools.cycle(cyclevars)

    def render(self, context):
        return next(self.cycle_iter)

लेकिन, मान लें कि हमारे पास एक ही समय में ऊपर से टेम्पलेट स्निपेट प्रदान करने वाले दो टेम्पलेट हैं:

  1. थ्रेड 1 अपना पहला लूप पुनरावृत्ति करता है, CycleNode.render() रिटर्न 'row1'
  2. थ्रेड 2 अपना पहला लूप पुनरावृत्ति करता है, CycleNode.render() रिटर्न 'row2'
  3. थ्रेड 1 अपना दूसरा लूप पुनरावृत्ति करता है, CycleNode.render() रिटर्न 'row1'
  4. थ्रेड 2 अपना दूसरा लूप पुनरावृत्ति करता है, CycleNode.render() रिटर्न 'row2'

CycleNode iterating है, लेकिन यह विश्व स्तर पर पुनरावृत्ति कर रहा है। जहां तक ​​थ्रेड 1 और थ्रेड 2 का संबंध है, यह हमेशा एक ही मूल्य पर लौट रहा है। यह स्पष्ट रूप से नहीं है कि हम क्या चाहते हैं!

इस समस्या को हल करने के लिए, Django एक render_context प्रदान करता है जो वर्तमान में प्रस्तुत किए जा रहे टेम्पलेट के context से जुड़ा हुआ है। render_context एक पायथन डिक्शनरी की तरह व्यवहार करता है, और render विधि के इनवोकेशन के बीच Node स्टेट को स्टोर करने के लिए उपयोग किया जाना चाहिए।

CycleNode का उपयोग करने के लिए हमारे CycleNode कार्यान्वयन को render_context :

class CycleNode(template.Node):
    def __init__(self, cyclevars):
        self.cyclevars = cyclevars

    def render(self, context):
        if self not in context.render_context:
            context.render_context[self] = itertools.cycle(self.cyclevars)
        cycle_iter = context.render_context[self]
        return next(cycle_iter)

ध्यान दें कि यह वैश्विक जानकारी संग्रहीत करने के लिए पूरी तरह से सुरक्षित है जो एक विशेषता के रूप में Node के जीवन भर में नहीं बदलेगी। CycleNode के मामले में, CycleNode के cyclevars चालू होने के बाद, cyclevars तर्क नहीं बदलता है, इसलिए हमें इसे render_context में डालने की आवश्यकता नहीं है। लेकिन राज्य की जानकारी जो उस टेम्पलेट के लिए विशिष्ट है जो वर्तमान में रेंडर की जा रही है, जैसे CycleNode की वर्तमान पुनरावृत्ति, को CycleNode में संग्रहीत किया जाना चाहिए।

ध्यान दें

सूचना दें कि हमने CycleNode भीतर CycleNode विशिष्ट जानकारी को स्कोप करने के लिए self का उपयोग कैसे किया। किसी दिए गए टेम्प्लेट में कई CycleNodes हो सकते हैं, इसलिए हमें दूसरे नोड की स्थिति की जानकारी को ध्यान में नहीं रखने की आवश्यकता है। ऐसा करने का सबसे आसान तरीका है कि हमेशा self को render_context की कुंजी के रूप में उपयोग करें। यदि आप कई राज्य चरों का ट्रैक रख रहे हैं, तो render_context[self] एक शब्दकोश render_context[self]

टैग पंजीकृत करना

अंत में, अपने मॉड्यूल के Library इंस्टेंस के साथ टैग को पंजीकृत करें, जैसा कि ऊपर कस्टम टेम्पलेट फ़िल्टर लिखने में समझाया गया है। उदाहरण:

register.tag('current_time', do_current_time)

tag() विधि में दो तर्क हैं:

  1. टेम्पलेट टैग का नाम - एक स्ट्रिंग। यदि इसे छोड़ दिया जाता है, तो संकलन फ़ंक्शन का नाम उपयोग किया जाएगा।
  2. संकलन समारोह - एक पायथन फ़ंक्शन (स्ट्रिंग के रूप में फ़ंक्शन का नाम नहीं)।

फ़िल्टर पंजीकरण के साथ, इसे डेकोरेटर के रूप में उपयोग करना भी संभव है:

@register.tag(name="current_time")
def do_current_time(parser, token):
    ...

@register.tag
def shout(parser, token):
    ...

यदि आप name तर्क को छोड़ देते हैं , जैसा कि ऊपर दिए गए दूसरे उदाहरण में, Django टैग के नाम के रूप में फ़ंक्शन के नाम का उपयोग करेगा।

टैग के लिए टेम्प्लेट वैरिएबल पास करना

हालाँकि आप किसी भी संख्या के तर्कों का उपयोग करके टेम्प्लेट टैग में पास कर सकते हैं token.split_contents() , तर्कों को स्ट्रिंग शाब्दिक के रूप में अनपैक किया गया है। एक तर्क के रूप में एक टेम्पलेट टैग के लिए गतिशील सामग्री (एक टेम्पलेट चर) को पारित करने के लिए थोड़ा और काम आवश्यक है।

जबकि पिछले उदाहरणों ने वर्तमान समय को एक स्ट्रिंग में स्वरूपित किया है और स्ट्रिंग को वापस कर दिया है, मान लीजिए कि आप DateTimeField किसी ऑब्जेक्ट से पास होना चाहते हैं और उस तारीख के समय का टेम्प्लेट टैग प्रारूप है:

<p>This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.</p>

प्रारंभ में, token.split_contents() तीन मान लौटाएंगे:

  1. टैग का नाम format_time
  2. स्ट्रिंग 'blog_entry.date_updated' (आसपास के उद्धरण के बिना)।
  3. स्वरूपण स्ट्रिंग '"%Y-%m-%d %I:%M %p"' । से वापसी मूल्य में split_contents() इस तरह स्ट्रिंग शाब्दिक के लिए अग्रणी और अनुगामी उद्धरण शामिल होंगे।

अब आपका टैग इस तरह दिखना शुरू होना चाहिए:

from django import template

def do_format_time(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, date_to_be_formatted, format_string = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError(
            "%r tag requires exactly two arguments" % token.contents.split()[0]
        )
    if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
        raise template.TemplateSyntaxError(
            "%r tag's argument should be in quotes" % tag_name
        )
    return FormatTimeNode(date_to_be_formatted, format_string[1:-1])

आपको ऑब्जेक्ट की date_updated संपत्ति की वास्तविक सामग्री को पुनः प्राप्त करने के लिए रेंडरर को भी बदलना होगा blog_entry । इसमें Variable() कक्षा का उपयोग करके इसे पूरा किया जा सकता है django.template

Variable वर्ग का उपयोग करने के लिए , बस इसे हल करने के लिए चर के नाम के साथ त्वरित करें, और फिर कॉल करें variable.resolve(context) । इसलिए, उदाहरण के लिए:

class FormatTimeNode(template.Node):
    def __init__(self, date_to_be_formatted, format_string):
        self.date_to_be_formatted = template.Variable(date_to_be_formatted)
        self.format_string = format_string

    def render(self, context):
        try:
            actual_date = self.date_to_be_formatted.resolve(context)
            return actual_date.strftime(self.format_string)
        except template.VariableDoesNotExist:
            return ''

वैरिएबल रिज़ॉल्यूशन एक VariableDoesNotExist अपवाद को फेंक देगा यदि यह पृष्ठ के वर्तमान संदर्भ में इसे पास किए गए स्ट्रिंग को हल नहीं कर सकता है।

संदर्भ में एक चर सेट करना

उपरोक्त उदाहरण बस एक मूल्य का उत्पादन करते हैं। आम तौर पर, यह अधिक लचीला होता है यदि आपके टेम्प्लेट टैग आउटपुट मान के बजाय टेम्प्लेट चर सेट करते हैं। इस तरह, टेम्प्लेट लेखक उन मानों का पुन: उपयोग कर सकते हैं जो आपके टेम्प्लेट टैग बनाते हैं।

संदर्भ में एक चर सेट करने के लिए, render() विधि में संदर्भ वस्तु पर केवल शब्दकोश असाइनमेंट का उपयोग करें । यहां इसका अपडेट किया गया संस्करण इसे आउटपुट करने के बजाय CurrentTimeNode एक टेम्प्लेट चर सेट करता current_time है:

import datetime
from django import template

class CurrentTimeNode2(template.Node):
    def __init__(self, format_string):
        self.format_string = format_string
    def render(self, context):
        context['current_time'] = datetime.datetime.now().strftime(self.format_string)
        return ''

ध्यान दें कि render() खाली स्ट्रिंग लौटाता है। render() हमेशा स्ट्रिंग आउटपुट वापस करना चाहिए। यदि सभी टेम्प्लेट टैग एक चर सेट करते हैं, render() तो खाली स्ट्रिंग को वापस करना चाहिए।

आप टैग के इस नए संस्करण का उपयोग कैसे करेंगे:

{% current_time "%Y-%M-%d %I:%M %p" %}<p>The time is {{ current_time }}.</p>

संदर्भ में परिवर्तनशील गुंजाइश

संदर्भ में निर्धारित कोई भी चर केवल उसी block टेम्पलेट में उपलब्ध होगा जिसमें इसे सौंपा गया था। यह व्यवहार जानबूझकर है; यह चर के लिए एक गुंजाइश प्रदान करता है ताकि वे अन्य ब्लॉकों में संदर्भ के साथ संघर्ष न करें।

लेकिन, इसमें एक समस्या है CurrentTimeNode2 : चर नाम current_time हार्ड-कोडेड है। इसका मतलब है कि आपको यह सुनिश्चित करने की आवश्यकता होगी कि आपका टेम्पलेट {{ current_time }} कहीं और उपयोग न करें , क्योंकि {% current_time %} इच्छाशक्ति उस चर के मूल्य को नेत्रहीन रूप से अधिलेखित कर देगी। एक क्लीनर समाधान टेम्पलेट टैग आउटपुट चर के नाम को निर्दिष्ट करने के लिए है, जैसे:

{% current_time "%Y-%M-%d %I:%M %p" as my_current_time %}
<p>The current time is {{ my_current_time }}.</p>

ऐसा करने के लिए, आपको संकलन समारोह और Node कक्षा, दोनों को रिफैक्ट करने की आवश्यकता होगी , जैसे:

import re

class CurrentTimeNode3(template.Node):
    def __init__(self, format_string, var_name):
        self.format_string = format_string
        self.var_name = var_name
    def render(self, context):
        context[self.var_name] = datetime.datetime.now().strftime(self.format_string)
        return ''

def do_current_time(parser, token):
    # This version uses a regular expression to parse tag contents.
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError(
            "%r tag requires arguments" % token.contents.split()[0]
        )
    m = re.search(r'(.*?) as (\w+)', arg)
    if not m:
        raise template.TemplateSyntaxError("%r tag had invalid arguments" % tag_name)
    format_string, var_name = m.groups()
    if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
        raise template.TemplateSyntaxError(
            "%r tag's argument should be in quotes" % tag_name
        )
    return CurrentTimeNode3(format_string[1:-1], var_name)

यहां अंतर यह है कि do_current_time() प्रारूप स्ट्रिंग और चर नाम को पकड़ लेता है, दोनों को पास करना CurrentTimeNode3

अंत में, यदि आपको केवल अपने कस्टम संदर्भ-अद्यतन करने वाले टेम्प्लेट टैग के लिए एक सरल वाक्यविन्यास करने की आवश्यकता है, तो simple_tag() शॉर्टकट का उपयोग करने पर विचार करें , जो टेम्प्लेट चर के लिए टैग परिणामों को निर्दिष्ट करने का समर्थन करता है।

एक और ब्लॉक टैग तक पार्सिंग

टेम्पलेट टैग मिलकर काम कर सकते हैं। उदाहरण के लिए, मानक {% comment %} टैग तब तक सब कुछ छुपाता है {% endcomment %} । इस तरह एक टेम्प्लेट टैग बनाने के लिए parser.parse() , अपने संकलन फ़ंक्शन में उपयोग करें।

यहां बताया गया है कि एक सरलीकृत {% comment %} टैग कैसे लागू किया जा सकता है:

def do_comment(parser, token):
    nodelist = parser.parse(('endcomment',))
    parser.delete_first_token()
    return CommentNode()

class CommentNode(template.Node):
    def render(self, context):
        return ''

ध्यान दें

के वास्तविक क्रियान्वयन {% comment %} में यह टूटा टेम्पलेट टैग के बीच प्रकट करने के लिए अनुमति देता है कि थोड़ा अलग है {% comment %} और {% endcomment %} । ऐसा parser.skip_past('endcomment') करने के बजाय कॉल करने से ऐसा होता parser.parse(('endcomment',)) है parser.delete_first_token() , इस प्रकार एक नोड सूची की पीढ़ी से बचा जाता है।

parser.parse() ब्लॉक टैग के नामों का एक समूह लेता है '' जब तक '' को पार्स करने के लिए ''। यह एक उदाहरण देता है django.template.NodeList , जो उन सभी Node वस्तुओं की एक सूची है जो पार्सर ने '' '' से पहले '' का सामना किया था, यह किसी भी नाम से टपल में पाया गया था।

में "nodelist = parser.parse(('endcomment',))" ऊपर के उदाहरण में, nodelist के बीच सभी नोड्स की एक सूची है {% comment %} और {% endcomment %} , इसमें शामिल नहीं होते {% comment %} और {% endcomment %} खुद को।

parser.parse() कहा जाता है के बाद , पार्सर ने अभी तक {% endcomment %} टैग को "भस्म" नहीं किया है , इसलिए कोड को स्पष्ट रूप से कॉल करने की आवश्यकता है parser.delete_first_token()

CommentNode.render() बस एक खाली स्ट्रिंग देता है। के बीच कुछ भी {% comment %} और {% endcomment %} नजरअंदाज कर दिया है।

एक और ब्लॉक टैग तक पार्सिंग, और सामग्री सहेजना

पिछले उदाहरण में, के do_comment() बीच सब कुछ त्याग दिया {% comment %} और {% endcomment %} । ऐसा करने के बजाय, ब्लॉक टैग के बीच कोड के साथ कुछ करना संभव है।

उदाहरण के लिए, यहां एक कस्टम टेम्प्लेट टैग है, {% upper %} जो अपने आप में सब कुछ कैपिटल करता है और {% endupper %}

उपयोग:

{% upper %}This will appear in uppercase, {{ your_name }}.{% endupper %}

पिछले उदाहरण के अनुसार, हम उपयोग करेंगे parser.parse() । लेकिन इस बार, हम परिणाम nodelist को पास करते हैं Node :

def do_upper(parser, token):
    nodelist = parser.parse(('endupper',))
    parser.delete_first_token()
    return UpperNode(nodelist)

class UpperNode(template.Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist
    def render(self, context):
        output = self.nodelist.render(context)
        return output.upper()

यहाँ केवल नई अवधारणा है self.nodelist.render(context) में UpperNode.render()

जटिल प्रतिपादन का अधिक उदाहरण के लिए, के स्रोत कोड को देखने {% for %} में django/template/defaulttags.py और {% if %} में django/template/smartif.py

Original text