Skip to content

Commit

Permalink
Fixed django#18254 -- Added ability to the static template tags to st…
Browse files Browse the repository at this point in the history
…ore the result in a contextt variable. Many thanks to Andrei Antoukh for the initial patch.
  • Loading branch information
jezdez committed Jul 7, 2012
1 parent 8dafd04 commit 0a68a29
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 11 deletions.
30 changes: 27 additions & 3 deletions django/contrib/staticfiles/templatetags/staticfiles.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,37 @@
from django import template
from django.templatetags.static import StaticNode
from django.contrib.staticfiles.storage import staticfiles_storage

register = template.Library()


@register.simple_tag
def static(path):
class StaticFilesNode(StaticNode):

def url(self, context):
path = self.path.resolve(context)
return staticfiles_storage.url(path)


@register.tag('static')
def do_static(parser, token):
"""
A template tag that returns the URL to a file
using staticfiles' storage backend
Usage::
{% static path [as varname] %}
Examples::
{% static "myapp/css/base.css" %}
{% static variable_with_path %}
{% static "myapp/css/base.css" as admin_base_css %}
{% static variable_with_path as varname %}
"""
return staticfiles_storage.url(path)
return StaticFilesNode.handle_token(parser, token)


def static(path):
return StaticNode.handle_simple(path)
63 changes: 59 additions & 4 deletions django/templatetags/static.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from urlparse import urljoin
from django import template
from django.template.base import Node
from django.utils.encoding import iri_to_uri

register = template.Library()


class PrefixNode(template.Node):

def __repr__(self):
Expand Down Expand Up @@ -48,6 +50,7 @@ def render(self, context):
context[self.varname] = prefix
return ''


@register.tag
def get_static_prefix(parser, token):
"""
Expand All @@ -66,6 +69,7 @@ def get_static_prefix(parser, token):
"""
return PrefixNode.handle_token(parser, token, "STATIC_URL")


@register.tag
def get_media_prefix(parser, token):
"""
Expand All @@ -84,19 +88,70 @@ def get_media_prefix(parser, token):
"""
return PrefixNode.handle_token(parser, token, "MEDIA_URL")

@register.simple_tag
def static(path):

class StaticNode(Node):
def __init__(self, varname=None, path=None):
if path is None:
raise template.TemplateSyntaxError(
"Static template nodes must be given a path to return.")
self.path = path
self.varname = varname

def url(self, context):
path = self.path.resolve(context)
return self.handle_simple(path)

def render(self, context):
url = self.url(context)
if self.varname is None:
return url
context[self.varname] = url
return ''

@classmethod
def handle_simple(cls, path):
return urljoin(PrefixNode.handle_simple("STATIC_URL"), path)

@classmethod
def handle_token(cls, parser, token):
"""
Class method to parse prefix node and return a Node.
"""
bits = token.split_contents()

if len(bits) < 2:
raise template.TemplateSyntaxError(
"'%s' takes at least one argument (path to file)" % bits[0])

path = parser.compile_filter(bits[1])

if len(bits) >= 2 and bits[-2] == 'as':
varname = bits[3]
else:
varname = None

return cls(varname, path)


@register.tag('static')
def do_static(parser, token):
"""
Joins the given path with the STATIC_URL setting.
Usage::
{% static path %}
{% static path [as varname] %}
Examples::
{% static "myapp/css/base.css" %}
{% static variable_with_path %}
{% static "myapp/css/base.css" as admin_base_css %}
{% static variable_with_path as varname %}
"""
return urljoin(PrefixNode.handle_simple("STATIC_URL"), path)
return StaticNode.handle_token(parser, token)


def static(path):
return StaticNode.handle_simple(path)
11 changes: 11 additions & 0 deletions docs/ref/contrib/staticfiles.txt
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,17 @@ The previous example is equal to calling the ``url`` method of an instance of
useful when using a non-local storage backend to deploy files as documented
in :ref:`staticfiles-from-cdn`.

.. versionadded:: 1.5

If you'd like to retrieve a static URL without displaying it, you can use a
slightly different call::

.. code-block:: html+django

{% load static from staticfiles %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}" alt="Hi!" />

Other Helpers
=============

Expand Down
11 changes: 11 additions & 0 deletions docs/ref/templates/builtins.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2354,6 +2354,17 @@ It is also able to consume standard context variables, e.g. assuming a
{% load static %}
<link rel="stylesheet" href="{% static user_stylesheet %}" type="text/css" media="screen" />

If you'd like to retrieve a static URL without displaying it, you can use a
slightly different call::

.. versionadded:: 1.5

.. code-block:: html+django

{% load static %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}"></img>

.. note::

The :mod:`staticfiles<django.contrib.staticfiles>` contrib app also ships
Expand Down
12 changes: 8 additions & 4 deletions tests/regressiontests/staticfiles_tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,16 @@ def render_template(self, template, **kwargs):
template = loader.get_template_from_string(template)
return template.render(Context(kwargs)).strip()

def static_template_snippet(self, path):
def static_template_snippet(self, path, asvar=False):
if asvar:
return "{%% load static from staticfiles %%}{%% static '%s' as var %%}{{ var }}" % path
return "{%% load static from staticfiles %%}{%% static '%s' %%}" % path

def assertStaticRenders(self, path, result, **kwargs):
template = self.static_template_snippet(path)
def assertStaticRenders(self, path, result, asvar=False, **kwargs):
template = self.static_template_snippet(path, asvar)
self.assertEqual(self.render_template(template, **kwargs), result)

def assertStaticRaises(self, exc, path, result, **kwargs):
def assertStaticRaises(self, exc, path, result, asvar=False, **kwargs):
self.assertRaises(exc, self.assertStaticRenders, path, result, **kwargs)


Expand Down Expand Up @@ -368,6 +370,8 @@ def test_template_tag_return(self):
"/static/does/not/exist.png")
self.assertStaticRenders("test/file.txt",
"/static/test/file.dad0999e4f8f.txt")
self.assertStaticRenders("test/file.txt",
"/static/test/file.dad0999e4f8f.txt", asvar=True)
self.assertStaticRenders("cached/styles.css",
"/static/cached/styles.93b1147e8552.css")
self.assertStaticRenders("path/",
Expand Down
2 changes: 2 additions & 0 deletions tests/regressiontests/templates/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,8 @@ def get_template_tests(self):
'static-prefixtag04': ('{% load static %}{% get_media_prefix as media_prefix %}{{ media_prefix }}', {}, settings.MEDIA_URL),
'static-statictag01': ('{% load static %}{% static "admin/base.css" %}', {}, urljoin(settings.STATIC_URL, 'admin/base.css')),
'static-statictag02': ('{% load static %}{% static base_css %}', {'base_css': 'admin/base.css'}, urljoin(settings.STATIC_URL, 'admin/base.css')),
'static-statictag03': ('{% load static %}{% static "admin/base.css" as foo %}{{ foo }}', {}, urljoin(settings.STATIC_URL, 'admin/base.css')),
'static-statictag04': ('{% load static %}{% static base_css as foo %}{{ foo }}', {'base_css': 'admin/base.css'}, urljoin(settings.STATIC_URL, 'admin/base.css')),

# Verbatim template tag outputs contents without rendering.
'verbatim-tag01': ('{% verbatim %}{{bare }}{% endverbatim %}', {}, '{{bare }}'),
Expand Down

0 comments on commit 0a68a29

Please sign in to comment.