Enhanced version of snippet 1113
Usage example (not self-contained):
@register.tag
def groupurl(parser, token):
'''
Syntax::
{% groupurl view_name group [key=val, [key=val, ...]] [as varname] %}
Example::
<a href="{% groupurl blog_detail group slug=blog.slug %}">{{ blog.name }}</a>
'''
bits = token.contents.split()
tag_name = bits[0]
if len(bits) < 3:
raise template.TemplateSyntaxError("'%s' takes tag at least 2 arguments (path to a view and a group)" % (tag_name,)
bits_iter = iter(bits[1:])
# view_name + group and url kwargs
args, kwargs = parse_args_and_kwargs(parser, bits_iter, stop_test='as', tagname=tag_name)
if len(args) != 2:
raise template.TemplateSyntaxError("'%s' takes exactly two non-kwargs (path to a view and a group)" % (tag_name,))
view_name, group = args
# as var
asvar = None
for bit in bits_iter:
asvar = bit
return GroupURLNode(view_name, group, kwargs, asvar)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import inspect
from functools import partial
from django.template import TemplateSyntaxError
def isiterable(obj):
'''
Check if arg is iterable. Object is iterable when implements metod __iter__.
'''
return hasattr(obj, '__iter__')
def isstring(obj):
'''
Check whether `obj` is a string instance, i.e. str or unicode instance.
'''
return isinstance(obj, basestring)
_ARGS_TYPES = (_BOTH, _ARGS_ONLY, _KWARGS_ONLY) = (0, 1, 2)
def _parse_args_and_kwargs(parser, bits_iter, sep=",", tagname=None, type=_BOTH,
stop_test=lambda bit: False, return_stop_bit=False):
'''
:param type: indicates to support _ARGS_ONLY, _KWARGS_ONLY or _BOTH.
'''
assert type in _ARGS_TYPES, "'type' argument must be one of: _BOTH, _ARGS_ONLY, _KWARGS_ONLY."
args_only, kwargs_only = (type == _ARGS_ONLY), (type == _KWARGS_ONLY)
args = []
kwargs = {}
tagname = " '%s'" % tagname if tagname else ""
bit_test = stop_test if inspect.isfunction(stop_test) else\
(lambda bit: bit in stop_test) if (isiterable(stop_test) and not isstring(stop_test)) else\
(lambda bit: bit == stop_test)
stop_bit = None
for bit in bits_iter:
if bit_test(bit):
# if we would like to stop on the bit that passes the test:
#bits_iter = itertools.chain((bit,), bits_iter)
stop_bit = bit
break
for arg in bit.split(sep):
if '=' in arg:
if args_only:
raise TemplateSyntaxError("Tag%s does not support kwargs arguments." % tagname)
k, v = arg.split('=', 1)
k = str(k.strip())
if k in kwargs:
raise TemplateSyntaxError("Duplicate key '%s' in%s tag kwargs." % (k, tagname))
kwargs[k] = parser.compile_filter(v)
elif arg:
if kwargs_only:
raise TemplateSyntaxError("Tag%s does not support non-kwargs arguments." % tagname)
args.append(parser.compile_filter(arg))
ret = (args,) if args_only else\
(kwargs,) if kwargs_only else\
(args, kwargs)
if return_stop_bit:
ret += (stop_bit,)
if len(ret) == 1:
return ret[0]
return ret
parse_args_and_kwargs = partial(_parse_args_and_kwargs, type=_BOTH)
parse_args_and_kwargs.__name__ = 'parse_args_and_kwargs'
parse_args_and_kwargs.__doc__ =\
'''
Parses bits created form token "arg1,key1=val1, arg2 , ..." after splitting
contents.
:param sep: Single bit separator; by default ",".
:rtype sep: str
:param stop_test: Optional test to stop parsing earlier. This can be one of:
* single string with keyword to stop on
* list of string keywords to stop on
* function which takes only current bit of `bits_iter` as an argument and
returns boolean value.
Attention: `bits_iter` will be stopped after the bit that passes the test.
To obtain the stop bit use `return_stop_bit` flag and capture the
additional return value.
:rtype stop_test: str or list or callable
:returns: List of args and dictionary of kwargs with values compiled with the
parser.compile_filter() function. If `return_stop_bit` is True then
also the the last bit at which parsing stopped (see `stop_test`).
:rtype: tuple(list, dict [, basestring or None])
'''
parse_args = partial(_parse_args_and_kwargs, type=_ARGS_ONLY)
parse_args.__name__ = 'parse_args'
parse_args.__doc__ =\
'''
Parses bits created form token "arg1,arg2 , ...", after splitting contents. See
`parse_args_and_kwargs` for params details.
:returns: List of args with values compiled with the parser.compile_filter()
function. If `return_stop_bit` is True then also the the last bit at
which parsing stopped (see `stop_test`).
:rtype: list or tuple(list, basestring or None)
'''
parse_kwargs = partial(_parse_args_and_kwargs, type=_KWARGS_ONLY)
parse_kwargs.__name__ = 'parse_kwargs'
parse_kwargs.__doc__ =\
'''
Parses bits created form token "key1=val1, key2=val2, ...", after splitting
contents. See parse_args_and_kwargs` for params details.
:returns: Dictionary of kwargs with values compiled with the
parser.compile_filter() function. If `return_stop_bit` is True then
also the the last bit at which parsing stopped (see `stop_test`).
:rtype: dict or tuple(dict, basestring or None)
'''
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 8 months ago
Comments
Have a look how Django 1.3 does it in the url tag code: django/template/defaulttags.py
#
Please login first before commenting.