Skip to content

Commit

Permalink
Merge branch 'release/4.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
dyve committed May 21, 2014
2 parents 066fb49 + 7b58e7c commit c52b53a
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 66 deletions.
3 changes: 2 additions & 1 deletion AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ Contributors
* Austin Whittier <austin.whitt@gmail.com>
* Caio Ariede <caio.ariede@gmail.com>
* Fabio C. Barrionuevo da Luz <bnafta@gmail.com>
* Fabio Perfetti <perfabio87@gmail.com>
* Jay Pipes <jaypipes@gmail.com>
* Jonas Hagstedt <hagstedt@gmail.com>
* Jordan Starcher <jstarcher@gmail.com>
* Juan Carlos <juancarlospaco@gmail.com>
* Markus Holtermann <info@markusholtermann.eu>
* Nick S <nsmith448@gmail.com>
* Owais Lone <loneowais@gmail.com>
* Richard Hajdu <tuskone16@gmail.com>
* pmav99 <pmav99@users.noreply.github.com>
* Richard Hajdu <tuskone16@gmail.com>
8 changes: 8 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
History
-------

4.5.0 (2014-05-21)
++++++++++++++++++

* bug fixes in formsets
* new formset renderer
* new bootstrap_form_errors tag


4.4.2 (2014-05-20)
++++++++++++++++++

Expand Down
2 changes: 1 addition & 1 deletion bootstrap3/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-

__version__ = '4.4.2'
__version__ = '4.5.0'
8 changes: 8 additions & 0 deletions bootstrap3/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
'set_required': True,
'form_required_class': '',
'form_error_class': '',
'formset_renderers':{
'default': 'bootstrap3.renderers.FormsetRenderer',
},
'form_renderers': {
'default': 'bootstrap3.renderers.FormRenderer',
},
Expand Down Expand Up @@ -83,6 +86,11 @@ def get_renderer(renderers, layout):
return getattr(import_module(mod), cls)


def get_formset_renderer(layout):
renderers = get_bootstrap_setting('formset_renderers')
return get_renderer(renderers, layout)


def get_form_renderer(layout):
renderers = get_bootstrap_setting('form_renderers')
return get_renderer(renderers, layout)
Expand Down
22 changes: 16 additions & 6 deletions bootstrap3/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.forms import HiddenInput, FileInput, CheckboxSelectMultiple, Textarea, TextInput, DateInput, Select
from django.forms.formsets import BaseFormSet

from .bootstrap import get_bootstrap_setting, get_form_renderer, get_field_renderer
from .bootstrap import get_bootstrap_setting, get_form_renderer, get_field_renderer, get_formset_renderer
from .text import text_concat, text_value
from .exceptions import BootstrapError
from .html import add_css_class, render_tag
Expand All @@ -15,14 +15,16 @@
FORM_GROUP_CLASS = 'form-group'


def render_formset(formset, **kwargs):
def render_formset(formset, layout='', **kwargs):
"""
Render a formset to a Bootstrap layout
"""
if not isinstance(formset, BaseFormSet):
raise BootstrapError('Parameter "formset" should contain a valid Django FormSet.')
forms = [render_form(f, **kwargs) for f in formset]
return text_value(formset.management_form) + '\n' + '\n'.join(forms)
# if not isinstance(formset, BaseFormSet):
# raise BootstrapError('Parameter "formset" should contain a valid Django FormSet.')
# forms = [render_form(f, **kwargs) for f in formset]
# return text_value(formset.management_form) + '\n' + '\n'.join(forms)
renderer_cls = get_formset_renderer(layout)
return renderer_cls(formset, layout, **kwargs).render()


def render_form(form, layout='', **kwargs):
Expand All @@ -33,6 +35,14 @@ def render_form(form, layout='', **kwargs):
return renderer_cls(form, layout, **kwargs).render()


def render_form_errors(form, layout='', type='all', **kwargs):
"""
Render form errors to a Bootstrap layout
"""
renderer_cls = get_form_renderer(layout)
return renderer_cls(form, layout, **kwargs).render_errors(type)


