[Python-modules-commits] [python-django-crispy-forms] 01/01: New upstream version 1.7.0

Michael Fladischer fladi at moszumanska.debian.org
Sat Nov 4 20:40:10 UTC 2017


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch upstream
in repository python-django-crispy-forms.

commit 12ffacb1d91dd0a700cf5980f6827190268b9e1a
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Sat Nov 4 12:59:26 2017 +0100

    New upstream version 1.7.0
---
 .coveragerc                                        |   2 -
 .github/ISSUE_TEMPLATE.md                          |   9 +-
 .gitignore                                         |   3 +
 .travis.yml                                        |  16 +-
 CHANGELOG.md                                       |   8 +
 README.rst                                         |  12 +-
 crispy_forms/__init__.py                           |   2 +-
 crispy_forms/bootstrap.py                          |  16 +-
 crispy_forms/compatibility.py                      |  17 --
 crispy_forms/helper.py                             |  44 +++--
 crispy_forms/layout.py                             |  11 +-
 crispy_forms/layout_slice.py                       |   2 +-
 crispy_forms/models.py                             |   0
 crispy_forms/templates/bootstrap/field.html        |   2 +-
 .../bootstrap/layout/checkboxselectmultiple.html   |   2 +-
 .../templates/bootstrap/layout/radioselect.html    |   4 +-
 .../templates/bootstrap/table_inline_formset.html  |   6 +
 crispy_forms/templates/bootstrap3/field.html       |   4 +-
 .../bootstrap3/layout/checkboxselectmultiple.html  |   2 +-
 .../templates/bootstrap3/layout/inline_field.html  |   4 +-
 .../templates/bootstrap3/layout/multifield.html    |  40 ++--
 .../templates/bootstrap3/layout/radioselect.html   |   4 +-
 crispy_forms/templates/bootstrap3/multifield.html  |  39 ++++
 .../templates/bootstrap3/table_inline_formset.html |   6 +
 crispy_forms/templates/bootstrap4/field.html       |   6 +-
 crispy_forms/templates/bootstrap4/inputs.html      |   2 +-
 .../bootstrap4/layout/checkboxselectmultiple.html  |   2 +-
 .../templates/bootstrap4/layout/field_errors.html  |   2 +-
 .../bootstrap4/layout/field_errors_block.html      |   2 +-
 .../templates/bootstrap4/layout/inline_field.html  |   4 +-
 .../bootstrap4/layout/prepended_appended_text.html |   2 +-
 .../templates/bootstrap4/layout/radioselect.html   |   4 +-
 .../templates/bootstrap4/table_inline_formset.html |   6 +
 crispy_forms/templatetags/crispy_forms_field.py    |  33 +++-
 crispy_forms/templatetags/crispy_forms_filters.py  |  47 +++--
 crispy_forms/templatetags/crispy_forms_tags.py     |  40 +---
 crispy_forms/templatetags/crispy_forms_utils.py    |  15 +-
 crispy_forms/tests/compatibility.py                |   9 -
 crispy_forms/tests/conftest.py                     |   3 +-
 crispy_forms/tests/forms.py                        |  54 ++++--
 crispy_forms/tests/test_dynamic_api.py             |  25 ++-
 crispy_forms/tests/test_form_helper.py             | 215 ++++++++++++---------
 crispy_forms/tests/test_layout.py                  | 164 ++++++++--------
 crispy_forms/tests/test_layout_objects.py          | 102 +++++-----
 crispy_forms/tests/test_settings.py                |   1 -
 crispy_forms/tests/test_tags.py                    |  58 +++---
 crispy_forms/tests/test_utils.py                   |  57 ++++--
 crispy_forms/tests/urls.py                         |   1 -
 crispy_forms/tests/utils.py                        |  21 ++
 crispy_forms/utils.py                              |  71 ++-----
 docs/crispy_tag_forms.rst                          |   4 +-
 docs/form_helper.rst                               |   8 +-
 docs/install.rst                                   |   2 +-
 docs/layouts.rst                                   |   4 +-
 requirements.txt                                   |   2 +-
 setup.cfg                                          |  13 ++
 setup.py                                           |   3 +-
 tox.ini                                            |   7 +-
 58 files changed, 704 insertions(+), 540 deletions(-)

diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index 398ff08..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,2 +0,0 @@
-[run]
-branch = True
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 9568528..0c2414f 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,6 +1,7 @@
 * Package version:
 * Django version:
 * Python version:
+* Template pack: (Optional)
 
 ### Description:
 
@@ -8,7 +9,7 @@
 
 ### Preferably also include:
 
--[ ] Example Django Crispy Forms code
--[ ] Screenshots
--[ ] Actual HTML generated
--[ ] Exepcted HTML
+- [ ] Example Django Crispy Forms code
+- [ ] Screenshots
+- [ ] Actual HTML generated
+- [ ] Expected HTML
diff --git a/.gitignore b/.gitignore
index 5b225c0..fd140a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,9 @@ __pycache__
 tempfile
 *.swp
 
+# testing
+.tox/
+
 # coverage 
 .coverage
 htmlcov
diff --git a/.travis.yml b/.travis.yml
index 9e5bc9b..02dac43 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,14 +4,14 @@ sudo: false
 
 python:
   - "2.7"
-  - "3.2"
   - "3.3"
   - "3.4"
   - "3.5"
+  - "3.6"
 env:
   - DJANGO='django>=1.8.0,<1.9.0'
-  - DJANGO='django>=1.9.0,<1.10.0'
   - DJANGO='django>=1.10.0,<1.11.0'
+  - DJANGO='django>=1.11.0,<2.0'
   - DJANGO='https://github.com/django/django/archive/master.tar.gz'
 before_install:
   - pip install --upgrade 'pytest<3.0.0'
@@ -24,22 +24,16 @@ notifications:
   email: false
 matrix:
   exclude:
-    - python: "3.2"
+    - python: "2.7"
       env: DJANGO='https://github.com/django/django/archive/master.tar.gz'
-    - python: "3.2"
-      env: DJANGO='django>=1.10.0,<1.11.0'
-    - python: "3.2"
-      env: DJANGO='django>=1.9.0,<1.10.0'
     - python: "3.3"
       env: DJANGO='https://github.com/django/django/archive/master.tar.gz'
     - python: "3.3"
-      env: DJANGO='django>=1.10.0,<1.11.0'
+      env: DJANGO='django>=1.11.0,<2.0'
     - python: "3.3"
-      env: DJANGO='django>=1.9.0,<1.10.0'
+      env: DJANGO='django>=1.10.0,<1.11.0'
     - python: "3.4"
       env: DJANGO='https://github.com/django/django/archive/master.tar.gz'
-    - python: "3.5"
-      env: DJANGO='django>=1.7.0,<1.8.0'
   allow_failures:
     - env: DJANGO='https://github.com/django/django/archive/master.tar.gz'
 after_success:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 219f059..eba3f06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # CHANGELOG for django-crispy-forms
 
