自定义过滤器及标签

上节知识复习

前后端合并—-加快软件开发项目流程—-》学习成套知识

常用的模板标签

{% %}标签的语法

基本原则:有开始一定有结束【标签成对出现】

  1. {%if%}
  2. {%endif%}
  3. if .... else
  4. forloop
  5. {%load%}
  6. {%endload%}
  7. {%url 'index_05'%}

模板继承的意义

  1. 方便代码复用,使用继承模板的方式
  2. 继承时,使用标签[extends]

使用模板中的内容

block.super()

调用关系描述

在浏览器中,发出特定request请求,从项目中设置文件指定的urls文件中,匹配URL规则,调用views视图函数,由视图调用HTML文件,该文件中包含自定义过滤器以及指定标签扩展,从标签中调用模板标签,读取自定义过滤器

文件路径配置

【重点】创建自定义模板的步骤

1,在项目文件目录下创建名为common的包(即目录中含有init.py)

2,将common加入到settings文件中

3,在common里面创建目录templates,在目录里面创建自定义过滤器及标签文件

4, 在common_extras.py文件中自定义cut和lower的过滤器功能

  1. def mycut(value, arg):
  2. return value.replace(arg, '')
  3. def mylower(value):
  4. return value.lower()

自定义过滤器

概念:自定义过滤器就是一个带有一个或者两个参数的python函数;

  1. (输入的)变量的值 --- 不一定是字符串形式;
  2. 参数的值 --- 可以有一个初始值,或者完全不要这个参数

示例

  1. {{var|fool:"bar"}} # var:变量; fool:自定义过滤器的名字;"bar":传给变量的值

功能实现

在url访问中调用对应的视图函数,然后从视图函数的返回中我们可以看到:自定义一个字符串,以上下文的方式传递给H5文件,由H5文件处理

  1. def common(request):
  2. ts = 'YKK seasame'
  3. return render(request, 'loads.html', context={
  4. 'ts': ts, # 上下文中ts的名字需要完全保持一致
  5. })

