Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions pyramid_simpleform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ class Form(object):
`list_char` : variabledecode list char

Also note that values of ``obj`` supercede those of ``defaults``. Only
fields specified in your schema or validators will be taken from the
fields specified in your schema or validators will be taken from the
object.
"""

default_state = State

def __init__(self, request, schema=None, validators=None, defaults=None,
obj=None, extra=None, include=None, exclude=None, state=None,
method="POST", variable_decode=False, dict_char=".",
def __init__(self, request, schema=None, validators=None, defaults=None,
obj=None, extra=None, include=None, exclude=None, state=None,
method="POST", variable_decode=False, dict_char=".",
list_char="-", multipart=False):

self.request = request
Expand Down Expand Up @@ -120,7 +120,16 @@ def __init__(self, request, schema=None, validators=None, defaults=None,
def is_error(self, field):
"""
Checks if individual field has errors.

Traverses nested forms when dotted notation is provided.
"""
if '.' in field:
subset = self.errors
for part in field.split('.'):
if part not in subset:
return False
subset = subset[part]
return True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by why you would need this? The errors should exist as {'foo.bar': error} not {'foo': {'bar': error}}. I can see why you would want it for renderer.value() since the data may be a nested dict if you are using variabledecode.

return field in self.errors

def all_errors(self):
Expand All @@ -140,28 +149,35 @@ def errors_for(self, field):
"""
Returns any errors for a given field as a list.
"""
errors = self.errors.get(field, [])
if '.' in field:
errors = self.errors
for part in field.split('.'):
if not isinstance(errors, dict) or part not in errors:
return []
errors = errors[part]
else:
errors = self.errors.get(field, [])
if isinstance(errors, basestring):
errors = [errors]
return errors

def validate(self, force_validate=False, params=None):
"""
Runs validation and returns True/False whether form is
Runs validation and returns True/False whether form is
valid.

This will check if the form should be validated (i.e. the
request method matches) and the schema/validators validate.

Validation will only be run once; subsequent calls to
Validation will only be run once; subsequent calls to
validate() will have no effect, i.e. will just return
the original result.

The errors and data values will be updated accordingly.

`force_validate` : will run validation regardless of request method.

`params` : dict or MultiDict of params. By default
`params` : dict or MultiDict of params. By default
will use **request.POST** (if HTTP POST) or **request.params**.
"""

Expand All @@ -180,7 +196,7 @@ def validate(self, force_validate=False, params=None):
params = self.request.POST
else:
params = self.request.params

if self.variable_decode:
decoded = variabledecode.variable_decode(
params, self.dict_char, self.list_char)
Expand Down Expand Up @@ -216,10 +232,10 @@ def bind(self, obj, include=None, exclude=None):
Binds validated field values to an object instance, for example a
SQLAlchemy model instance.

`include` : list of included fields. If field not in this list it
`include` : list of included fields. If field not in this list it
will not be bound to this object.

`exclude` : list of excluded fields. If field is in this list it
`exclude` : list of excluded fields. If field is in this list it
will not be bound to the object.

Returns the `obj` passed in.
Expand Down Expand Up @@ -258,7 +274,7 @@ def htmlfill(self, content, **htmlfill_kwargs):

charset = getattr(self.request, 'charset', 'utf-8')
htmlfill_kwargs.setdefault('encoding', charset)
return htmlfill.render(content,
return htmlfill.render(content,
defaults=self.data,
errors=self.errors,
**htmlfill_kwargs)
Expand All @@ -267,7 +283,7 @@ def render(self, template, extra_info=None, htmlfill=True,
**htmlfill_kwargs):
"""
Renders the form directly to a template,
using Pyramid's **render** function.
using Pyramid's **render** function.

`template` : name of template

Expand All @@ -292,7 +308,7 @@ def submit(request):
return dict(form=form.render("my_form.html"))

"""

extra_info = extra_info or {}
extra_info.setdefault('form', self)

Expand Down
86 changes: 42 additions & 44 deletions pyramid_simpleform/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ def text(self, name, value=None, id=None, **attrs):
Outputs text input.
"""
return tags.text(
name,
self.value(name, value),
self._get_id(id, name),
name,
self.value(name, value),
self._get_id(id, name),
**attrs
)

Expand All @@ -40,9 +40,9 @@ def file(self, name, value=None, id=None, **attrs):
Outputs file input.
"""
return tags.file(
name,
self.value(name, value),
self._get_id(id, name),
name,
self.value(name, value),
self._get_id(id, name),
**attrs
)

Expand All @@ -54,9 +54,9 @@ def hidden(self, name, value=None, id=None, **attrs):
value = self.value(name)

return tags.hidden(
name,
value,
self._get_id(id, name),
name,
value,
self._get_id(id, name),
**attrs
)

Expand All @@ -72,9 +72,9 @@ def submit(self, name, value=None, id=None, **attrs):
Outputs submit button.
"""
return tags.submit(
name,
self.value(name, value),
self._get_id(id, name),
name,
self.value(name, value),
self._get_id(id, name),
**attrs
)