+## 1.7.0 (2017/10/17)
+
+* Fixes compatibility with Django 2.0
+* Various other fixes.
+
+See [1.7 Milestone](https://github.com/django-crispy-forms/django-crispy-forms/milestone/4?closed=1)
+for full issue list.
+
 ## 1.6.1 (2016/10/17)
 
   * Updates compatibility for Django 1.10
diff --git a/README.rst b/README.rst
index a6c6d88..07d06b8 100644
--- a/README.rst
+++ b/README.rst
@@ -2,16 +2,16 @@
 django-crispy-forms
 ===================
 
-.. image:: https://travis-ci.org/maraujop/django-crispy-forms.png?branch=dev
+.. image:: https://travis-ci.org/django-crispy-forms/django-crispy-forms.png?branch=dev
    :alt: Build Status
-   :target: https://travis-ci.org/maraujop/django-crispy-forms
+   :target: https://travis-ci.org/django-crispy-forms/django-crispy-forms
 
-.. image:: http://codecov.io/github/maraujop/django-crispy-forms/coverage.svg?branch=master
-   :target: http://codecov.io/github/maraujop/django-crispy-forms?branch=master
+.. image:: http://codecov.io/github/django-crispy-forms/django-crispy-forms/coverage.svg?branch=master
+   :target: http://codecov.io/github/django-crispy-forms/django-crispy-forms?branch=master
 
 The best way to have Django_ DRY forms. Build programmatic reusable layouts out of components, having full control of the rendered HTML without writing HTML in templates. All this without breaking the standard way of doing things in Django, so it plays nice with any other form application.
 
-`django-crispy-forms` supports Python 2.7/Python 3.2+ and Django 1.8+
+`django-crispy-forms` supports Python 2.7/Python 3.3+ and Django 1.8/Django 1.10+
 
 The application mainly provides:
 
@@ -27,7 +27,7 @@ Django-crispy-forms supports several frontend frameworks, such as Twitter `Boots
 Authors
 =======
 
-django-crispy-forms is the new django-uni-form. django-uni-form was an application created by `Daniel Greenfeld`_ that I leaded since version 0.8.0. The name change tries to better explain the purpose of the application, which changed in a significant way since its birth.
+django-crispy-forms is the new django-uni-form. django-uni-form was an application created by `Daniel Greenfeld`_ that I led since version 0.8.0. The name change tries to better explain the purpose of the application, which changed in a significant way since its birth.
 
 If you are upgrading from django-uni-form, we have `instructions`_ for helping you.
 
diff --git a/crispy_forms/__init__.py b/crispy_forms/__init__.py
index 586ed4b..9ad4313 100644
--- a/crispy_forms/__init__.py
+++ b/crispy_forms/__init__.py
@@ -1,3 +1,3 @@
 # -*- coding: utf-8 -*-
 
-__version__ = '1.6.1'
+__version__ = '1.7.0'
diff --git a/crispy_forms/bootstrap.py b/crispy_forms/bootstrap.py
index 3349c0d..093753b 100644
--- a/crispy_forms/bootstrap.py
+++ b/crispy_forms/bootstrap.py
@@ -1,13 +1,14 @@
 from __future__ import unicode_literals
+
 from random import randint
 
 from django.template import Template
-from django.template.loader import render_to_string
 from django.template.defaultfilters import slugify
+from django.template.loader import render_to_string
 
 from .compatibility import text_type
-from .layout import LayoutObject, Field, Div
-from .utils import render_field, flatatt, TEMPLATE_PACK
+from .layout import Div, Field, LayoutObject, TemplateNameMixin
+from .utils import TEMPLATE_PACK, flatatt, render_field
 
 
 class PrependedAppendedText(Field):
@@ -30,12 +31,13 @@ class PrependedAppendedText(Field):
         super(PrependedAppendedText, self).__init__(field, *args, **kwargs)
 
     def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs):
-        extra_context = {
+        extra_context = extra_context.copy() if extra_context is not None else {}
+        extra_context.update({
             'crispy_appended_text': self.appended_text,
             'crispy_prepended_text': self.prepended_text,
             'input_size': self.input_size,
             'active': getattr(self, "active", False)
-        }
+        })
         if hasattr(self, 'wrapper_class'):
             extra_context['wrapper_class'] = self.wrapper_class
         template = self.get_template_name(template_pack)
@@ -159,7 +161,7 @@ class FieldWithButtons(Div):
             )
 
 
-class StrictButton(object):
+class StrictButton(TemplateNameMixin):
     """
     Layout object for rendering an HTML button::
 
@@ -185,7 +187,7 @@ class StrictButton(object):
 
     def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs):
         self.content = Template(text_type(self.content)).render(context)
-        template = self.template % template_pack
+        template = self.get_template_name(template_pack)
         context.update({'button': self})
 
         return render_to_string(template, context.flatten())
diff --git a/crispy_forms/compatibility.py b/crispy_forms/compatibility.py
index bf2803a..e30ca20 100644
--- a/crispy_forms/compatibility.py
+++ b/crispy_forms/compatibility.py
@@ -1,7 +1,5 @@
 import sys
 
-from django.utils.functional import SimpleLazyObject
-
 try:
     basestring
 except:
@@ -18,18 +16,3 @@ else:
     binary_type = str
     string_types = basestring
     integer_types = (int, long)
-
-try:
-    # avoid RemovedInDjango19Warning by using lru_cache where available
-    from django.utils.lru_cache import lru_cache
-except ImportError:
-    from django.utils.functional import memoize
-
-    def lru_cache():
-
-        def decorator(function, cache_dict=None):
-            if cache_dict is None:
-                cache_dict = {}
-            return memoize(function, cache_dict, 1)
-
-        return decorator
diff --git a/crispy_forms/helper.py b/crispy_forms/helper.py
index 0ae1263..5c7c809 100644
--- a/crispy_forms/helper.py
+++ b/crispy_forms/helper.py
@@ -1,14 +1,21 @@
 # -*- coding: utf-8 -*-
 import re
 
-from django.core.urlresolvers import reverse, NoReverseMatch
 from django.utils.safestring import mark_safe
 
 from crispy_forms.compatibility import string_types
+from crispy_forms.exceptions import FormHelpersException
 from crispy_forms.layout import Layout
 from crispy_forms.layout_slice import LayoutSlice
-from crispy_forms.utils import render_field, flatatt, TEMPLATE_PACK, list_intersection, list_difference
-from crispy_forms.exceptions import FormHelpersException
+from crispy_forms.utils import (
+    TEMPLATE_PACK, flatatt, list_difference, list_intersection, render_field,
+)
+
+try:
+    from django.urls import reverse, NoReverseMatch
+except ImportError:
+    # Django < 1.10
+    from django.core.urlresolvers import reverse, NoReverseMatch
 
 
 class DynamicLayoutHandler(object):
@@ -131,10 +138,13 @@ class FormHelper(DynamicLayoutHandler):
         **form_id**: Generates a form id for dom identification.
             If no id provided then no id attribute is created on the form.
 
-        **form_class**: String containing separated CSS clases to be applied
+        **form_class**: String containing separated CSS classes to be applied
             to form class attribute. The form will always have by default
             'uniForm' class.
 
+        **form_group_wrapper_class**: String containing separated CSS classes to be applied
+            to each row of inputs.
+
         **form_tag**: It specifies if <form></form> tags should be rendered when using a Layout.
             If set to False it renders the form without the <form></form> tags. Defaults to True.
 
@@ -189,6 +199,7 @@ class FormHelper(DynamicLayoutHandler):
     form = None
     form_id = ''
     form_class = ''
+    form_group_wrapper_class = ''
     layout = None
     form_tag = True
     form_error_title = None
@@ -326,7 +337,16 @@ class FormHelper(DynamicLayoutHandler):
                 left_fields_to_render = list_difference(fields_to_render, form.rendered_fields)
 
                 for field in left_fields_to_render:
-                    html += render_field(field, form, self.form_style, context)
+                    # We still respect the configuration of the helper
+                    # regarding which fields to render
+                    if (
+                        self.render_unmentioned_fields or
+                        (self.render_hidden_fields and
+                         form.fields[field].widget.is_hidden) or
+                        (self.render_required_fields and
+                         form.fields[field].widget.is_required)
+                    ):
+                        html += render_field(field, form, self.form_style, context)
 
         return mark_safe(html)
 
@@ -348,15 +368,9 @@ class FormHelper(DynamicLayoutHandler):
             'field_class': self.field_class,
             'include_media': self.include_media
         }
-        # col-[lg|md|sm|xs]-<number>
-        label_size_match = re.search('(\d+)', self.label_class)
-        device_type_match = re.search('(lg|md|sm|xs)', self.label_class)
-        if label_size_match and device_type_match:
-            try:
-                items['label_size'] = int(label_size_match.groups()[0])
-                items['bootstrap_device_type'] = device_type_match.groups()[0]
-            except:
-                pass
+        bootstrap_size_match = re.findall('col-(lg|md|sm|xs)-(\d+)', self.label_class)
+        if bootstrap_size_match:
+            items['bootstrap_checkbox_offsets'] = ['col-%s-offset-%s' % m for m in bootstrap_size_match]
 
         items['attrs'] = {}
         if self.attrs:
@@ -374,6 +388,8 @@ class FormHelper(DynamicLayoutHandler):
         else:
             if template_pack == 'uni_form':
                 items['attrs']['class'] = self.attrs.get('class', '') + " uniForm"
+        if self.form_group_wrapper_class:
+            items['attrs']['form_group_wrapper_class'] = self.form_group_wrapper_class
 
         items['flat_attrs'] = flatatt(items['attrs'])
 
diff --git a/crispy_forms/layout.py b/crispy_forms/layout.py
index 57673dc..8f0b63d 100644
--- a/crispy_forms/layout.py
+++ b/crispy_forms/layout.py
@@ -5,7 +5,9 @@ from django.template.loader import render_to_string
 from django.utils.html import conditional_escape
 
 from crispy_forms.compatibility import string_types, text_type
-from crispy_forms.utils import render_field, flatatt, TEMPLATE_PACK, get_template_pack
+from crispy_forms.utils import (
+    TEMPLATE_PACK, flatatt, get_template_pack, render_field,
+)
 
 
 class TemplateNameMixin(object):
@@ -53,7 +55,7 @@ class LayoutObject(TemplateNameMixin):
                 [[0,3], 'field_name2']
             ]
         """
-        return self.get_layout_objects(string_types, greedy=True)
+        return self.get_layout_objects(string_types, index=None, greedy=True)
 
     def get_layout_objects(self, *LayoutClasses, **kwargs):
         """
@@ -306,6 +308,7 @@ class MultiField(LayoutObject):
         self.label_class = kwargs.pop('label_class', 'blockLabel')
         self.css_class = kwargs.pop('css_class', 'ctrlHolder')
         self.css_id = kwargs.pop('css_id', None)
+        self.help_text = kwargs.pop('help_text', None)
         self.template = kwargs.pop('template', self.template)
         self.field_template = kwargs.pop('field_template', self.field_template)
         self.flat_attrs = flatatt(kwargs)
@@ -416,6 +419,9 @@ class Field(LayoutObject):
 
         if not hasattr(self, 'attrs'):
             self.attrs = {}
+        else:
+            # Make sure shared state is not edited.
+            self.attrs = self.attrs.copy()
 
         if 'css_class' in kwargs:
             if 'class' in self.attrs:
@@ -465,3 +471,4 @@ class MultiWidgetField(Field):
         self.fields = list(args)
         self.attrs = kwargs.pop('attrs', {})
         self.template = kwargs.pop('template', self.template)
+        self.wrapper_class = kwargs.pop('wrapper_class', None)
diff --git a/crispy_forms/layout_slice.py b/crispy_forms/layout_slice.py
index aee2457..ca453d7 100644
--- a/crispy_forms/layout_slice.py
+++ b/crispy_forms/layout_slice.py
@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
+from crispy_forms.bootstrap import Container
 from crispy_forms.compatibility import integer_types, string_types
 from crispy_forms.exceptions import DynamicError
 from crispy_forms.layout import Fieldset, MultiField
-from crispy_forms.bootstrap import Container
 
 
 class LayoutSlice(object):
diff --git a/crispy_forms/models.py b/crispy_forms/models.py
deleted file mode 100644
index e69de29..0000000
diff --git a/crispy_forms/templates/bootstrap/field.html b/crispy_forms/templates/bootstrap/field.html
index 5726f5d..3c6e2b3 100644
--- a/crispy_forms/templates/bootstrap/field.html
+++ b/crispy_forms/templates/bootstrap/field.html
@@ -23,7 +23,7 @@
                 {% if field|is_checkbox and form_show_labels %}
                     <label for="{{ field.id_for_label }}" class="checkbox {% if field.field.required %}requiredField{% endif %}">
                         {% crispy_field field %}
-                        {{ field.label|safe }}
+                        {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
                     </label>
                     {% include 'bootstrap/layout/help_text_and_errors.html' %}
                 {% else %}
diff --git a/crispy_forms/templates/bootstrap/layout/checkboxselectmultiple.html b/crispy_forms/templates/bootstrap/layout/checkboxselectmultiple.html
index 4139a02..3f83471 100644
--- a/crispy_forms/templates/bootstrap/layout/checkboxselectmultiple.html
+++ b/crispy_forms/templates/bootstrap/layout/checkboxselectmultiple.html
@@ -6,7 +6,7 @@
 
     {% for choice in field.field.choices %}
         <label class="checkbox{% if inline_class %} {{ inline_class }}{% endif %}">
-            <input type="checkbox"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
+            <input type="checkbox"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
         </label>
     {% endfor %}
 
diff --git a/crispy_forms/templates/bootstrap/layout/radioselect.html b/crispy_forms/templates/bootstrap/layout/radioselect.html
index aeb33c7..521d17c 100644
--- a/crispy_forms/templates/bootstrap/layout/radioselect.html
+++ b/crispy_forms/templates/bootstrap/layout/radioselect.html
@@ -5,8 +5,8 @@
     {% include 'bootstrap/layout/field_errors_block.html' %}
 
     {% for choice in field.field.choices %}
-        <label class="radio{% if inline_class %} {{ inline_class }}{% endif %}">
-            <input type="radio"{% if choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
+        <label for="id_{{ field.html_name }}_{{ forloop.counter }}" class="radio{% if inline_class %} {{ inline_class }}{% endif %}">
+            <input type="radio"{% if choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
         </label>
     {% endfor %}
 
diff --git a/crispy_forms/templates/bootstrap/table_inline_formset.html b/crispy_forms/templates/bootstrap/table_inline_formset.html
index 0be9241..4b3e778 100644
--- a/crispy_forms/templates/bootstrap/table_inline_formset.html
+++ b/crispy_forms/templates/bootstrap/table_inline_formset.html
@@ -31,6 +31,12 @@
         </thead>
 
         <tbody>
+            <tr class="hidden empty-form">
+                {% for field in formset.empty_form %}
+                    {% include 'bootstrap/field.html' with tag="td" form_show_labels=False %}
+                {% endfor %}
+            </tr>
+
             {% for form in formset %}
                 {% if form_show_errors and not form.is_extra %}
                     {% include "bootstrap/errors.html" %}
diff --git a/crispy_forms/templates/bootstrap3/field.html b/crispy_forms/templates/bootstrap3/field.html
index 02db7d9..0d569a5 100644
--- a/crispy_forms/templates/bootstrap3/field.html
+++ b/crispy_forms/templates/bootstrap3/field.html
@@ -6,7 +6,7 @@
     {% if field|is_checkbox %}
         <div class="form-group">
         {% if label_class %}
-            <div class="controls col-{{ bootstrap_device_type }}-offset-{{ label_size }} {{ field_class }}">
+            <div class="controls {% for offset in bootstrap_checkbox_offsets %}{{ offset }} {% endfor %}{{ field_class }}">
         {% endif %}
     {% endif %}
     <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-error{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
@@ -28,7 +28,7 @@
             {% if field|is_checkbox and form_show_labels %}
                 <label for="{{ field.id_for_label }}" class="{% if field.field.required %} requiredField{% endif %}">
                     {% crispy_field field %}
-                    {{ field.label|safe }}
+                    {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
                 </label>
                 {% include 'bootstrap3/layout/help_text_and_errors.html' %}
             {% else %}
diff --git a/crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple.html b/crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple.html
index 919ae0f..d4816a0 100644
--- a/crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple.html
+++ b/crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple.html
@@ -8,7 +8,7 @@
 
       {% if not inline_class %}<div class="checkbox">{% endif %}
         <label class="{% if inline_class %}checkbox-{{ inline_class }}{% endif %}">
-            <input type="checkbox"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
+            <input type="checkbox"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
         </label>
       {% if not inline_class %}</div>{% endif %}
     {% endfor %}
diff --git a/crispy_forms/templates/bootstrap3/layout/inline_field.html b/crispy_forms/templates/bootstrap3/layout/inline_field.html
index 9677181..8b1dc77 100644
--- a/crispy_forms/templates/bootstrap3/layout/inline_field.html
+++ b/crispy_forms/templates/bootstrap3/layout/inline_field.html
@@ -4,14 +4,14 @@
     {{ field }}
 {% else %}
     {% if field|is_checkbox %}
-        <div id="div_{{ field.auto_id }}" class="checkbox">
+        <div id="div_{{ field.auto_id }}" class="checkbox{% if wrapper_class %} {{ wrapper_class }}{% endif %}">
             <label for="{{ field.id_for_label }}" class="{% if field.field.required %} requiredField{% endif %}">
                 {% crispy_field field 'class' 'checkbox' %}
                 {{ field.label|safe }}
             </label>
         </div>
     {% else %}
-        <div id="div_{{ field.auto_id }}" class="form-group">
+        <div id="div_{{ field.auto_id }}" class="form-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}">
             <label for="{{ field.id_for_label }}" class="sr-only{% if field.field.required %} requiredField{% endif %}">
                 {{ field.label|safe }}
             </label>
diff --git a/crispy_forms/templates/bootstrap3/layout/multifield.html b/crispy_forms/templates/bootstrap3/layout/multifield.html
index 0a2c050..a7cda4b 100644
--- a/crispy_forms/templates/bootstrap3/layout/multifield.html
+++ b/crispy_forms/templates/bootstrap3/layout/multifield.html
@@ -1,27 +1,25 @@
-{% load crispy_forms_field %}
+<div {% if multifield.css_id or errors %}id="{{ multifield.css_id }}"{% endif %}
+    {% if multifield.css_class %}class="{{ multifield.css_class }}"{% endif %}
+    {{ multifield.flat_attrs|safe }}>
 
-{% if field.is_hidden %}
-    {{ field }}
-{% else %}
-
-    {% if field.label %}
-        <label for="{{ field.id_for_label }}"{% if labelclass %} class="{{ labelclass }}"{% endif %}>
-    {% endif %}
-
-    {% if field|is_checkbox %}
-        {% crispy_field field %}
+    {% if form_show_errors %}
+        <div class="alert alert-danger" role="alert">
+        {% for field in multifield.bound_fields %}
+            {% if field.errors %}
+                {% for error in field.errors %}
+                    <p id="error_{{ forloop.counter }}_{{ field.auto_id }}">{{ error }}</p>
+                {% endfor %}
+            {% endif %}
+        {% endfor %}
+        </div>
     {% endif %}
 
-    {% if field.label %}
-        {{ field.label }}
+    {% if multifield.label_html %}
+        <p {% if multifield.label_class %}class="{{ multifield.label_class }}"{% endif %}>{{ multifield.label_html|safe }}</p>
     {% endif %}
 
-    {% if not field|is_checkbox %}
-        {% crispy_field field %}
-    {% endif %}
-
-    {% if field.label %}
-        </label>
-    {% endif %}
+    <div class="multiField">
+        {{ fields_output|safe }}
+    </div>
 
-{% endif %}
+</div>
diff --git a/crispy_forms/templates/bootstrap3/layout/radioselect.html b/crispy_forms/templates/bootstrap3/layout/radioselect.html
index 872dcda..ea6e80a 100644
--- a/crispy_forms/templates/bootstrap3/layout/radioselect.html
+++ b/crispy_forms/templates/bootstrap3/layout/radioselect.html
@@ -6,8 +6,8 @@
 
     {% for choice in field.field.choices %}
       {% if not inline_class %}<div class="radio">{% endif %}
-        <label class="{% if inline_class %}radio-{{ inline_class }}{% endif %}">
-            <input type="radio"{% if choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
+        <label for="id_{{ field.html_name }}_{{ forloop.counter }}" class="{% if inline_class %}radio-{{ inline_class }}{% endif %}">
+            <input type="radio"{% if choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>{{ choice.1|unlocalize }}
         </label>
       {% if not inline_class %}</div>{% endif %}
     {% endfor %}
diff --git a/crispy_forms/templates/bootstrap3/multifield.html b/crispy_forms/templates/bootstrap3/multifield.html
new file mode 100644
index 0000000..47fd4b3
--- /dev/null
+++ b/crispy_forms/templates/bootstrap3/multifield.html
@@ -0,0 +1,39 @@
+{% load crispy_forms_field %}
+
+{% if field.is_hidden %}
+    {{ field }}
+{% else %}
+
+    {% if field|is_checkbox %}
+        {% if field.errors %}<div class="has-error">{% endif %}
+        <div class="checkbox">
+            {% if field.label %}
+                <label for="{{ field.id_for_label }}"{% if labelclass %} class="{{ labelclass }}"{% endif %}>
+            {% endif %}
+            
+            {% crispy_field field %}
+            {{ field.label }}
+            
+            {% if field.label %}
+                </label>
+            {% endif %}
+            {% if field.help_text %}
+                <p id="help_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</span>
+            {% endif %}
+        </div>
+        {% if field.errors %}</div>{% endif %}
+    {% else %}
+        <div class="form-group {% if field.errors %}has-error{% endif %}">
+            {% if field.label %}
+                <label class="control-label" for="{{ field.id_for_label }}"{% if labelclass %} class="{{ labelclass }}"{% endif %}>
+                {{ field.label }}
+                </label>
+            {% endif %}
+            {% crispy_field field %}
+            {% if field.help_text %}
+                <span id="help_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</span>
+            {% endif %}
+        </div>
+    {% endif %}
+
+{% endif %}
diff --git a/crispy_forms/templates/bootstrap3/table_inline_formset.html b/crispy_forms/templates/bootstrap3/table_inline_formset.html
index df26b3a..3a622fc 100644
--- a/crispy_forms/templates/bootstrap3/table_inline_formset.html
+++ b/crispy_forms/templates/bootstrap3/table_inline_formset.html
@@ -31,6 +31,12 @@
         </thead>
 
         <tbody>
+            <tr class="hidden empty-form">
+                {% for field in formset.empty_form %}
+                    {% include 'bootstrap3/field.html' with tag="td" form_show_labels=False %}
+                {% endfor %}
+            </tr>
+
             {% for form in formset %}
                 {% if form_show_errors and not form.is_extra %}
                     {% include "bootstrap3/errors.html" %}
diff --git a/crispy_forms/templates/bootstrap4/field.html b/crispy_forms/templates/bootstrap4/field.html
index bf2e71d..3d12cb2 100644
--- a/crispy_forms/templates/bootstrap4/field.html
+++ b/crispy_forms/templates/bootstrap4/field.html
@@ -6,10 +6,10 @@
     {% if field|is_checkbox %}
         <div class="form-group">
         {% if label_class %}
-            <div class="col-{{ bootstrap_device_type }}-offset-{{ label_size }} {{ field_class }}">
+            <div class="{% for offset in bootstrap_checkbox_offsets %}{{ offset }} {% endfor %}{{ field_class }}">
         {% endif %}
     {% endif %}
-    <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-danger{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
+    <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_group_wrapper_class %} {{ form_group_wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-danger{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
         {% if field.label and not field|is_checkbox and form_show_labels %}
             <label for="{{ field.id_for_label }}" class="form-control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
                 {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
@@ -28,7 +28,7 @@
             {% if field|is_checkbox and form_show_labels %}
                 <label for="{{ field.id_for_label }}" class="{% if field.field.required %} requiredField{% endif %}">
                     {% crispy_field field %}
-                    {{ field.label|safe }}
+                    {{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
                 </label>
                 {% include 'bootstrap4/layout/help_text_and_errors.html' %}
             {% else %}
diff --git a/crispy_forms/templates/bootstrap4/inputs.html b/crispy_forms/templates/bootstrap4/inputs.html
index ee4e1ac..6006a74 100644
--- a/crispy_forms/templates/bootstrap4/inputs.html
+++ b/crispy_forms/templates/bootstrap4/inputs.html
@@ -1,5 +1,5 @@
 {% if inputs %}
-    <div class="form-group">
+    <div class="form-group{% if form_group_wrapper_class %} {{ form_group_wrapper_class }}{% endif %}">
         {% if label_class %}
             <div class="aab {{ label_class }}"></div>
         {% endif %}
diff --git a/crispy_forms/templates/bootstrap4/layout/checkboxselectmultiple.html b/crispy_forms/templates/bootstrap4/layout/checkboxselectmultiple.html
index 36453bd..6707541 100644
--- a/crispy_forms/templates/bootstrap4/layout/checkboxselectmultiple.html
+++ b/crispy_forms/templates/bootstrap4/layout/checkboxselectmultiple.html
@@ -8,7 +8,7 @@
 
       {% if not inline_class %}<div class="checkbox">{% endif %}
         <label class="{% if inline_class %}checkbox-{{ inline_class }}{% endif %}">
-            <input type="checkbox"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>
+            <input type="checkbox"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>
             {{ choice.1|unlocalize }}
         </label>
       {% if not inline_class %}</div>{% endif %}
diff --git a/crispy_forms/templates/bootstrap4/layout/field_errors.html b/crispy_forms/templates/bootstrap4/layout/field_errors.html
index b49cdc3..f649872 100644
--- a/crispy_forms/templates/bootstrap4/layout/field_errors.html
+++ b/crispy_forms/templates/bootstrap4/layout/field_errors.html
@@ -1,5 +1,5 @@
 {% if form_show_errors and field.errors %}
     {% for error in field.errors %}
-        <span id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block"><strong>{{ error }}</strong></span>
+        <span id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="invalid-feedback"><strong>{{ error }}</strong></span>
     {% endfor %}
 {% endif %}
diff --git a/crispy_forms/templates/bootstrap4/layout/field_errors_block.html b/crispy_forms/templates/bootstrap4/layout/field_errors_block.html
index fb02dee..e788b79 100644
--- a/crispy_forms/templates/bootstrap4/layout/field_errors_block.html
+++ b/crispy_forms/templates/bootstrap4/layout/field_errors_block.html
@@ -1,5 +1,5 @@
 {% if form_show_errors and field.errors %}
     {% for error in field.errors %}
-        <p id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block"><strong>{{ error }}</strong></p>
+        <p id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="invalid-feedback"><strong>{{ error }}</strong></p>
     {% endfor %}
 {% endif %}
diff --git a/crispy_forms/templates/bootstrap4/layout/inline_field.html b/crispy_forms/templates/bootstrap4/layout/inline_field.html
index 9677181..8b1dc77 100644
--- a/crispy_forms/templates/bootstrap4/layout/inline_field.html
+++ b/crispy_forms/templates/bootstrap4/layout/inline_field.html
@@ -4,14 +4,14 @@
     {{ field }}
 {% else %}
     {% if field|is_checkbox %}
-        <div id="div_{{ field.auto_id }}" class="checkbox">
+        <div id="div_{{ field.auto_id }}" class="checkbox{% if wrapper_class %} {{ wrapper_class }}{% endif %}">
             <label for="{{ field.id_for_label }}" class="{% if field.field.required %} requiredField{% endif %}">
                 {% crispy_field field 'class' 'checkbox' %}
                 {{ field.label|safe }}
             </label>
         </div>
     {% else %}
-        <div id="div_{{ field.auto_id }}" class="form-group">
+        <div id="div_{{ field.auto_id }}" class="form-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}">
             <label for="{{ field.id_for_label }}" class="sr-only{% if field.field.required %} requiredField{% endif %}">
                 {{ field.label|safe }}
             </label>
diff --git a/crispy_forms/templates/bootstrap4/layout/prepended_appended_text.html b/crispy_forms/templates/bootstrap4/layout/prepended_appended_text.html
index 75ec9db..596c9a3 100644
--- a/crispy_forms/templates/bootstrap4/layout/prepended_appended_text.html
+++ b/crispy_forms/templates/bootstrap4/layout/prepended_appended_text.html
@@ -3,7 +3,7 @@
 {% if field.is_hidden %}
     {{ field }}
 {% else %}
-    <div id="div_{{ field.auto_id }}" class="form-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors and field.errors %} has-danger{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
+    <div id="div_{{ field.auto_id }}" class="form-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_group_wrapper_class %} {{ form_group_wrapper_class }}{% endif %}{% if form_show_errors and field.errors %} has-danger{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
 
         {% if field.label and form_show_labels %}
             <label for="{{ field.id_for_label }}" class="form-control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
diff --git a/crispy_forms/templates/bootstrap4/layout/radioselect.html b/crispy_forms/templates/bootstrap4/layout/radioselect.html
index 2efe495..6f8c83e 100644
--- a/crispy_forms/templates/bootstrap4/layout/radioselect.html
+++ b/crispy_forms/templates/bootstrap4/layout/radioselect.html
@@ -6,8 +6,8 @@
 
     {% for choice in field.field.choices %}
       {% if not inline_class %}<div class="radio">{% endif %}
-        <label class="{% if inline_class %}radio-{{ inline_class }}{% endif %}">
-            <input type="radio"{% if choice.0|stringformat:"s" == field.value|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>
+        <label for="id_{{ field.html_name }}_{{ forloop.counter }}" class="{% if inline_class %}radio-{{ inline_class }}{% endif %}">
+            <input type="radio"{% if choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}>
             {{ choice.1|unlocalize }}
         </label>
       {% if not inline_class %}</div>{% endif %}
diff --git a/crispy_forms/templates/bootstrap4/table_inline_formset.html b/crispy_forms/templates/bootstrap4/table_inline_formset.html
index b129a8c..5ffbe01 100644
--- a/crispy_forms/templates/bootstrap4/table_inline_formset.html
+++ b/crispy_forms/templates/bootstrap4/table_inline_formset.html
@@ -31,6 +31,12 @@
         </thead>
 
         <tbody>
+            <tr class="hidden empty-form">
+                {% for field in formset.empty_form %}
+                    {% include 'bootstrap4/field.html' with tag="td" form_show_labels=False %}
+                {% endfor %}
+            </tr>
+
             {% for form in formset %}
                 {% if form_show_errors and not form.is_extra %}
                     {% include "bootstrap4/errors.html" %}
diff --git a/crispy_forms/templatetags/crispy_forms_field.py b/crispy_forms/templatetags/crispy_forms_field.py
index be3a33d..827ab59 100644
--- a/crispy_forms/templatetags/crispy_forms_field.py
+++ b/crispy_forms/templatetags/crispy_forms_field.py
@@ -3,11 +3,9 @@ try:
 except ImportError:
     izip = zip
 
-import django
-from django import forms
-from django import template
-from django.template import loader, Context
+from django import forms, template
 from django.conf import settings
+from django.template import Context, loader
 
 from crispy_forms.utils import TEMPLATE_PACK, get_template_pack
 
@@ -41,7 +39,12 @@ def is_checkboxselectmultiple(field):
 
 @register.filter
 def is_file(field):
-    return isinstance(field.field.widget, forms.ClearableFileInput)
+    return isinstance(field.field.widget, forms.FileInput)
+
+
+ at register.filter
+def is_multivalue(field):
+    return isinstance(field.field.widget, forms.MultiWidget)
 
 
 @register.filter
@@ -92,7 +95,9 @@ class CrispyFieldNode(template.Node):
         # If template pack has been overridden in FormHelper we can pick it from context
         template_pack = context.get('template_pack', TEMPLATE_PACK)
 
-        widgets = getattr(field.field.widget, 'widgets', [field.field.widget])
+        # There are special django widgets that wrap actual widgets,
+        # such as forms.widgets.MultiWidget, admin.widgets.RelatedFieldWidgetWrapper
+        widgets = getattr(field.field.widget, 'widgets', [getattr(field.field.widget, 'widget', field.field.widget)])
 
         if isinstance(attrs, dict):
             attrs = [attrs] * len(widgets)
@@ -115,13 +120,24 @@ class CrispyFieldNode(template.Node):
                 css_class = class_name
 
             if (
-                template_pack in ['bootstrap3', 'bootstrap4']
+                template_pack in ['bootstrap3']
                 and not is_checkbox(field)
                 and not is_file(field)
+                and not is_multivalue(field)
             ):
                 css_class += ' form-control'
                 if field.errors:
                     css_class += ' form-control-danger'
+            
+            if (
+                template_pack in ['bootstrap4']
+                and not is_checkbox(field)
+                and not is_file(field)
+                and not is_multivalue(field)
+            ):
+                css_class += ' form-control'
+                if field.errors:
+                    css_class += ' is-invalid'
 
             widget.attrs['class'] = css_class
 
@@ -183,7 +199,6 @@ def crispy_addon(field, append="", prepend="", form_show_labels=True):
         if not prepend and not append:
             raise TypeError("Expected a prepend and/or append argument")
 
-        if django.VERSION >= (1, 8):
-            context = context.flatten()
+        context = context.flatten()
 
     return template.render(context)
diff --git a/crispy_forms/templatetags/crispy_forms_filters.py b/crispy_forms/templatetags/crispy_forms_filters.py
index 88dbd00..de07c0e 100644
--- a/crispy_forms/templatetags/crispy_forms_filters.py
+++ b/crispy_forms/templatetags/crispy_forms_filters.py
@@ -1,16 +1,15 @@
 # -*- coding: utf-8 -*-
-import django
+from django import template
 from django.conf import settings
 from django.forms import forms
 from django.forms.formsets import BaseFormSet
 from django.template import Context
 from django.template.loader import get_template
+from django.utils.lru_cache import lru_cache
 from django.utils.safestring import mark_safe
-from django import template
 
-from crispy_forms.compatibility import lru_cache
 from crispy_forms.exceptions import CrispyError
-from crispy_forms.utils import flatatt, TEMPLATE_PACK
+from crispy_forms.utils import TEMPLATE_PACK, flatatt
 
 
 @lru_cache()
@@ -53,7 +52,7 @@ def as_crispy_form(form, template_pack=TEMPLATE_PACK, label_class="", field_clas
             'form_show_labels': True,
             'label_class': label_class,
             'field_class': field_class,
-        })
+        }).flatten()
     else:
         template = uni_form_template(template_pack)
         c = Context({
@@ -62,10 +61,7 @@ def as_crispy_form(form, template_pack=TEMPLATE_PACK, label_class="", field_clas
             'form_show_labels': True,
             'label_class': label_class,
             'field_class': field_class,
-        })
-
-    if django.VERSION >= (1, 8):
-        c = c.flatten()
+        }).flatten()
 
     return template.render(c)
 
@@ -84,19 +80,16 @@ def as_crispy_errors(form, template_pack=TEMPLATE_PACK):
     """
     if isinstance(form, BaseFormSet):
         template = get_template('%s/errors_formset.html' % template_pack)
-        c = Context({'formset': form})
+        c = Context({'formset': form}).flatten()
     else:
         template = get_template('%s/errors.html' % template_pack)
-        c = Context({'form': form})
-
-    if django.VERSION >= (1, 8):
-        c = c.flatten()
+        c = Context({'form': form}).flatten()
 
     return template.render(c)
 
 
 @register.filter(name='as_crispy_field')
-def as_crispy_field(field, template_pack=TEMPLATE_PACK):
+def as_crispy_field(field, template_pack=TEMPLATE_PACK, label_class="", field_class=""):
     """
     Renders a form field like a django-crispy-forms field::
 
@@ -110,12 +103,24 @@ def as_crispy_field(field, template_pack=TEMPLATE_PACK):
     if not isinstance(field, forms.BoundField) and settings.DEBUG:
         raise CrispyError('|as_crispy_field got passed an invalid or inexistent field')
 
... 2297 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-crispy-forms.git



More information about the Python-modules-commits mailing list