Commit 12ffacb1 authored by Michael Fladischer's avatar Michael Fladischer

New upstream version 1.7.0

parent 1c49d963
[run]
branch = True
* 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
......@@ -21,6 +21,9 @@ __pycache__
tempfile
*.swp
# testing
.tox/
# coverage
.coverage
htmlcov
......
......@@ -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:
......
# 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
......
......@@ -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.
......
# -*- coding: utf-8 -*-
__version__ = '1.6.1'
__version__ = '1.7.0'
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())
......
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
# -*- 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'])
......
......@@ -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)
# -*- 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):
......
......@@ -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 %}
......
......@@ -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 %}
......
......@@ -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 %}
......
......@@ -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" %}
......
......@@ -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 %}
......
......@@ -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 %}
......
......@@ -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>
......
{% 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>
......@@ -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 %}
......
{% 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 %}
......@@ -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" %}
......
......@@ -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 %}
......
{% 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 %}
......
......@@ -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 %}
......
{% 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 %}
{% 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 %}
......@@ -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>
......
......@@ -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 %}">
......
......@@ -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 %}
......
......@@ -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" %}
......
......@@ -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)
@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])