Expand All @@ -83,25 +83,25 @@ def select(self, name, options, selected_value=None, id=None, **attrs):
Outputs <select> element.
"""
return tags.select(
name,
self.value(name, selected_value),
options,
self._get_id(id, name),
name,
self.value(name, selected_value),
options,
self._get_id(id, name),
**attrs
)

def checkbox(self, name, value="1", checked=False, label=None, id=None,
def checkbox(self, name, value="1", checked=False, label=None, id=None,
**attrs):
"""
Outputs checkbox input.
"""

return tags.checkbox(
name,
value,
self.value(name),
label,
self._get_id(id, name),
name,
value,
self.value(name),
label,
self._get_id(id, name),
**attrs
)

Expand All @@ -111,9 +111,9 @@ def textarea(self, name, content="", id=None, **attrs):
"""

return tags.textarea(
name,
self.value(name, content),
self._get_id(id, name),
name,
self.value(name, content),
self._get_id(id, name),
**attrs
)

Expand All @@ -122,15 +122,15 @@ def password(self, name, value=None, id=None, **attrs):
Outputs a password input.
"""
return tags.password(
name, self.value(name, value),
self._get_id(id, name),
name, self.value(name, value),
self._get_id(id, name),
**attrs)

def is_error(self, name):
"""
Shortcut for **self.form.is_error(name)**
"""
return name in self.errors
return self.form.is_error(name)

def errors_for(self, name):
"""
Expand Down Expand Up @@ -163,15 +163,15 @@ def errorlist(self, name=None, **attrs):
return ''

content = "\n".join(HTML.tag("li", error) for error in errors)

if 'class_' not in attrs:
attrs['class_'] = "error"

return HTML.tag("ul", tags.literal(content), **attrs)

def label(self, name, label=None, **attrs):
"""
Outputs a <label> element.
Outputs a <label> element.

`name` : field name. Automatically added to "for" attribute.

Expand All @@ -182,7 +182,7 @@ def label(self, name, label=None, **attrs):
if self.id_prefix:
for_ = self.id_prefix + for_
attrs['for_'] = for_

label = label or name.capitalize()
return HTML.tag("label", label, **attrs)

Expand Down Expand Up @@ -210,8 +210,8 @@ def __init__(self, form, csrf_field='_csrf', id_prefix=None):
self.csrf_field = csrf_field

super(FormRenderer, self).__init__(
self.form.data,
self.form.errors,
self.form.data,
self.form.errors,
id_prefix,
)

Expand All @@ -231,7 +231,7 @@ def end(self):
Closes the form, i.e. outputs </form>.
"""
return tags.end_form()

def csrf(self, name=None):
"""
Returns the CSRF hidden input. Creates new CSRF token
Expand All @@ -255,15 +255,15 @@ def csrf_token(self, name=None):

def hidden_tag(self, *names):
"""
Convenience for printing all hidden fields in a form inside a
Convenience for printing all hidden fields in a form inside a
hidden DIV. Will also render the CSRF hidden field.

:versionadded: 0.4
"""
inputs = [self.hidden(name) for name in names]
inputs.append(self.csrf())
return HTML.tag("div",
tags.literal("".join(inputs)),
return HTML.tag("div",
tags.literal("".join(inputs)),
style="display:none;")


Expand All @@ -272,7 +272,7 @@ class SequenceRenderer(Renderer):
def __init__(self, name, data, errors, id_prefix=None, min_entries=0):

self.name = name

num_entries = min_entries - len(data)
if num_entries > 0:
for i in xrange(num_entries):
Expand All @@ -291,9 +291,9 @@ def end(self):
return self.hidden('__end__', value='%s:sequence' % self.name, id='')

def __iter__(self):

# what kind of data we dealing with ?

for i, d in enumerate(self.data):

if not isinstance(d, dict):
Expand All @@ -302,7 +302,7 @@ def __iter__(self):
errors = [] # to be determined
id_prefix = "%d-" % i

yield MappingRenderer(self.name, d, errors, id_prefix=id_prefix)
yield MappingRenderer(self.name, d, errors, id_prefix=id_prefix)


class MappingRenderer(Renderer):
Expand All @@ -328,5 +328,3 @@ def end(self, name=None):
name = name or self.name

return self.hidden('__end__', value='%s:mapping' % name, id='')