__init__.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from __future__ import unicode_literals
  2. from functools import wraps
  3. import warnings
  4. from django import template
  5. from django import VERSION as DJANGO_VERSION
  6. from django.template.context import Context
  7. from django.template.loader import get_template, select_template
  8. from mezzanine.utils.device import templates_for_device
  9. class Library(template.Library):
  10. """
  11. Extends ``django.template.Library`` providing several shortcuts
  12. that attempt to take the leg-work out of creating different types
  13. of template tags.
  14. """
  15. def as_tag(self, tag_func):
  16. """
  17. Creates a tag expecting the format:
  18. ``{% tag_name as var_name %}``
  19. The decorated func returns the value that is given to
  20. ``var_name`` in the template.
  21. """
  22. if DJANGO_VERSION >= (1, 9):
  23. warnings.warn(
  24. "The `as_tag` template tag builder is deprecated in favour of "
  25. "Django's built-in `simple_tag`, which supports variable "
  26. "assignment in Django 1.9 and above.",
  27. DeprecationWarning, stacklevel=2
  28. )
  29. @wraps(tag_func)
  30. def tag_wrapper(parser, token):
  31. class AsTagNode(template.Node):
  32. def render(self, context):
  33. parts = token.split_contents()
  34. # Resolve variables if their names are given.
  35. def resolve(arg):
  36. try:
  37. return template.Variable(arg).resolve(context)
  38. except template.VariableDoesNotExist:
  39. return arg
  40. args, kwargs = [], {}
  41. for arg in parts[1:-2]:
  42. if "=" in arg:
  43. name, val = arg.split("=", 1)
  44. if name in tag_func.__code__.co_varnames:
  45. kwargs[name] = resolve(val)
  46. continue
  47. args.append(resolve(arg))
  48. context[parts[-1]] = tag_func(*args, **kwargs)
  49. return ""
  50. return AsTagNode()
  51. return self.tag(tag_wrapper)
  52. def render_tag(self, tag_func):
  53. """
  54. Creates a tag using the decorated func as the render function
  55. for the template tag node. The render function takes two
  56. arguments - the template context and the tag token.
  57. """
  58. @wraps(tag_func)
  59. def tag_wrapper(parser, token):
  60. class RenderTagNode(template.Node):
  61. def render(self, context):
  62. return tag_func(context, token)
  63. return RenderTagNode()
  64. return self.tag(tag_wrapper)
  65. def to_end_tag(self, tag_func):
  66. """
  67. Creates a tag that parses until it finds the corresponding end
  68. tag, eg: for a tag named ``mytag`` it will parse until
  69. ``endmytag``. The decorated func's return value is used to
  70. render the parsed content and takes three arguments - the
  71. parsed content between the start and end tags, the template
  72. context and the tag token.
  73. """
  74. @wraps(tag_func)
  75. def tag_wrapper(parser, token):
  76. class ToEndTagNode(template.Node):
  77. def __init__(self):
  78. end_name = "end%s" % tag_func.__name__
  79. self.nodelist = parser.parse((end_name,))
  80. parser.delete_first_token()
  81. def render(self, context):
  82. args = (self.nodelist.render(context), context, token)
  83. return tag_func(*args[:tag_func.__code__.co_argcount])
  84. return ToEndTagNode()
  85. return self.tag(tag_wrapper)
  86. def inclusion_tag(self, name, context_class=Context, takes_context=False):
  87. """
  88. Replacement for Django's ``inclusion_tag`` which looks up device
  89. specific templates at render time.
  90. """
  91. def tag_decorator(tag_func):
  92. @wraps(tag_func)
  93. def tag_wrapper(parser, token):
  94. class InclusionTagNode(template.Node):
  95. def render(self, context):
  96. if not getattr(self, "nodelist", False):
  97. try:
  98. request = context["request"]
  99. except KeyError:
  100. t = get_template(name)
  101. else:
  102. ts = templates_for_device(request, name)
  103. t = select_template(ts)
  104. self.template = t
  105. parts = [template.Variable(part).resolve(context)
  106. for part in token.split_contents()[1:]]
  107. if takes_context:
  108. parts.insert(0, context)
  109. result = tag_func(*parts)
  110. autoescape = context.autoescape
  111. context = context_class(result, autoescape=autoescape)
  112. return self.template.render(context)
  113. return InclusionTagNode()
  114. return self.tag(tag_wrapper)
  115. return tag_decorator