Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bendavis78 committed Jun 29, 2011
0 parents commit 9f094f5
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 0 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,2 @@
include MANIFEST.in
recursive-include formhelper/templates *
91 changes: 91 additions & 0 deletions README.rst
@@ -0,0 +1,91 @@
=================
Django FormHelper
=================

Django FormHelper is a collection of templates and templatetags to ease the
pain in building out web forms. It does this by breaking the form down into
separate re-usable and customizable components. This allows you to only
customize the parts you want, and let the rest happen automatically.


Usage
=============
After installing django-formhelper, add ``formhelper`` to your ``INSTALLED_APPS`` in your ``settings.py``.

In your template, you need to load ``formhelper``:

...
{% load formhelper %}
...


Template Tags
=============

----------
form_field
----------
Render out a single form field. Uses the template ``formhelper/includes/field.html``. Example usage:

{% form_field contact_form first_name %}

If your form is in your view's context as the variable "form", you can omit the first argument:

{% form_field first_name %}

Otherwise, you can use the "with" templatetag

{% with my_form as form %}
...
{% form_field first_name %}
...
{% endwith %}


--------
form_row
--------
Like form_field, but renders out multiple fields. Uses the template ``formhelper/includes/form_row.html``. Example usage:

{% form_row first_name middle_name last_name %}


----------
error_list
----------
Render out the form error list as an unordered list. Uses the template ``formhelper/includes/error_list.html`` Example usage:

{% error_list %}

You may also render out only non-field errors or only field-specific errors:

{% error_list non_field %}
... or ...
{% error_list field %}

-----------
class_names
-----------
Renders a list of useful class names for a field that includes the field name, the widget type, whether or not the field is requried, and whether or not the field has an error.

For example, if your field was a textinput named "first_name" and it was required:

<div class="{% field|class_names %}">

would result in:

<div class="first_name text-input required">

if the field has an error:

<div class="first_name text-input required error">

Templates
=========
``formhelper/includes/form.html``

``formhelper/includes/form_row.html``

``formhelper/includes/field.html``

``formhelper/includes/errorlist.html``
Empty file added formhelper/__init__.py
Empty file.
Empty file added formhelper/models.py
Empty file.
15 changes: 15 additions & 0 deletions formhelper/templates/formhelper/includes/errorlist.html
@@ -0,0 +1,15 @@
{% load formhelper %}
{% if form.errors %}
<ul class="errorlist non-field-errors">
{% if not only == "field" %}
{% for err in form.non_field_errors %}
<li>{{ err }}</li>
{% endfor %}
{% endif %}
{% if not only == "non_field" %}
{% for field in form %}
{% if field.errors %}<li>{{ field|field_error }}</li>{% endif %}
{% endfor %}
{% endif %}
</ul>
{% endif %}
6 changes: 6 additions & 0 deletions formhelper/templates/formhelper/includes/field.html
@@ -0,0 +1,6 @@
{% load formhelper %}
<div class="field {{ field|class_names }}">
<label for="id_{{ field.name }}">{{ field.label }}{% if field.field.required %}<span>*</span>{% endif %}</label>
{{ field.errors }}
{{ field }}
</div>
5 changes: 5 additions & 0 deletions formhelper/templates/formhelper/includes/form.html
@@ -0,0 +1,5 @@
{% load formhelper %}
{% error_list form %}
{% for field in form %}
{% form_field field %}
{% endfor %}
6 changes: 6 additions & 0 deletions formhelper/templates/formhelper/includes/form_row.html
@@ -0,0 +1,6 @@
{% load formhelper %}
<div class="form-row">
{% for field in fields %}
{% form_field form field %}
{% endfor %}
</div>
Empty file.
96 changes: 96 additions & 0 deletions formhelper/templatetags/formhelper.py
@@ -0,0 +1,96 @@
from django import template
from django.template.loader import get_template
from django import forms
import re

register = template.Library()
first_cap_re = re.compile('(.)([A-Z][a-z]+)')
all_cap_re = re.compile('([a-z0-9])([A-Z])')

@register.filter
def field_error(field):
if field.errors:
err = field.errors[0]
if err == unicode(field.field.default_error_messages.get('required')):
err = '%s is required' % field.label
else:
err = '%s: ' % field.label
return err
return ''

@register.filter
def class_names(field):
class_names = [field.name.replace('_','-')]
widget_class = pyclass_to_cssclass(field.field.widget.__class__.__name__)
class_names.append(widget_class)
if field.field.required:
class_names.append('required')
if field.errors:
class_names.append('error')
return ' '.join(class_names)

class FormFieldNode(template.Node):
def __init__(self, form, field):
if not form:
form = 'form'
self.form = template.Variable(form)
self.field = field

def render(self, context):
try:
form = self.form.resolve(context)
except template.VariableDoesNotExist:
raise template.TemplateSyntaxError('a form must either be passed to form_field as first argument or must exist in context as "form"')
tpl = get_template('formhelper/includes/field.html')
field = _get_field(self.field, form, context)
return tpl.render(template.Context({'form':form, 'field':field}))

@register.tag
def form_field(parser, token):
args = token.split_contents()[1:]
if not args:
raise template.TemplateSyntaxError("form_field takes at least one argument")
if len(args) == 1:
return FormFieldNode(None, args[0])
return FormFieldNode(args[0], args[1])

class FormRowNode(template.Node):
def __init__(self, args):
self.args = args

def render(self, context):
tpl = get_template('formhelper/includes/form_row.html')
if context.get(self.args[0]) and isinstance(context[self.args[0]], forms.Form):
form = context[self.args[0]]
field_list = self.args[1:]
elif context.get('form'):
form = context['form']
field_list = self.args
else:
raise template.TemplateSyntaxError('a form must either be passed to form_row as first argument or must exist in context as "form"')
fields = [_get_field(f, form, context) for f in field_list]
return tpl.render(template.Context({'form': form, 'fields': fields}))

@register.tag
def form_row(parser, token):
args = token.split_contents()[1:]
if not args:
raise template.TemplateSyntaxError("form_row takes at least one argument")
return FormRowNode(args)

@register.inclusion_tag('formhelper/includes/errorlist.html')
def error_list(form, only=''):
return {'form':form, 'only':only}

def _get_field(field, form, context):
try:
field = template.Variable(field).resolve(context)
except template.VariableDoesNotExist:
pass
if isinstance(field, basestring):
field = form[field]
return field

def pyclass_to_cssclass(name):
s1 = first_cap_re.sub(r'\1-\2', name)
return all_cap_re.sub(r'\1-\2', s1).lower()
20 changes: 20 additions & 0 deletions setup.py
@@ -0,0 +1,20 @@
from setuptools import setup, find_packages

setup(
name='formhelper',
version='0.1',
author='Ben Davis',
author_email='bendavis78@gmail.com',
url='http://github.com/bendavis78',
description='Django FormHelper is a collection of templates and templatetags to ease the pain in building out web forms',
keywords='django forms',
classifiers = [
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules'
],
packages = find_packages(),
include_package_data = True
)

0 comments on commit 9f094f5

Please sign in to comment.