进入H5之后,由自定义过滤器处理传入的字符串,字符串是用参数ts传入的,因此我们在H5中书写时必须使用ts作为参数,才有对象处理

  1. {% load common_extras %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>测试自定义过滤器及标签</title>
  7. </head>
  8. <body>
  9. <p>展示自定义过滤器---转为小写</p>
  10. {{somevariable|mylower}}<br>
  11. <p>展示自定义过滤器---删除空格</p>
  12. {{somevariable|mycut:''}}<br>
  13. </body>
  14. </html>

注册自定义过滤器

【语法】

  1. django.template.Library.filter()
  2. 1.Library.filter()方法需要两个参数:
  3. a. 过滤器的名称(一个字符串对象)
  4. b. 编译的函数 一个Python函数(不要把函数名写成字符串)
  5. 2.可以把register.filter()用作装饰器;
  6. 3.没有声明 name 参数,Django将使用函数名作为过滤器的名字。

【总结】过滤器及自定义标签格式解读

形式: @函数名

作用:在不改变原有代码逻辑的情况下,增加功能

自定义装饰器的方法

1 filter()

第一个参数:self是类对象的名字(Library对象)

第二个参数:name = None 返回的是函数的名字

【源码】—- 装饰器filter

  1. def filter(self, name=None, filter_func=None, **flags):
  2. """
  3. Register a callable as a template filter. Example: # 注册一个可以调用的过滤器
  4. @register.filter
  5. def lower(value): # 需要为自定义函数传递参数
  6. return value.lower()
  7. """
  8. if name is None and filter_func is None: # 如果没有为过滤器传入名字和被装饰的函数的名字
  9. # @register.filter()
  10. def dec(func):
  11. return self.filter_function(func, **flags)
  12. return dec # 返回被装饰函数的名字
  13. elif name is not None and filter_func is None:
  14. if callable(name):
  15. # @register.filter
  16. return self.filter_function(name, **flags)
  17. else:
  18. # @register.filter('somename') or @register.filter(name='somename')
  19. def dec(func):
  20. return self.filter(name, func, **flags)
  21. return dec
  22. elif name is not None and filter_func is not None:
  23. # register.filter('somename', somefunc)
  24. self.filters[name] = filter_func
  25. for attr in ('expects_localtime', 'is_safe', 'needs_autoescape'):
  26. if attr in flags:
  27. value = flags[attr]
  28. # set the flag on the filter for FilterExpression.resolve
  29. setattr(filter_func, attr, value)
  30. # set the flag on the innermost decorated function
  31. # for decorators that need it, e.g. stringfilter
  32. if hasattr(filter_func, "_decorated_function"):
  33. setattr(filter_func._decorated_function, attr, value)
  34. filter_func._filter_name = name
  35. return filter_func
  36. else:
  37. raise ValueError(
  38. "Unsupported arguments to Library.filter: (%r, %r)" %
  39. (name, filter_func),
  40. )
  41. def filter_function(self, func, **flags):
  42. name = getattr(func, "_decorated_function", func).__name__
  43. return self.filter(name, func, **flags)

2 simple_tag

第一个参数:self是Library对象

第二个参数: func这个是函数

第三个参数:take_context获取上下文

【源码】

  1. def simple_tag(self, func=None, takes_context=None, name=None):
  2. """
  3. Register a callable as a compiled template tag. Example:
  4. @register.simple_tag
  5. def hello(*args, **kwargs):
  6. return 'world'
  7. """
  8. def dec(func):
  9. params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(func)
  10. function_name = (name or getattr(func, '_decorated_function', func).__name__)
  11. @functools.wraps(func)
  12. def compile_func(parser, token):
  13. bits = token.split_contents()[1:]
  14. target_var = None
  15. if len(bits) >= 2 and bits[-2] == 'as':
  16. target_var = bits[-1]
  17. bits = bits[:-2]
  18. args, kwargs = parse_bits(
  19. parser, bits, params, varargs, varkw, defaults,
  20. kwonly, kwonly_defaults, takes_context, function_name,
  21. )
  22. return SimpleNode(func, takes_context, args, kwargs, target_var)
  23. self.tag(function_name, compile_func)
  24. return func
  25. if func is None:
  26. # @register.simple_tag(...)
  27. return dec
  28. elif callable(func):
  29. # @register.simple_tag
  30. return dec(func)
  31. else:
  32. raise ValueError("Invalid arguments provided to simple_tag")

【业务代码】

  1. from django import template
  2. register = template.library()
  3. def mycut(value, arg):
  4. return value.replace(arg, '')
  5. register.filter('mycut', mycut)
  6. @register.filter
  7. def mylower(value):
  8. return value.lower()

【在模板中使用自定义的过滤器】

需要使用{%load%}标签将我们自定义模块加载进来,{%load%}声明将会载入给定模块名中的标签/过滤器

  1. {%load common_extras%}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>自定义过滤器及标签</title>
  7. </head>
  8. <body>
  9. {{ts|mylower}}<br>
  10. {{ts|mycut:''}}<br>
  11. </body>
  12. </html>

【代码演示】

在book/views.py文件中定义类—-调用HTML网页文件

【自定义过滤器前端效果】

【show_tag()标签的使用】

业务逻辑

  1. 在与views.py同级目录下创建文件夹templatetags,然后在其中创建init.py便于将此文件夹视为一个函数包,然后再创建一个customtag.py文件,用于在此处自定义标签和过滤器
  2. 首先在customtag中导入template.Library()便于对标签进行函数实例注册
  3. 然后就可以在该文件中自定义标签和装饰器
  4. 访问逻辑
  5. 首先从浏览器中输入正确的URL,输入值与项目urlpattern匹配
  6. 然后进入views.py文件中调用合适的类
  7. 在类中返回特定HTML5网页渲染
  8. 在HTML5文件中首先引入模板标签和过滤器,然后以指定标签格式调用已经定义好的模板标签和过滤器
    代码含义:
    参考与引用
    1. @register.simple_tag(name='current_time1') # 用于向Library注册实例,其后的name就是我们定义的类的名字
    2. def current_time1(format_string): # 用于在网页上展示时间
    3. print(format_string) # 格式化字符串就是我们在H5中定义的现实格式:%Y 年 %m 月 %I 时 %M 分 %p
    4. return datetime.now().strftime(format_string) #

首先在common_extra中创建类,利用return语句返回字典对象

  1. def common(request):
  2. ts = 'YKK seasame'
  3. return render(request, 'loads.html', context={
  4. 'ts': ts,
  5. })

然后在HTML5文件中使用模板标签调用

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>show_tag标签的使用</title>
  6. </head>
  7. <body>
  8. <ul>
  9. {% for i in choices %}
  10. <li>{{ i }}</li>
  11. {% endfor %}
  12. </ul>
  13. </body>
  14. </html>

其次在views视图下创建类,以HTML5为返回对象

  1. def show_tag(request):
  2. return render(request, 'show_tag.html')

inclusion_tag()装饰器的使用

文件路径:Library.py[136]

定义:

  1. def inclusion_tag(self, filename, func=None, takes_context=None, name=None):
  2. """
  3. Register a callable as an inclusion tag:
  4. @register.inclusion_tag('results.html')
  5. def show_results(poll):
  6. choices = poll.choice_set.all()
  7. return {'choices': choices}

【源码阅读】

渲染器的处理逻辑

  1. import functools
  2. import os
  3. from django.conf import settings
  4. from django.template.backends.django import DjangoTemplates
  5. from django.template.loader import get_template
  6. from django.utils.functional import cached_property
  7. from django.utils.module_loading import import_string
  8. try:
  9. from django.template.backends.jinja2 import Jinja2
  10. except ImportError:
  11. def Jinja2(params):
  12. raise ImportError("jinja2 isn't installed")
  13. ROOT = os.path.dirname(__file__)
  14. @functools.lru_cache()
  15. def get_default_renderer():
  16. renderer_class = import_string(settings.FORM_RENDERER)
  17. return renderer_class()
  18. class BaseRenderer:
  19. def get_template(self, template_name):
  20. raise NotImplementedError('subclasses must implement get_template()')
  21. def render(self, template_name, context, request=None):
  22. template = self.get_template(template_name)
  23. return template.render(context, request=request).strip()
  24. class EngineMixin:
  25. def get_template(self, template_name):
  26. return self.engine.get_template(template_name)
  27. @cached_property
  28. def engine(self):
  29. return self.backend({
  30. 'APP_DIRS': True,
  31. 'DIRS': [os.path.join(ROOT, self.backend.app_dirname)],
  32. 'NAME': 'djangoforms',
  33. 'OPTIONS': {},
  34. })
  35. class DjangoTemplates(EngineMixin, BaseRenderer):
  36. """
  37. Load Django templates from the built-in widget templates in
  38. django/forms/templates and from apps' 'templates' directory.
  39. """
  40. backend = DjangoTemplates
  41. class Jinja2(EngineMixin, BaseRenderer):
  42. """
  43. Load Jinja2 templates from the built-in widget templates in
  44. django/forms/jinja2 and from apps' 'jinja2' directory.
  45. """
  46. backend = Jinja2
  47. class TemplatesSetting(BaseRenderer):
  48. """
  49. Load templates using template.loader.get_template() which is configured
  50. based on settings.TEMPLATES.
  51. """
  52. def get_template(self, template_name):
  53. return get_template(template_name)

LRU算法的实现

  1. def lru_cache(maxsize=128, typed=False):
  2. """Least-recently-used cache decorator.
  3. If *maxsize* is set to None, the LRU features are disabled and the cache
  4. can grow without bound.
  5. If *typed* is True, arguments of different types will be cached separately.
  6. For example, f(3.0) and f(3) will be treated as distinct calls with
  7. distinct results.
  8. Arguments to the cached function must be hashable.
  9. View the cache statistics named tuple (hits, misses, maxsize, currsize)
  10. with f.cache_info(). Clear the cache and statistics with f.cache_clear().
  11. Access the underlying function with f.__wrapped__.
  12. See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
  13. """
  14. # Users should only access the lru_cache through its public API:
  15. # cache_info, cache_clear, and f.__wrapped__
  16. # The internals of the lru_cache are encapsulated for thread safety and
  17. # to allow the implementation to change (including a possible C version).
  18. # Early detection of an erroneous call to @lru_cache without any arguments
  19. # resulting in the inner function being passed to maxsize instead of an
  20. # integer or None.
  21. if maxsize is not None and not isinstance(maxsize, int):
  22. raise TypeError('Expected maxsize to be an integer or None')
  23. def decorating_function(user_function):
  24. wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
  25. return update_wrapper(wrapper, user_function)
  26. return decorating_function
  27. def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
  28. # Constants shared by all lru cache instances:
  29. sentinel = object() # unique object used to signal cache misses
  30. make_key = _make_key # build a key from the function arguments
  31. PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
  32. cache = {}
  33. hits = misses = 0
  34. full = False
  35. cache_get = cache.get # bound method to lookup a key or return None
  36. cache_len = cache.__len__ # get cache size without calling len()
  37. lock = RLock() # because linkedlist updates aren't threadsafe
  38. root = [] # root of the circular doubly linked list
  39. root[:] = [root, root, None, None] # initialize by pointing to self
  40. if maxsize == 0:
  41. def wrapper(*args, **kwds):
  42. # No caching -- just a statistics update after a successful call
  43. nonlocal misses
  44. result = user_function(*args, **kwds)
  45. misses += 1
  46. return result
  47. elif maxsize is None:
  48. def wrapper(*args, **kwds):
  49. # Simple caching without ordering or size limit
  50. nonlocal hits, misses
  51. key = make_key(args, kwds, typed)
  52. result = cache_get(key, sentinel)
  53. if result is not sentinel:
  54. hits += 1
  55. return result
  56. result = user_function(*args, **kwds)
  57. cache[key] = result
  58. misses += 1
  59. return result
  60. else:
  61. def wrapper(*args, **kwds):
  62. # Size limited caching that tracks accesses by recency
  63. nonlocal root, hits, misses, full
  64. key = make_key(args, kwds, typed)
  65. with lock:
  66. link = cache_get(key)
  67. if link is not None:
  68. # Move the link to the front of the circular queue
  69. link_prev, link_next, _key, result = link
  70. link_prev[NEXT] = link_next
  71. link_next[PREV] = link_prev
  72. last = root[PREV]
  73. last[NEXT] = root[PREV] = link
  74. link[PREV] = last
  75. link[NEXT] = root
  76. hits += 1
  77. return result
  78. result = user_function(*args, **kwds)
  79. with lock:
  80. if key in cache:
  81. # Getting here means that this same key was added to the
  82. # cache while the lock was released. Since the link
  83. # update is already done, we need only return the
  84. # computed result and update the count of misses.
  85. pass
  86. elif full:
  87. # Use the old root to store the new key and result.
  88. oldroot = root
  89. oldroot[KEY] = key
  90. oldroot[RESULT] = result
  91. # Empty the oldest link and make it the new root.
  92. # Keep a reference to the old key and old result to
  93. # prevent their ref counts from going to zero during the
  94. # update. That will prevent potentially arbitrary object
  95. # clean-up code (i.e. __del__) from running while we're
  96. # still adjusting the links.
  97. root = oldroot[NEXT]
  98. oldkey = root[KEY]
  99. oldresult = root[RESULT]
  100. root[KEY] = root[RESULT] = None
  101. # Now update the cache dictionary.
  102. del cache[oldkey]
  103. # Save the potentially reentrant cache[key] assignment
  104. # for last, after the root and links have been put in
  105. # a consistent state.
  106. cache[key] = oldroot
  107. else:
  108. # Put result in a new link at the front of the queue.
  109. last = root[PREV]
  110. link = [last, root, key, result]
  111. last[NEXT] = root[PREV] = cache[key] = link
  112. # Use the cache_len bound method instead of the len() function
  113. # which could potentially be wrapped in an lru_cache itself.
  114. full = (cache_len() >= maxsize)
  115. misses += 1
  116. return result
  117. def cache_info():
  118. """Report cache statistics"""
  119. with lock:
  120. return _CacheInfo(hits, misses, maxsize, cache_len())
  121. def cache_clear():
  122. """Clear the cache and cache statistics"""
  123. nonlocal hits, misses, full
  124. with lock:
  125. cache.clear()
  126. root[:] = [root, root, None, None]
  127. hits = misses = 0
  128. full = False
  129. wrapper.cache_info = cache_info
  130. wrapper.cache_clear = cache_clear
  131. return wrapper
  132. try:
  133. from _functools import _lru_cache_wrapper
  134. except ImportError:
  135. pass

官方文档补充

1.【与过滤器相关的字符串】

1,将参数强制转换为字符串

自定义过滤器部分的代码书写(文件路径:templatetags/extra_tags)

  1. from django.template.defaultfilters import stringfilter # 用于将传递给类的参数强制转换为字符串
  2. register = template.Library()
  3. @register.filter
  4. @stringfilter
  5. def lower(value):
  6. return value.lower()

在视图中为该过滤器传入参数(项目下或者app下的均可)

  1. def self_register(request):
  2. fstring = ('Today', '20170912')
  3. return render(request, 'load.html', context={'fstring': fstring})

与其匹配的合适URL路径

  1. path('selfre/',views.self_register, name='self_register'),

HTML中调用过滤器(从view导入的参数)

  1. {% load extra_tags %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>自定义模板标签</title>
  7. </head>
  8. <body>
  9. <p>强制转换为字符串格式</p>
  10. {{fstring|lower}}
  11. </body>
  12. </html>

功能说明

  1. ### 强制将传入对象转换为字符串---包括数字,元组,字符串本身
  2. ### 该功能的实现需要导入对应的字符串过滤器,同时使用装饰器注册过滤器已经使用装饰器形式的字符串过滤器

2,自动转义—-安全字符串

自动转义功能的过滤器书写(templatetags/extra_tags)

  1. from django.utils.html import conditional_escape
  2. from django.utils.safestring import mark_safe
  3. register = template.Library()
  4. @register.filter(needs_autoescape=True) # 参数实际上是一个flag(标记)
  5. def initial_letter_filter(text, autoescape=True): # 传入参数,一个设置,自动转义已经开启
  6. """
  7. 安全字符串指的是:可以被安全展示,无需HTML引擎进行转换
  8. """
  9. first, other = text[0], text[1:] # 分别将传入的列表对象赋值给两个变量
  10. if autoescape:
  11. esc = conditional_escape # 如果是
  12. else:
  13. esc = lambda x: x # 如果没有自动转义则显示原文
  14. result = '<strong>%s </strong>%s' % (esc(first), esc(other)) # 以格式化字符串的方式展示
  15. return mark_safe(result) # 将已经完成转义的字符串标记为安全字符串

视图函数中的传值和调用

  1. def safe_string(request):
  2. safe_string = ['Javascript', 'Python', 'Django', 'Flask', 'M&G']
  3. return render(request, 'load.html', context={'safe_string': safe_string})

URL对视图的调用

  1. path('safestring/',views.safe_string, name='safe_string'),

HTML中使用过滤器(指定过滤器名字和传入的变量[由view视图传入])

  1. {% load extra_tags %}
  2. <!DOCTYPE html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>自定义模板标签</title>
  7. </head>
  8. <body>
  9. <p>安全字符串---自动转义</p>
  10. {{safe_string|initial_letter_filter}}
  11. </body>
  12. </html>

【补充】—- 安全字符串的定义

  1. """
  2. Functions for working with "safe strings": strings that can be displayed safely
  3. without further escaping in HTML. Marking something as a "safe string" means
  4. that the producer of the string has already turned characters that should not
  5. be interpreted by the HTML engine (e.g. '<') into the appropriate entities.
  6. """
  7. """
  8. 这个函数和安全字符串一起运行:安全字符串能够被安全展示,无需进一步从HTML转义。
  9. 将变量标记为安全字符串意味着:字符串的生产者已经将字符转换,它就不会需要HTML的引擎将其解释为合适的实体
  10. """
  11. from functools import wraps
  12. class SafeData:
  13. def __html__(self):
  14. """
  15. Return the html representation of a string for interoperability.
  16. This allows other template engines to understand Django's SafeData.
  17. """
  18. return self
  19. [文档]class SafeString(str, SafeData):
  20. """
  21. A str subclass that has been specifically marked as "safe" for HTML output
  22. purposes.
  23. """
  24. def __add__(self, rhs):
  25. """
  26. Concatenating a safe string with another safe bytestring or
  27. safe string is safe. Otherwise, the result is no longer safe.
  28. """
  29. t = super().__add__(rhs)
  30. if isinstance(rhs, SafeData):
  31. return SafeString(t)
  32. return t
  33. def __str__(self):
  34. return self
  35. SafeText = SafeString # For backwards compatibility since Django 2.0.
  36. def _safety_decorator(safety_marker, func):
  37. @wraps(func)
  38. def wrapped(*args, **kwargs):
  39. return safety_marker(func(*args, **kwargs))
  40. return wrapped
  41. [文档]def mark_safe(s):
  42. """
  43. Explicitly mark a string as safe for (HTML) output purposes. The returned
  44. object can be used everywhere a string is appropriate.
  45. If used on a method as a decorator, mark the returned data as safe.
  46. Can be called multiple times on a single string.
  47. """
  48. if hasattr(s, '__html__'):
  49. return s
  50. if callable(s):
  51. return _safety_decorator(mark_safe, s)
  52. return SafeString(s)

参考与引用