- 建立tag方式,首先在須要使用tag的app下建立一個templatetags的python包,
- 而後在包裏建立一個tag模塊,例如hellotag.py
from django import template register = template.Library() # 注意,這裏的變量名必定是register,可不是任意的名稱 @ register.simple_tag def hello(*args): return "hello " + " ".join(str(args))
而後就能夠在須要使用tag的頁面引用自定義tag了html
{% load hellotag %} {% hello "world !" %}
而後須要重啓Django服務,再經過頁面測試結果python
- 爲何只能是register,由於Django源碼裏就是這麼解析的
def get_package_libraries(pkg): """ Recursively yield template tag libraries defined in submodules of a package. """ for entry in walk_packages(pkg.__path__, pkg.__name__ + '.'): try: module = import_module(entry[1]) except ImportError as e: raise InvalidTemplateLibrary( "Invalid template library specified. ImportError raised when " "trying to load '%s': %s" % (entry[1], e) ) if hasattr(module, 'register'): yield entry[1]
- 爲何須要重啓tag才生效?由於Django在第一次啓動時就將一些不易變更的模塊代碼使用緩存方式的裝飾器將這些模塊緩存起來了,這樣方便加快響應速度,而不是每次返回給用戶頁面時都從新加載一次tag模塊,那樣每一個用戶每訪問一個頁面,都從新導入一次響應確定會很慢
- Django默認會加載如下tag模塊
'django.template.defaulttags', 'django.template.defaultfilters', 'django.template.loader_tags',
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'cloud.api.libs.context_processors.common_processor', ], 'autoescape': True, 'debug': settings.DEBUG, # 該值就是咱們自定義的settings模塊的debug變量值。 'file_charset': settings.FILE_CHARSET, # 這個settings是從全局的Django settings導入的,默認爲"utf-8" 'libraries': {} # 這裏是字典類型的tag模塊名稱,例如:項目cloud目錄下有一個show.py tag模塊,那麼完整的模塊路徑爲cloud.show,定義到這個字典的格式爲{'show': 'cloud.show'} }, }, ]
context_processors
之外,其他值均可以不寫,默認自帶的配置已經有context_processors
了,若是須要自定義其它位置的tag模塊,能夠添加libraries
這個選項
- render處理過程
# view方法經過render方法返回模版,如return render(request, 'index.html', context=None, content_type=None, status=200, using=None) # 首先調用render方法,傳入參數 # render再調用render_to_string方法去獲取模版,並渲染模版,返回一個字符串 # 咱們假設只傳了一個模版文件的狀況,那樣的話render_to_string就會調用get_template方法獲取指定的模版文件 # get_template 首先經過_engine_list獲取到模版引擎 # _engine_list經過EngineHandler的實例化對象獲得settings.TEMPLATES的全部模版引擎相關的信息 # EngineHandler在程序啓動時就將模版引擎緩存起來了,不過這裏仍是先說下它的過程,因爲對象自己實現了iter方法,全部咱們對數據進行迭代時,會觸發該方法 # 而後找到對象的templates屬性,該屬性經過獲取settings.TEMPLATES這個列表形式的模版引擎信息,能夠有多個模版引擎,每個引擎都是字典形式存在,且必須 #包含那些KEY,並保存一個副本,將每一個引擎的`BACKEND`KEY值以點形式從右開始分割2次,取倒數第二個元素爲引擎的名稱,這樣默認的引擎,名稱就是django。 # 而後設置引擎的NAME、DIRS、APP_DIRS、OPTIONS的值,最終全部的模版引擎以字典形式返回,KEY爲引擎的名稱 # _engine_list調用EngineHandler對象的all方法,all方法經過列表推導遍歷self對象自己,也就是在調用__iter__方法,並使用self[alias]這種語法糖形式, # 實際調用的就是self自己實現的__getitem__方法,將值取出來,每一個alias就是上面所分析的全部以KEY方法存到字典的模版引擎,經過__getitem__傳入該KEY,再調用 # self.templates[alias]方式,self.templates自己返回的是一個有序字典,全部能夠這麼取字典的值。經過這樣就能夠獲得每一個模版引擎的信息。__getitem__每 # 獲取到一個引擎信息時,都將BACKEND對應的值的字符串形式的的模塊名經過import_string方法導入該具體的模塊,並將獲得的模塊返回,這裏的默認引擎爲 # django.template.backends.django.DjangoTemplates,import_string導入時會先導入模塊,再獲取模塊的屬性DjangoTemplates,而後返回該屬性。 # 接着__getitem__經過獲得的DjangoTemplates,進行實例化,並把這個引擎相關的信息參數形式傳入。 # _engine_list獲得的就是一個包含全部模版引擎的對象,這裏實際上就是DjangoTemplates對象, # get_template在遍歷_engine_list返回的每個引擎,而後調用引擎的get_template方法,並把模版名稱傳入。 # DjangoTemplates實例化時,會將全部APP下的templatetags模塊下的tag加載。每一個tag必須包含`register`這個屬性,也就是你定義tag裝飾器時,取的名稱必須 # 是register = Library(),名字不能隨意,不然不會加載這種tag, # 這些tag都賦值給libraries每一個模版的引擎的option的key值。經過settings也能夠手動指定其它位置的tag模塊, # 還將這些參數傳遞給Engine進行實例化,還記得平時咱們的模版不引用tag也有一些默認tag可用麼,其實就是這時導入了一些默認的tag,並賦值給對象的template_builtins。 # 默認的default_builtins = [ # 'django.template.defaulttags', # 'django.template.defaultfilters', # 'django.template.loader_tags', # ] def render(request, template_name, context=None, content_type=None, status=None, using=None): content = render_to_string(template_name, context, request, using=using) return HttpResponse(content, content_type, status) def render_to_string(template_name, context=None, request=None, using=None): """ Loads a template and renders it with a context. Returns a string. template_name may be a string or a list of strings. """ if isinstance(template_name, (list, tuple)): template = select_template(template_name, using=using) else: template = get_template(template_name, using=using) return template.render(context, request) def get_template(template_name, using=None): """ Loads and returns a template for the given name. Raises TemplateDoesNotExist if no such template exists. """ chain = [] engines = _engine_list(using) for engine in engines: try: return engine.get_template(template_name) except TemplateDoesNotExist as e: chain.append(e) raise TemplateDoesNotExist(template_name, chain=chain) def _engine_list(using=None): return engines.all() if using is None else [engines[using]] class EngineHandler(object): def __init__(self, templates=None): """ templates is an optional list of template engine definitions (structured like settings.TEMPLATES). """ self._templates = templates self._engines = {} @cached_property def templates(self): if self._templates is None: self._templates = settings.TEMPLATES templates = OrderedDict() backend_names = [] for tpl in self._templates: tpl = tpl.copy() try: # This will raise an exception if 'BACKEND' doesn't exist or # isn't a string containing at least one dot. default_name = tpl['BACKEND'].rsplit('.', 2)[-2] except Exception: invalid_backend = tpl.get('BACKEND', '<not defined>') raise ImproperlyConfigured( "Invalid BACKEND for a template engine: {}. Check " "your TEMPLATES setting.".format(invalid_backend)) tpl.setdefault('NAME', default_name) tpl.setdefault('DIRS', []) tpl.setdefault('APP_DIRS', False) tpl.setdefault('OPTIONS', {}) templates[tpl['NAME']] = tpl backend_names.append(tpl['NAME']) counts = Counter(backend_names) duplicates = [alias for alias, count in counts.most_common() if count > 1] if duplicates: raise ImproperlyConfigured( "Template engine aliases aren't unique, duplicates: {}. " "Set a unique NAME for each engine in settings.TEMPLATES." .format(", ".join(duplicates))) return templates def __getitem__(self, alias): try: return self._engines[alias] except KeyError: try: params = self.templates[alias] except KeyError: raise InvalidTemplateEngineError( "Could not find config for '{}' " "in settings.TEMPLATES".format(alias)) # If importing or initializing the backend raises an exception, # self._engines[alias] isn't set and this code may get executed # again, so we must preserve the original params. See #24265. params = params.copy() backend = params.pop('BACKEND') engine_cls = import_string(backend) engine = engine_cls(params) self._engines[alias] = engine return engine def __iter__(self): return iter(self.templates) def all(self): return [self[alias] for alias in self] engines = EngineHandler() class DjangoTemplates(BaseEngine): app_dirname = 'templates' def __init__(self, params): params = params.copy() options = params.pop('OPTIONS').copy() options.setdefault('autoescape', True) options.setdefault('debug', settings.DEBUG) options.setdefault('file_charset', settings.FILE_CHARSET) libraries = options.get('libraries', {}) options['libraries'] = self.get_templatetag_libraries(libraries) super(DjangoTemplates, self).__init__(params) self.engine = Engine(self.dirs, self.app_dirs, **options) def from_string(self, template_code): return Template(self.engine.from_string(template_code), self) def get_template(self, template_name): try: return Template(self.engine.get_template(template_name), self) except TemplateDoesNotExist as exc: reraise(exc, self) def get_templatetag_libraries(self, custom_libraries): """ Return a collation of template tag libraries from installed applications and the supplied custom_libraries argument. """ libraries = get_installed_libraries() libraries.update(custom_libraries) return libraries