Permalink
Browse files

Include monkeypatch from nuggets. Fix #11.

  • Loading branch information...
1 parent 42321f5 commit 4361b9aacd7b1d1f080fc7b154dff725ef220508 James Socol committed Nov 9, 2012
Showing with 128 additions and 0 deletions.
  1. +9 −0 README.rst
  2. +9 −0 docs/index.rst
  3. +85 −0 jingo/monkey.py
  4. +24 −0 jingo/tests/test_monkey.py
  5. +1 −0 jingo/tests/urls.py
View
@@ -152,6 +152,15 @@ The other method uses Jinja's ``trans`` tag::
directly. Both methods are useful, pick the one that makes you happy.
+Forms
+-----
+
+Django marks its form HTML "safe" according to its own rules, which Jinja2 does
+not recognize.
+
+.. automodule:: jingo.monkey
+
+
Testing
-------
View
@@ -152,6 +152,15 @@ The other method uses Jinja's ``trans`` tag::
directly. Both methods are useful, pick the one that makes you happy.
+Forms
+-----
+
+Django marks its form HTML "safe" according to its own rules, which Jinja2 does
+not recognize.
+
+.. automodule:: jingo.monkey
+
+
Testing
-------
View
@@ -0,0 +1,85 @@
+"""
+This monkeypatches Django to support the __html__ protocol used in Jinja
+templates. Form, BoundField, ErrorList, and other form objects that
+render HTML through their __unicode__ method are extended with __html__
+so they can be rendered in Jinja templates without adding |safe.
+
+Call the patch() function to execute the patch. It must be called
+before django.forms is imported for the conditional_escape patch to work
+properly. The root URLconf is the recommended location for calling patch().
+
+Usage::
+
+ import jingo.monkey
+ jingo.monkey.patch()
+
+This patch was originally developed by Jeff Balogh and this version is taken
+from the nuggets project at
+https://github.com/mozilla/nuggets/blob/master/safe_django_forms.py
+
+"""
+import django.utils.encoding
+import django.utils.html
+import django.utils.safestring
+
+
+# This function gets directly imported within Django, so this change needs to
+# happen before too many Django imports happen.
+def conditional_escape(html):
+ """
+ Similar to escape(), except that it doesn't operate on pre-escaped strings.
+ """
+ if hasattr(html, '__html__'):
+ return html.__html__()
+ elif isinstance(html, django.utils.safestring.SafeData):
+ return html
+ return django.utils.html.escape(html)
+
+
+# Django uses SafeData to mark a string that has already been escaped or
+# otherwise deemed safe. This __html__ method lets Jinja know about that too.
+def __html__(self):
+ """
+ Returns the html representation of a string.
+
+ Allows interoperability with other template engines.
+ """
+ return self
+
+
+# Django uses StrAndUnicode for classes like Form, BoundField, Widget which
+# have a __unicode__ method which returns escaped html. We replace
+# StrAndUnicode with SafeStrAndUnicode to get the __html__ method.
+class SafeStrAndUnicode(django.utils.encoding.StrAndUnicode):
+ """A class whose __str__ and __html__ returns __unicode__."""
+
+ def __html__(self):
+ return unicode(self)
+
+
+def patch():
+ django.utils.html.conditional_escape = conditional_escape
+ django.utils.safestring.SafeData.__html__ = __html__
+
+ # forms imports have to come after we patch conditional_escape.
+ from django.forms import forms, formsets, util, widgets
+
+ # Replace StrAndUnicode with SafeStrAndUnicode in the inheritance
+ # for all these classes.
+ classes = (
+ forms.BaseForm,
+ forms.BoundField,
+ formsets.BaseFormSet,
+ util.ErrorDict,
+ util.ErrorList,
+ widgets.Media,
+ widgets.RadioInput,
+ widgets.RadioFieldRenderer,
+ )
+
+ for cls in classes:
+ bases = list(cls.__bases__)
+ if django.utils.encoding.StrAndUnicode in bases:
+ idx = bases.index(django.utils.encoding.StrAndUnicode)
+ bases[idx] = SafeStrAndUnicode
+ cls.__bases__ = tuple(bases)
@@ -0,0 +1,24 @@
+from django import forms
+
+from jinja2 import escape
+from nose.tools import eq_
+
+import jingo
+import jingo.monkey
+from test_helpers import render
+
+
+class MyForm(forms.Form):
+ email = forms.EmailField()
+
+
+def test_monkey_patch():
+ form = MyForm()
+ html = form.as_ul()
+ context = {'form': form}
+ t = '{{ form.as_ul() }}'
+
+ eq_(escape(html), render(t, context))
+
+ jingo.monkey.patch()
+ eq_(html, render(t, context))
View
@@ -1,5 +1,6 @@
from django.conf.urls.defaults import patterns
+
urlpatterns = patterns('',
(r'^url/(\d+)/(\w+)/$', lambda r: None, {}, "url-args"),
(r'^url/(?P<num>\d+)/(?P<word>\w+)/$', lambda r: None, {}, "url-kwargs"),

0 comments on commit 4361b9a

Please sign in to comment.