Last active
December 15, 2015 22:59
-
-
Save yoloseem/5336709 to your computer and use it in GitHub Desktop.
Jinja2 htmlfill Extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Jinja2 x htmlfill | |
Known bugs: Can't skip empty dictionaries when htmlfilling is not needed. | |
# AUTHOR: HYUNJUN KIM <[email protected]> | |
# LICENSE: Distributed under MIT License. | |
""" | |
import formencode.htmlfill | |
import jinja2 | |
import jinja2.ext | |
import jinja2.nodes | |
class FormExtension(jinja2.ext.Extension): | |
"""Jinja2 Extension for convenient using of HTML ``<form>`` element. | |
Works with `FormEncode htmlfill`__ :: | |
{% form 'user.signin', {'method': 'POST'} | |
with {'username': 'james'}, {'username': 'Invalid Username'} %} | |
<input type="text" name="username" /> | |
<form:error name="username"> | |
<input type="password" name="password" /> | |
{% endform %} | |
Above code will be rendered like below :: | |
<form action="u/signin" method="POST"> | |
<input type="text" name="username" value="james" /> | |
<p class="error">Invalid Username</p> | |
<input type="password" name="password" /> | |
</form> | |
__ http://www.formencode.org/en/latest/htmlfill.html | |
.. seealso:: | |
gist --- `formencode htmlfill in jinja2 template`__ | |
__ https://gist.github.com/4595277 | |
""" | |
tags = set(['form']) | |
def parse(self, parser): | |
lineno = parser.stream.next().lineno | |
endpoint = parser.parse_expression() | |
endpoint_args = [] | |
form_attrs = [] | |
defaults = jinja2.nodes.Dict() | |
errors = jinja2.nodes.Dict() | |
while parser.stream.current.type != 'block_end': | |
while parser.stream.skip_if('comma'): | |
name = parser.stream.next_if('name') | |
if name and parser.stream.skip_if('assign'): | |
keyword = jinja2.nodes.Keyword(name.value, | |
parser.parse_expression()) | |
endpoint_args.append(keyword) | |
else: | |
if name: | |
parser.stream.push(parser.stream.current) | |
parser.stream.current = name | |
form_attrs.append(parser.parse_expression()) | |
name = parser.stream.next_if('name') | |
if name is None: | |
break | |
if name.value == 'with': | |
defaults = parser.parse_expression() | |
if parser.stream.skip_if('comma'): | |
errors = parser.parse_expression() | |
if isinstance(defaults, jinja2.nodes.Name): | |
defaults = jinja2.nodes.Getattr(jinja2.nodes.ContextReference(), | |
defaults.name, self.environment) | |
if isinstance(errors, jinja2.nodes.Name): | |
errors = jinja2.nodes.Getattr(jinja2.nodes.ContextReference(), | |
errors.name, self.environment) | |
body = parser.parse_statements(['name:endform'], drop_needle=True) | |
def output(*strings): | |
data = map(jinja2.nodes.TemplateData, strings) | |
return jinja2.nodes.Output(data) | |
body = [jinja2.nodes.CallBlock( | |
self.call_method('_htmlfill_support', (defaults, errors)), | |
[], [], body)] | |
action = jinja2.nodes.Call(jinja2.nodes.Name('url_for', 'load'), | |
[endpoint], endpoint_args, None, None) | |
form_attrs = self.call_method('_merge_dict', form_attrs) | |
for_form_attrs = jinja2.nodes.For( | |
jinja2.nodes.Tuple([jinja2.nodes.Name('__fattrname__', 'store'), | |
jinja2.nodes.Name('__fattrval__', 'store')], | |
'store'), | |
form_attrs, | |
[output(u' '), | |
jinja2.nodes.Output([jinja2.nodes.Name('__fattrname__', 'load')]), | |
output(u'="'), | |
jinja2.nodes.Output([jinja2.nodes.Name('__fattrval__', 'load')]), | |
output(u'"')], | |
[], None, False | |
) | |
start = [output(u'<form action="'), | |
jinja2.nodes.Output([action]), | |
output(u'" method="POST"'), | |
for_form_attrs, | |
output(u'>')] | |
body[:0] = start | |
body.append(output(u'</form>')) | |
return body | |
def _merge_dict(self, *dicts): | |
result = {} | |
for d in dicts: | |
result.update(d) | |
return result.iteritems() | |
def _htmlfill_support(self, defaults, errors, caller): | |
formatters = { | |
'default': lambda msg: u'<ul class="errors"><li>{0}</li></ul>' \ | |
.format(msg) | |
} | |
return formencode.htmlfill.render(caller(), defaults, errors, | |
error_formatters=formatters) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment