Skip to content

Commit

Permalink
Add grid template
Browse files Browse the repository at this point in the history
Fix some bugs
Add more documentation
Update template tests to use bs4
Actually make templates choose the fieldset dynamically
  • Loading branch information
Javex committed Apr 11, 2014
1 parent c018eab commit b54aa85
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 95 deletions.
19 changes: 19 additions & 0 deletions docs/usage/forms.rst
Expand Up @@ -21,6 +21,25 @@ Finally, you can of course always override the methods on the form.
:members:
:inherited-members:

.. _fieldset_templates:

Fieldset Templates
~~~~~~~~~~~~~~~~~~

You can configure custom fieldset templates on the :ref:`fieldsets <fieldsets>`
configuration parameter by setting the "template" key for a fieldset. The
following fieldsets are available:

horizontal
A typical horizontal display that renders each form field in its own row
with a label before the field.

grid
A grid display that renders the field first and then displays the label.
All fields are next to each other and line breaks only happen at the edge
of the screen. This is a good template for a fieldset that consists only of
checkboxes.

.. _inline_forms:

Inline Forms
Expand Down
5 changes: 5 additions & 0 deletions docs/usage/views.rst
Expand Up @@ -71,6 +71,11 @@ label
While you should normally set it, not setting it will invent some hopefully
nice-looking strings for the default usage (basically list and edit views).

description
Used on form fields to describe a field more in-depth than a label can.
This text may be arbitrarily long. It is not displayed on all templates
(see :ref:`fieldset_templates`).

css_class
A css class which should be set on this element's context. Currently this
is only used for the list view where the ``th`` element gets this class so
Expand Down
24 changes: 22 additions & 2 deletions pyramid_crud/forms.py
Expand Up @@ -100,6 +100,21 @@ def fieldsets(self):
if field.name != 'csrf_token']
return [{'title': '', 'fields': default_fields}]

def get_fieldsets(self):
"""
Get a list of all configured fieldsets, setting defaults where they are
missing.
"""
result = []
for original in self.fieldsets:
fieldset = {
'title': original.get('title', ''),
'template': original.get('template', 'horizontal'),
'fields': [getattr(self, f) for f in original['fields']],
}
result.append(fieldset)
return result


