自定义过滤器及标签
上节知识复习
前后端合并—-加快软件开发项目流程—-》学习成套知识
常用的模板标签
{% %}标签的语法
基本原则:有开始一定有结束【标签成对出现】
{%if%}
{%endif%}
if .... else
forloop
{%load%}
{%endload%}
{%url 'index_05'%}
模板继承的意义
方便代码复用,使用继承模板的方式
继承时,使用标签[extends]
使用模板中的内容
block.super()
调用关系描述
在浏览器中,发出特定request请求,从项目中设置文件指定的urls文件中,匹配URL规则,调用views视图函数,由视图调用HTML文件,该文件中包含自定义过滤器以及指定标签扩展,从标签中调用模板标签,读取自定义过滤器
文件路径配置
【重点】创建自定义模板的步骤
1,在项目文件目录下创建名为common的包(即目录中含有init.py)
2,将common加入到settings文件中
3,在common里面创建目录templates,在目录里面创建自定义过滤器及标签文件
4, 在common_extras.py文件中自定义cut和lower的过滤器功能
def mycut(value, arg):
return value.replace(arg, '')
def mylower(value):
return value.lower()
自定义过滤器
概念:自定义过滤器就是一个带有一个或者两个参数的python函数;
(输入的)变量的值 --- 不一定是字符串形式;
参数的值 --- 可以有一个初始值,或者完全不要这个参数
示例
{{var|fool:"bar"}} # var:变量; fool:自定义过滤器的名字;"bar":传给变量的值
功能实现
在url访问中调用对应的视图函数,然后从视图函数的返回中我们可以看到:自定义一个字符串,以上下文的方式传递给H5文件,由H5文件处理
def common(request):
ts = 'YKK seasame'
return render(request, 'loads.html', context={
'ts': ts, # 上下文中ts的名字需要完全保持一致
})
进入H5之后,由自定义过滤器处理传入的字符串,字符串是用参数ts传入的,因此我们在H5中书写时必须使用ts作为参数,才有对象处理
{% load common_extras %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试自定义过滤器及标签</title>
</head>
<body>
<p>展示自定义过滤器---转为小写</p>
{{somevariable|mylower}}<br>
<p>展示自定义过滤器---删除空格</p>
{{somevariable|mycut:''}}<br>
</body>
</html>
注册自定义过滤器
【语法】
django.template.Library.filter()
1.Library.filter()方法需要两个参数:
a. 过滤器的名称(一个字符串对象)
b. 编译的函数 – 一个Python函数(不要把函数名写成字符串)
2.可以把register.filter()用作装饰器;
3.没有声明 name 参数,Django将使用函数名作为过滤器的名字。
【总结】过滤器及自定义标签格式解读
形式: @函数名
作用:在不改变原有代码逻辑的情况下,增加功能
自定义装饰器的方法
1 filter()
第一个参数:self是类对象的名字(Library对象)
第二个参数:name = None 返回的是函数的名字
【源码】—- 装饰器filter
def filter(self, name=None, filter_func=None, **flags):
"""
Register a callable as a template filter. Example: # 注册一个可以调用的过滤器
@register.filter
def lower(value): # 需要为自定义函数传递参数
return value.lower()
"""
if name is None and filter_func is None: # 如果没有为过滤器传入名字和被装饰的函数的名字
# @register.filter()
def dec(func):
return self.filter_function(func, **flags)
return dec # 返回被装饰函数的名字
elif name is not None and filter_func is None:
if callable(name):
# @register.filter
return self.filter_function(name, **flags)
else:
# @register.filter('somename') or @register.filter(name='somename')
def dec(func):
return self.filter(name, func, **flags)
return dec
elif name is not None and filter_func is not None:
# register.filter('somename', somefunc)
self.filters[name] = filter_func
for attr in ('expects_localtime', 'is_safe', 'needs_autoescape'):
if attr in flags:
value = flags[attr]
# set the flag on the filter for FilterExpression.resolve
setattr(filter_func, attr, value)
# set the flag on the innermost decorated function
# for decorators that need it, e.g. stringfilter
if hasattr(filter_func, "_decorated_function"):
setattr(filter_func._decorated_function, attr, value)
filter_func._filter_name = name
return filter_func
else:
raise ValueError(
"Unsupported arguments to Library.filter: (%r, %r)" %
(name, filter_func),
)
def filter_function(self, func, **flags):
name = getattr(func, "_decorated_function", func).__name__
return self.filter(name, func, **flags)
2 simple_tag
第一个参数:self是Library对象
第二个参数: func这个是函数
第三个参数:take_context获取上下文
【源码】
def simple_tag(self, func=None, takes_context=None, name=None):
"""
Register a callable as a compiled template tag. Example:
@register.simple_tag
def hello(*args, **kwargs):
return 'world'
"""
def dec(func):
params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(func)
function_name = (name or getattr(func, '_decorated_function', func).__name__)
@functools.wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
target_var = None
if len(bits) >= 2 and bits[-2] == 'as':
target_var = bits[-1]
bits = bits[:-2]
args, kwargs = parse_bits(
parser, bits, params, varargs, varkw, defaults,
kwonly, kwonly_defaults, takes_context, function_name,
)
return SimpleNode(func, takes_context, args, kwargs, target_var)
self.tag(function_name, compile_func)
return func
if func is None:
# @register.simple_tag(...)
return dec
elif callable(func):
# @register.simple_tag
return dec(func)
else:
raise ValueError("Invalid arguments provided to simple_tag")
【业务代码】
from django import template
register = template.library()
def mycut(value, arg):
return value.replace(arg, '')
register.filter('mycut', mycut)
@register.filter
def mylower(value):
return value.lower()
【在模板中使用自定义的过滤器】
需要使用{%load%}标签将我们自定义模块加载进来,{%load%}声明将会载入给定模块名中的标签/过滤器
{%load common_extras%}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义过滤器及标签</title>
</head>
<body>
{{ts|mylower}}<br>
{{ts|mycut:''}}<br>
</body>
</html>
【代码演示】
在book/views.py文件中定义类—-调用HTML网页文件
【自定义过滤器前端效果】
【show_tag()标签的使用】
业务逻辑
- 在与views.py同级目录下创建文件夹templatetags,然后在其中创建init.py便于将此文件夹视为一个函数包,然后再创建一个customtag.py文件,用于在此处自定义标签和过滤器
- 首先在customtag中导入template.Library()便于对标签进行函数实例注册
- 然后就可以在该文件中自定义标签和装饰器
- 访问逻辑
- 首先从浏览器中输入正确的URL,输入值与项目urlpattern匹配
- 然后进入views.py文件中调用合适的类
- 在类中返回特定HTML5网页渲染
- 在HTML5文件中首先引入模板标签和过滤器,然后以指定标签格式调用已经定义好的模板标签和过滤器
代码含义:
参考与引用@register.simple_tag(name='current_time1') # 用于向Library注册实例,其后的name就是我们定义的类的名字
def current_time1(format_string): # 用于在网页上展示时间
print(format_string) # 格式化字符串就是我们在H5中定义的现实格式:%Y 年 %m 月 %I 时 %M 分 %p
return datetime.now().strftime(format_string) #
首先在common_extra中创建类,利用return语句返回字典对象
def common(request):
ts = 'YKK seasame'
return render(request, 'loads.html', context={
'ts': ts,
})
然后在HTML5文件中使用模板标签调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>show_tag标签的使用</title>
</head>
<body>
<ul>
{% for i in choices %}
<li>{{ i }}</li>
{% endfor %}
</ul>
</body>
</html>
其次在views视图下创建类,以HTML5为返回对象
def show_tag(request):
return render(request, 'show_tag.html')
inclusion_tag()装饰器的使用
文件路径:Library.py[136]
定义:
def inclusion_tag(self, filename, func=None, takes_context=None, name=None):
"""
Register a callable as an inclusion tag:
@register.inclusion_tag('results.html')
def show_results(poll):
choices = poll.choice_set.all()
return {'choices': choices}
【源码阅读】
渲染器的处理逻辑
import functools
import os
from django.conf import settings
from django.template.backends.django import DjangoTemplates
from django.template.loader import get_template
from django.utils.functional import cached_property
from django.utils.module_loading import import_string
try:
from django.template.backends.jinja2 import Jinja2
except ImportError:
def Jinja2(params):
raise ImportError("jinja2 isn't installed")
ROOT = os.path.dirname(__file__)
@functools.lru_cache()
def get_default_renderer():
renderer_class = import_string(settings.FORM_RENDERER)
return renderer_class()
class BaseRenderer:
def get_template(self, template_name):
raise NotImplementedError('subclasses must implement get_template()')
def render(self, template_name, context, request=None):
template = self.get_template(template_name)
return template.render(context, request=request).strip()
class EngineMixin:
def get_template(self, template_name):
return self.engine.get_template(template_name)
@cached_property
def engine(self):
return self.backend({
'APP_DIRS': True,
'DIRS': [os.path.join(ROOT, self.backend.app_dirname)],
'NAME': 'djangoforms',
'OPTIONS': {},
})
class DjangoTemplates(EngineMixin, BaseRenderer):
"""
Load Django templates from the built-in widget templates in
django/forms/templates and from apps' 'templates' directory.
"""
backend = DjangoTemplates
class Jinja2(EngineMixin, BaseRenderer):
"""
Load Jinja2 templates from the built-in widget templates in
django/forms/jinja2 and from apps' 'jinja2' directory.
"""
backend = Jinja2
class TemplatesSetting(BaseRenderer):
"""
Load templates using template.loader.get_template() which is configured
based on settings.TEMPLATES.
"""
def get_template(self, template_name):
return get_template(template_name)
LRU算法的实现
def lru_cache(maxsize=128, typed=False):
"""Least-recently-used cache decorator.
If *maxsize* is set to None, the LRU features are disabled and the cache
can grow without bound.
If *typed* is True, arguments of different types will be cached separately.
For example, f(3.0) and f(3) will be treated as distinct calls with
distinct results.
Arguments to the cached function must be hashable.
View the cache statistics named tuple (hits, misses, maxsize, currsize)
with f.cache_info(). Clear the cache and statistics with f.cache_clear().
Access the underlying function with f.__wrapped__.
See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
"""
# Users should only access the lru_cache through its public API:
# cache_info, cache_clear, and f.__wrapped__
# The internals of the lru_cache are encapsulated for thread safety and
# to allow the implementation to change (including a possible C version).
# Early detection of an erroneous call to @lru_cache without any arguments
# resulting in the inner function being passed to maxsize instead of an
# integer or None.
if maxsize is not None and not isinstance(maxsize, int):
raise TypeError('Expected maxsize to be an integer or None')
def decorating_function(user_function):
wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo)
return update_wrapper(wrapper, user_function)
return decorating_function
def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo):
# Constants shared by all lru cache instances:
sentinel = object() # unique object used to signal cache misses
make_key = _make_key # build a key from the function arguments
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
cache = {}
hits = misses = 0
full = False
cache_get = cache.get # bound method to lookup a key or return None
cache_len = cache.__len__ # get cache size without calling len()
lock = RLock() # because linkedlist updates aren't threadsafe
root = [] # root of the circular doubly linked list
root[:] = [root, root, None, None] # initialize by pointing to self
if maxsize == 0:
def wrapper(*args, **kwds):
# No caching -- just a statistics update after a successful call
nonlocal misses
result = user_function(*args, **kwds)
misses += 1
return result
elif maxsize is None:
def wrapper(*args, **kwds):
# Simple caching without ordering or size limit
nonlocal hits, misses
key = make_key(args, kwds, typed)
result = cache_get(key, sentinel)
if result is not sentinel:
hits += 1
return result
result = user_function(*args, **kwds)
cache[key] = result
misses += 1
return result
else:
def wrapper(*args, **kwds):
# Size limited caching that tracks accesses by recency
nonlocal root, hits, misses, full
key = make_key(args, kwds, typed)
with lock:
link = cache_get(key)
if link is not None:
# Move the link to the front of the circular queue
link_prev, link_next, _key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
hits += 1
return result
result = user_function(*args, **kwds)
with lock:
if key in cache:
# Getting here means that this same key was added to the
# cache while the lock was released. Since the link
# update is already done, we need only return the
# computed result and update the count of misses.
pass
elif full:
# Use the old root to store the new key and result.
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
# Empty the oldest link and make it the new root.
# Keep a reference to the old key and old result to
# prevent their ref counts from going to zero during the
# update. That will prevent potentially arbitrary object
# clean-up code (i.e. __del__) from running while we're
# still adjusting the links.
root = oldroot[NEXT]
oldkey = root[KEY]
oldresult = root[RESULT]
root[KEY] = root[RESULT] = None
# Now update the cache dictionary.
del cache[oldkey]
# Save the potentially reentrant cache[key] assignment
# for last, after the root and links have been put in
# a consistent state.
cache[key] = oldroot
else:
# Put result in a new link at the front of the queue.
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
# Use the cache_len bound method instead of the len() function
# which could potentially be wrapped in an lru_cache itself.
full = (cache_len() >= maxsize)
misses += 1
return result
def cache_info():
"""Report cache statistics"""
with lock:
return _CacheInfo(hits, misses, maxsize, cache_len())
def cache_clear():
"""Clear the cache and cache statistics"""
nonlocal hits, misses, full
with lock:
cache.clear()
root[:] = [root, root, None, None]
hits = misses = 0
full = False
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return wrapper
try:
from _functools import _lru_cache_wrapper
except ImportError:
pass
官方文档补充
1.【与过滤器相关的字符串】
1,将参数强制转换为字符串
自定义过滤器部分的代码书写(文件路径:templatetags/extra_tags)
from django.template.defaultfilters import stringfilter # 用于将传递给类的参数强制转换为字符串
register = template.Library()
@register.filter
@stringfilter
def lower(value):
return value.lower()
在视图中为该过滤器传入参数(项目下或者app下的均可)
def self_register(request):
fstring = ('Today', '20170912')
return render(request, 'load.html', context={'fstring': fstring})
与其匹配的合适URL路径
path('selfre/',views.self_register, name='self_register'),
HTML中调用过滤器(从view导入的参数)
{% load extra_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义模板标签</title>
</head>
<body>
<p>强制转换为字符串格式</p>
{{fstring|lower}}
</body>
</html>
功能说明
### 强制将传入对象转换为字符串---包括数字,元组,字符串本身
### 该功能的实现需要导入对应的字符串过滤器,同时使用装饰器注册过滤器已经使用装饰器形式的字符串过滤器
2,自动转义—-安全字符串
自动转义功能的过滤器书写(templatetags/extra_tags)
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter(needs_autoescape=True) # 参数实际上是一个flag(标记)
def initial_letter_filter(text, autoescape=True): # 传入参数,一个设置,自动转义已经开启
"""
安全字符串指的是:可以被安全展示,无需HTML引擎进行转换
"""
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) # 将已经完成转义的字符串标记为安全字符串
视图函数中的传值和调用
def safe_string(request):
safe_string = ['Javascript', 'Python', 'Django', 'Flask', 'M&G']
return render(request, 'load.html', context={'safe_string': safe_string})
URL对视图的调用
path('safestring/',views.safe_string, name='safe_string'),
HTML中使用过滤器(指定过滤器名字和传入的变量[由view视图传入])
{% load extra_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义模板标签</title>
</head>
<body>
<p>安全字符串---自动转义</p>
{{safe_string|initial_letter_filter}}
</body>
</html>
【补充】—- 安全字符串的定义
"""
Functions for working with "safe strings": strings that can be displayed safely
without further escaping in HTML. Marking something as a "safe string" means
that the producer of the string has already turned characters that should not
be interpreted by the HTML engine (e.g. '<') into the appropriate entities.
"""
"""
这个函数和安全字符串一起运行:安全字符串能够被安全展示,无需进一步从HTML转义。
将变量标记为安全字符串意味着:字符串的生产者已经将字符转换,它就不会需要HTML的引擎将其解释为合适的实体
"""
from functools import wraps
class SafeData:
def __html__(self):
"""
Return the html representation of a string for interoperability.
This allows other template engines to understand Django's SafeData.
"""
return self
[文档]class SafeString(str, SafeData):
"""
A str subclass that has been specifically marked as "safe" for HTML output
purposes.
"""
def __add__(self, rhs):
"""
Concatenating a safe string with another safe bytestring or
safe string is safe. Otherwise, the result is no longer safe.
"""
t = super().__add__(rhs)
if isinstance(rhs, SafeData):
return SafeString(t)
return t
def __str__(self):
return self
SafeText = SafeString # For backwards compatibility since Django 2.0.
def _safety_decorator(safety_marker, func):
@wraps(func)
def wrapped(*args, **kwargs):
return safety_marker(func(*args, **kwargs))
return wrapped
[文档]def mark_safe(s):
"""
Explicitly mark a string as safe for (HTML) output purposes. The returned
object can be used everywhere a string is appropriate.
If used on a method as a decorator, mark the returned data as safe.
Can be called multiple times on a single string.
"""
if hasattr(s, '__html__'):
return s
if callable(s):
return _safety_decorator(mark_safe, s)
return SafeString(s)