def render_field(field, layout='', **kwargs):
"""
Render a formset to a Bootstrap layout
Expand Down
75 changes: 68 additions & 7 deletions bootstrap3/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
ClearableFileInput, Select, RadioSelect, CheckboxSelectMultiple)
from django.forms.extras import SelectDateWidget
from django.forms.forms import BaseForm, BoundField
from django.forms.formsets import BaseFormSet
from django.utils.html import conditional_escape, strip_tags
from django.template import Context
from django.template.loader import get_template
Expand All @@ -14,10 +15,64 @@
from bootstrap3.text import text_value
from .exceptions import BootstrapError
from .html import add_css_class
from .forms import (render_field, render_label, render_form_group,
from .forms import (render_form, render_field, render_label, render_form_group,
is_widget_with_placeholder, is_widget_required_attribute, FORM_GROUP_CLASS)


class FormsetRenderer(object):
"""
Default formset renderer
"""

def __init__(self, formset, layout='', form_group_class=FORM_GROUP_CLASS,
field_class='', label_class='', show_help=True, exclude='',
set_required=True):
if not isinstance(formset, BaseFormSet):
raise BootstrapError(
'Parameter "formset" should contain a valid Django Formset.')
self.formset = formset
self.layout = layout
self.form_group_class = form_group_class
self.field_class = field_class
self.label_class = label_class
self.show_help = show_help
self.exclude = exclude
self.set_required = set_required

def render_forms(self):
rendered_forms = []
for form in self.formset.forms:
rendered_forms.append(render_form(
form,
layout=self.layout,
form_group_class=self.form_group_class,
field_class=self.field_class,
label_class=self.label_class,
show_help=self.show_help,
exclude=self.exclude,
set_required=self.set_required,
))
return '\n'.join(rendered_forms)

def get_formset_errors(self):
return self.formset.non_form_errors()

def render_errors(self):
formset_errors = self.get_formset_errors()
if formset_errors:
return get_template(
'bootstrap3/form_errors.html').render(Context({
'errors': formset_errors,
'form': self.formset,
'layout': self.layout,
}))
return ''

def render(self):
return self.render_errors() + self.render_forms()



class FormRenderer(object):
"""
Default form renderer
Expand All @@ -27,8 +82,7 @@ def __init__(self, form, layout='', form_group_class=FORM_GROUP_CLASS,
field_class='', label_class='', show_help=True, exclude='',
set_required=True):
if not isinstance(form, BaseForm):
raise BootstrapError(
'Parameter "form" should contain a valid Django Form.')
raise BootstrapError('Parameter "form" should contain a valid Django Form.')
self.form = form
self.layout = layout
self.form_group_class = form_group_class
Expand All @@ -53,15 +107,22 @@ def render_fields(self):
))
return '\n'.join(rendered_fields)

def get_form_errors(self):
def get_fields_errors(self):
form_errors = []
for field in self.form:
if field.is_hidden and field.errors:
form_errors += field.errors
return form_errors + self.form.non_field_errors()
return form_errors

def render_errors(self, type='all'):
form_errors = None
if type == 'all':
form_errors = self.get_fields_errors() + self.form.non_field_errors()
elif type == 'fields':
form_errors = self.get_fields_errors()
elif type == 'non_fields':
form_errors = self.form.non_field_errors()

