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
- Add Toggle Switch Widget to Django Forms by OgliariNatan 1 month, 4 weeks ago
 - get_object_or_none by azwdevops 5 months, 2 weeks ago
 - Mask sensitive data from logger by agusmakmun 7 months, 2 weeks ago
 - Template tag - list punctuation for a list of items by shapiromatron 1 year, 9 months ago
 - JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 1 year, 9 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.