Ticket #2016: object_tag-combined-patch.diff

File object_tag-combined-patch.diff, 6.8 KB (added by marcusf, 6 years ago)

Following Alex's advice by providing a single patch file

  • django/template/__init__.py

    diff --git a/django/template/__init__.py b/django/template/__init__.py
    index 5493e5b..03e2a6c 100644
    a b class VariableNode(Node): 
    830830            return ''
    831831        return _render_value_in_context(output, context)
    832832
    833 def generic_tag_compiler(params, defaults, name, node_class, parser, token):
    834     "Returns a template.Node subclass."
    835     bits = token.split_contents()[1:]
     833def match_number_of_arguments(bits, params, defaults, name):
    836834    bmax = len(params)
    837835    def_len = defaults and len(defaults) or 0
    838836    bmin = bmax - def_len
    def generic_tag_compiler(params, defaults, name, node_class, parser, token): 
    842840        else:
    843841            message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
    844842        raise TemplateSyntaxError(message)
     843
     844def generic_tag_compiler(params, defaults, name, node_class, parser, token):
     845    "Returns a template.Node subclass."
     846    bits = token.split_contents()[1:]
     847    match_number_of_arguments(bits, params, defaults, name)
    845848    return node_class(bits)
    846849
    847850class Library(object):
    class Library(object): 
    913916        self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
    914917        return func
    915918
     919    def object_tag(self, func):
     920        params, xx, xxx, defaults = getargspec(func)
     921
     922        class ObjectNode(Node):
     923            def __init__(self, vars_to_resolve, var_name):
     924                self.vars_to_resolve = map(Variable, vars_to_resolve)
     925                self.var_name = var_name
     926
     927            def render(self, context):
     928                resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
     929                obj = func(*resolved_vars)
     930                context[self.var_name] = obj
     931                return ''
     932
     933        def object_tag_compiler(params, defaults, name, node_class, parser, token):
     934            bits = token.split_contents()[1:]
     935
     936            if len(bits) < 2 or bits[-2] != 'as':
     937                raise TemplateSyntaxError("the next to last argument to %s must be 'as'" % name)
     938
     939            var_name, bits = bits[-1], bits[:-2]
     940            match_number_of_arguments(bits, params, defaults, name)
     941
     942            return node_class(bits, var_name)
     943
     944        compile_func = curry(object_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, ObjectNode)
     945        compile_func.__doc__ = func.__doc__
     946
     947        self.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
     948
     949        return func
     950
    916951    def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
    917952        def dec(func):
    918953            params, xx, xxx, defaults = getargspec(func)
  • docs/howto/custom-template-tags.txt

    diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt
    index c6f7677..6b78711 100644
    a b class, like so:: 
    783783The difference here is that ``do_current_time()`` grabs the format string and
    784784the variable name, passing both to ``CurrentTimeNode3``.
    785785
     786Shortcut for loading objects into template variables
     787~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     788
     789.. versionadded: 1.2
     790
     791If you only need to retrieve and store an object in a template variable, it
     792might seem cumbersome to write both a renderer and a compilation function
     793for such an easy task.
     794
     795That is why Django provides yet another shortcut -- ``object_tag`` -- which
     796makes it easy to write tags that loads objects into template variables.
     797
     798Its use is very similar to ``simple_tag`` in the way that it takes care of
     799all the argument parsing for you, and only requires a single return value --
     800the object you'd like to insert into the template variable.
     801
     802Example::
     803
     804    def get_latest_polls(max_num):
     805        return Poll.objects.order_by('-pub_date')[:max_num]
     806
     807    register.object_tag(get_latest_polls)
     808
     809Or if you wish to use the Python 2.4 decorator syntax::
     810
     811    @register.object_tag
     812    def get_latest_polls(max_num):
     813        return Poll.objects.order_by('-pub_date')[:max_num]
     814
     815This tag returns the latest ``Poll``-objects, sorted by descending order, and
     816limited by the value of ``max_num``. Its use in a template would look like
     817this:
     818
     819.. code-block:: html+django
     820
     821    {% get_latest_polls 5 as latest_polls %}
     822
     823Which would retrieve the 5 latest polls and store them inside a template
     824variable named "latest_polls".
     825
     826Note that the following syntax is *mandatory* for all object_tag's:
     827
     828.. code-block:: html+django
     829
     830    {% tag_name [args] as <var_name> %}
     831
     832Where ``args`` is the arguments for the templatetag ``tag_name``, and
     833``var_name`` is the name of the template variable in which the returned object
     834should be stored.
     835
    786836Parsing until another block tag
    787837~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    788838
  • new file tests/regressiontests/templates/object_tag.py

    diff --git a/tests/regressiontests/templates/object_tag.py b/tests/regressiontests/templates/object_tag.py
    new file mode 100644
    index 0000000..5f67b9e
    - +  
     1object_tag_tests = """
     2>>> t = template.Template('{% load custom %}{% get_meaning_of_life as answer %}{{ answer }}')
     3>>> t.render(template.Context())
     4u"42"
     5>>> t = template.Template('{% load custom %}{% get_mascot_with "awesomeness" "magical powers" as pony %}{{ pony }}')
     6>>> t.render(template.Context())
     7u"This mascot is filled with awesomeness and magical powers"
     8>>> t = template.Template('{% load custom %}{% count "e" word as num %}{{ num }}')
     9>>> t.render(template.Context({'word': 'beefcake'}))
     10u"3"
     11"""
  • tests/regressiontests/templates/templatetags/custom.py

    diff --git a/tests/regressiontests/templates/templatetags/custom.py b/tests/regressiontests/templates/templatetags/custom.py
    index fdf8d10..bbb8c08 100644
    a b trim = stringfilter(trim) 
    99
    1010register.filter(trim)
    1111
     12def get_meaning_of_life():
     13    return 42
     14register.object_tag(get_meaning_of_life)
     15
     16def get_mascot_with(power_one, power_two):
     17    return 'This mascot is filled with %s and %s' % (power_one, power_two)
     18register.object_tag(get_mascot_with)
     19
     20def count(char, word):
     21    return word.count(char)
     22register.object_tag(count)
  • tests/regressiontests/templates/tests.py

    diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
    index 9c01b49..0badc86 100644
    a b from context import context_tests 
    2424from custom import custom_filters
    2525from parser import filter_parsing, variable_parsing
    2626from unicode import unicode_tests
     27from object_tag import object_tag_tests
    2728
    2829try:
    2930    from loaders import *
    __test__ = { 
    3839    'context': context_tests,
    3940    'filter_parsing': filter_parsing,
    4041    'custom_filters': custom_filters,
     42    'object_tag': object_tag_tests,
    4143}
    4244
    4345#################################
Back to Top