def render_errors(self):
form_errors = self.get_form_errors()
if form_errors:
return get_template(
'bootstrap3/form_errors.html').render(Context({
Expand Down
27 changes: 26 additions & 1 deletion bootstrap3/templatetags/bootstrap3.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ..bootstrap import css_url, javascript_url, jquery_url, theme_url, get_bootstrap_setting
from ..html import render_link_tag
from ..forms import render_button, render_field, render_field_and_label, render_form, render_form_group, render_formset, \
render_label
render_label, render_form_errors
from ..components import render_icon, render_alert
from ..templates import handle_var, parse_token_contents
from ..text import force_text
Expand Down Expand Up @@ -247,6 +247,31 @@ def bootstrap_form(*args, **kwargs):
return render_form(*args, **kwargs)


@register.simple_tag
def bootstrap_form_errors(*args, **kwargs):
"""
Render form errors
**Tag name**::
bootstrap_form_errors
**Parameters**:
:args:
:kwargs:
**usage**::
{% bootstrap_form_errors form %}
**example**::
{% bootstrap_form_errors form layout='inline' %}
"""
return render_form_errors(*args, **kwargs)


@register.simple_tag
def bootstrap_field(*args, **kwargs):
"""
Expand Down
16 changes: 16 additions & 0 deletions demo/demo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from __future__ import unicode_literals

from django import forms
from django.forms.formsets import BaseFormSet, formset_factory


from bootstrap3.tests import TestForm

Expand Down Expand Up @@ -29,6 +31,20 @@ class ContactForm(TestForm):
pass


class ContactBaseFormSet(BaseFormSet):
def add_fields(self, form, index):
super(ContactBaseFormSet, self).add_fields(form, index)

def clean(self):
super(ContactBaseFormSet, self).clean()
raise forms.ValidationError("This error was added to show the non form errors styling")

ContactFormSet = formset_factory(TestForm, formset=ContactBaseFormSet,
extra=2,
max_num=4,
validate_max=True)


class FilesForm(forms.Form):
text1 = forms.CharField()
file1 = forms.FileField()
Expand Down
3 changes: 2 additions & 1 deletion demo/demo/templates/demo/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ <h1>{% block title %}(no title){% endblock %}</h1>

<p>
<a href="{% url 'home' %}">home</a>
<a href="{% url 'formset_default' %}">formset</a>
<a href="{% url 'form_default' %}">form</a>
<a href="{% url 'form_by_field' %}">form_by_field</a>
<a href="{% url 'form_horizontal' %}">form_horizontal</a>
<a href="{% url 'form_inline' %}">form_inline</a>
<a href="{% url 'form_with_files' %}">form_with_files</a>
<a href="{% url 'formset' %}">formset</a>
<a href="{% url 'pagination' %}">pagination</a>
<a href="{% url 'misc' %}">miscellaneous</a>
</p>
Expand Down
18 changes: 18 additions & 0 deletions demo/demo/templates/demo/form_by_field.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends 'demo/base.html' %}

{% load bootstrap3 %}

{% block title %}
Forms
{% endblock %}

{% block content %}

<form role="form" method="post">
{% csrf_token %}
{% bootstrap_form_errors form type='non_fields' %}
{% bootstrap_field form.subject layout='horizontal' %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>

{% endblock %}
5 changes: 3 additions & 2 deletions demo/demo/templates/demo/formset.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
{% load bootstrap3 %}

{% block title %}
Forms
Formset
{% endblock %}

{% block content %}

<form role="form" method="post">
{% csrf_token %}
{% bootstrap_formset formset %}
{{ form.management_form }}
{% bootstrap_formset form %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>

Expand Down
5 changes: 3 additions & 2 deletions demo/demo/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.conf.urls import patterns, url

from .views import HomePageView, FormHorizontalView, FormInlineView, PaginationView, FormWithFilesView, \
DefaultFormView, MiscView, FormSetView
DefaultFormView, MiscView, DefaultFormsetView, DefaultFormByFieldView

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
Expand All @@ -24,11 +24,12 @@

urlpatterns = patterns('',
url(r'^$', HomePageView.as_view(), name='home'),
url(r'^formset$', DefaultFormsetView.as_view(), name='formset_default'),
url(r'^form$', DefaultFormView.as_view(), name='form_default'),
url(r'^form_by_field$', DefaultFormByFieldView.as_view(), name='form_by_field'),
url(r'^form_horizontal$', FormHorizontalView.as_view(), name='form_horizontal'),
url(r'^form_inline$', FormInlineView.as_view(), name='form_inline'),
url(r'^form_with_files$', FormWithFilesView.as_view(), name='form_with_files'),
url(r'^formset$', FormSetView.as_view(), name='formset'),
url(r'^pagination$', PaginationView.as_view(), name='pagination'),
url(r'^misc$', MiscView.as_view(), name='misc'),
)

0 comments on commit c52b53a

Please sign in to comment.