class CSRFForm(SecureForm):
"""
Expand Down Expand Up @@ -136,8 +151,7 @@ def validate(self):

class ModelMeta(_CoreModelMeta):
def __new__(meta, name, bases, attrs):
if "inlines" not in attrs:
attrs["inlines"] = []
attrs.setdefault("inlines", [])
return super(ModelMeta, meta).__new__(meta, name, bases, attrs)


Expand Down Expand Up @@ -183,6 +197,12 @@ class as it will provide you with a complete overview of what's
* ``fields``: A list of field names that should be displayed together
in a fieldset. This is required.
* ``template``: The name of the fieldset template to load. This must be
the name of a file in the ``fieldsets`` directory of the current
theme **without** a file extension. It defaults to ``horizontal``
which uses bootstraps horizontal forms for each fieldset. See
:ref:`fieldset_templates` for details on available templates.
title
Set the title of your form. By default this returns the class name of
the model. It is used in different places such as the title of the
Expand Down
2 changes: 1 addition & 1 deletion pyramid_crud/templates/mako/bootstrap/base.mako
Expand Up @@ -9,7 +9,7 @@
<%block name="head" />
<title>${view.Form.title_plural} | CRUD</title>
</head>
<body style="margin:0 20px">
<body class="container">
<%block name="heading" />
% for msg in request.session.pop_flash('error'):
<div class="alert alert-danger">${msg}</div>
Expand Down
4 changes: 2 additions & 2 deletions pyramid_crud/templates/mako/bootstrap/edit.mako
@@ -1,8 +1,8 @@
<%inherit file="${context.get('view').get_template_for('base')}" />
<h1>${'New' if is_new else 'Edit'} ${view.Form.title}</h1>
<form method="POST" class="crud-edit">
% for fieldset in form.fieldsets:
<%include file="${context.get('view').get_template_for('fieldsets/horizontal')}" args="fieldset=fieldset" />
% for fieldset in form.get_fieldsets():
<%include file="${context.get('view').get_template_for('fieldsets/%s' % fieldset['template'])}" args="fieldset=fieldset" />
% endfor
% for inline, items in form.inline_fieldsets.values():
<%include file="${context.get('view').get_template_for('edit_inline/tabular')}" args="inline=inline, items=items" />
Expand Down
11 changes: 11 additions & 0 deletions pyramid_crud/templates/mako/bootstrap/fieldsets/grid.mako
@@ -0,0 +1,11 @@
<%page args="fieldset"/>
<fieldset>
% if fieldset["title"]:
<legend>${fieldset["title"]}</legend>
% endif
% for field in fieldset["fields"]:
<div class="pull-left col-md-2 form-group">
${field()} ${field.label()}
</div>
% endfor
</fieldset>
16 changes: 12 additions & 4 deletions pyramid_crud/templates/mako/bootstrap/fieldsets/horizontal.mako
@@ -1,20 +1,28 @@
<%page args="fieldset"/>
<fieldset>
<fieldset class="form-horizontal">
% if fieldset["title"]:
<legend>${fieldset["title"]}</legend>
% endif
% for field in map(lambda f: getattr(form, f), fieldset['fields']):
% if field.widget.input_type == 'hidden':
% for field in fieldset['fields']:
% if getattr(field.widget, 'input_type', None) == 'hidden':
${field()}
% else:
${field.label()}: ${field()}<br />
<div class="form-group">
${field.label(class_='control-label col-sm-2')}
<div class="col-sm-10">
${field(class_='form-control')}
% if getattr(field, 'description', ''):
<span class="help-block">${field.description}</span>
% endif
</div>
% if field.errors:
<ul>
% for msg in field.errors:
<li>${msg}</li>
% endfor
</ul>
% endif
</div>
% endif
% endfor
</fieldset>
2 changes: 1 addition & 1 deletion pyramid_crud/views.py
Expand Up @@ -756,7 +756,7 @@ def iter_head_cols(self):
col = getattr(self, col)
else:
raise AttributeError("No attribute of name '%s' on model "
"or view found")
"or view found", col)
# Create a copy
if hasattr(col, 'info'):
col_info = dict(col.info)
Expand Down
57 changes: 38 additions & 19 deletions tests/test_forms.py
Expand Up @@ -687,35 +687,54 @@ def test_field_names(self, model_factory, form_factory, any_form):
field_names = ['val']
assert Form.field_names == field_names

def test_fieldsets(self, model_factory, form_factory, any_form):
def test_get_fieldsets(self, model_factory, form_factory, any_form):
Model = model_factory([Column('val', Integer)])
Form = form_factory(base=any_form, model=Model)
form = Form()
fieldsets = [{'title': '', 'fields': ['val']}]
assert form.fieldsets == fieldsets
fieldsets = [{'title': '', 'fields': [form.val],
'template': 'horizontal'}]
assert form.get_fieldsets() == fieldsets

def test_fieldsets_empty(self, model_factory, form_factory, any_form):
def test_get_fieldsets_empty(self, model_factory, form_factory, any_form):
Model = model_factory()
Form = form_factory(base=any_form, model=Model)
form = Form()
fieldsets = [{'title': '', 'fields': []}]
assert form.fieldsets == fieldsets

def test_fieldsets_override(self, model_factory, form_factory, any_form):
fieldsets = [{'title': 'Test', 'fields': ['test', 'foo']}]
Model = model_factory()
Form = form_factory(base=any_form, model=Model,
fields={'fieldsets': fieldsets})
form = Form()
assert form.fieldsets == fieldsets

def test_fieldsets_no_csrf_token(self, model_factory, form_factory,
any_form):
fieldsets = [{'title': '', 'fields': [], 'template': 'horizontal'}]
assert form.get_fieldsets() == fieldsets

def test_get_fieldsets_override(self, model_factory, form_factory,
any_form):
fieldsets = [{'title': 'Test', 'fields': ['test_text'],
'template': 'horizontal'}]
self.form.fieldsets = fieldsets
form = self.form()
expected = []
for fieldset in fieldsets:
copy = dict(fieldset)
expected.append(copy)
expected[0]['fields'] = [form.test_text]
assert form.get_fieldsets() == expected

def test_get_fieldsets_override_default_template(
self, model_factory, form_factory, any_form):
fieldsets = [{'title': 'Test', 'fields': ['test_text']}]
self.form.fieldsets = fieldsets
form = self.form()
expected = []
for fieldset in fieldsets:
copy = dict(fieldset)
expected.append(copy)
expected[0]['template'] = 'horizontal'
expected[0]['fields'] = [form.test_text]
assert form.get_fieldsets() == expected

def test_get_fieldsets_no_csrf_token(self, model_factory, form_factory,
any_form):
Model = model_factory([Column('csrf_token', Integer)])
Form = form_factory(base=any_form, model=Model)
form = Form()
fieldsets = [{'title': '', 'fields': []}]
assert form.fieldsets == fieldsets
fieldsets = [{'title': '', 'fields': [], 'template': 'horizontal'}]
assert form.get_fieldsets() == fieldsets

def test_primary_keys(self, model_factory, form_factory, any_form,
DBSession):
Expand Down

0 comments on commit b54aa85

Please sign in to comment.