├── __init__.py ├── docs ├── __init__.py ├── .DS_Store ├── images │ ├── alert.png │ ├── appended_text.png │ ├── form_actions.png │ ├── inline_radios.jpg │ ├── prepended_text.png │ ├── strict_button.png │ ├── field_with_buttons.png │ ├── inline_checkboxes.png │ ├── tab_and_tabholder.jpg │ ├── appended_prepended_text.png │ ├── bootstrap3_inline_form.jpg │ ├── bootstrap3_horizontal_form.jpg │ └── accordiongroup_and_accordion.jpg ├── _themes │ ├── kr │ │ ├── theme.conf │ │ ├── relations.html │ │ ├── layout.html │ │ └── static │ │ │ ├── small_flask.css │ │ │ └── flasky.css_t │ ├── kr_small │ │ ├── theme.conf │ │ ├── layout.html │ │ └── static │ │ │ └── flasky.css_t │ ├── LICENSE │ └── flask_theme_support.py ├── api_helpers.rst ├── api_layout.rst ├── settings.py ├── api_templatetags.rst ├── filters.rst ├── _templates │ └── sidebarintro.html ├── index.rst ├── concepts.rst ├── Makefile ├── make.bat ├── template_packs.rst ├── faq.rst ├── crispy_tag_formsets.rst ├── install.rst ├── contributing.rst ├── conf.py └── form_helper.rst ├── crispy_forms ├── models.py ├── templatetags │ ├── __init__.py │ ├── crispy_forms_utils.py │ ├── crispy_forms_filters.py │ └── crispy_forms_field.py ├── __init__.py ├── tests │ ├── templates │ │ ├── crispy_render_template.html │ │ ├── custom_field_template.html │ │ └── custom_form_template.html │ ├── __init__.py │ ├── urls.py │ ├── runtests.py │ ├── runtests_uniform.py │ ├── runtests_bootstrap.py │ ├── runtests_bootstrap3.py │ ├── base.py │ ├── test_settings.py │ ├── utils.py │ ├── forms.py │ └── test_tags.py ├── templates │ ├── bootstrap │ │ ├── layout │ │ │ ├── button.html │ │ │ ├── formactions.html │ │ │ ├── tab.html │ │ │ ├── tab-link.html │ │ │ ├── div.html │ │ │ ├── field_errors.html │ │ │ ├── field_errors_block.html │ │ │ ├── alert.html │ │ │ ├── help_text.html │ │ │ ├── fieldset.html │ │ │ ├── help_text_and_errors.html │ │ │ ├── baseinput.html │ │ │ ├── uneditable_input.html │ │ │ ├── multifield.html │ │ │ ├── radioselect_inline.html │ │ │ ├── checkboxselectmultiple_inline.html │ │ │ ├── radioselect.html │ │ │ ├── checkboxselectmultiple.html │ │ │ ├── field_with_buttons.html │ │ │ └── prepended_appended_text.html │ │ ├── accordion.html │ │ ├── uni_formset.html │ │ ├── display_form.html │ │ ├── errors.html │ │ ├── uni_form.html │ │ ├── errors_formset.html │ │ ├── accordion-group.html │ │ ├── whole_uni_form.html │ │ ├── betterform.html │ │ ├── whole_uni_formset.html │ │ ├── field.html │ │ └── table_inline_formset.html │ ├── bootstrap3 │ │ ├── layout │ │ │ ├── button.html │ │ │ ├── div.html │ │ │ ├── tab-link.html │ │ │ ├── tab.html │ │ │ ├── field_errors.html │ │ │ ├── field_errors_block.html │ │ │ ├── alert.html │ │ │ ├── help_text.html │ │ │ ├── formactions.html │ │ │ ├── fieldset.html │ │ │ ├── help_text_and_errors.html │ │ │ ├── baseinput.html │ │ │ ├── uneditable_input.html │ │ │ ├── multifield.html │ │ │ ├── checkboxselectmultiple_inline.html │ │ │ ├── radioselect_inline.html │ │ │ ├── radioselect.html │ │ │ ├── checkboxselectmultiple.html │ │ │ ├── inline_field.html │ │ │ ├── field_with_buttons.html │ │ │ └── prepended_appended_text.html │ │ ├── accordion.html │ │ ├── uni_formset.html │ │ ├── display_form.html │ │ ├── errors.html │ │ ├── uni_form.html │ │ ├── errors_formset.html │ │ ├── inputs.html │ │ ├── accordion-group.html │ │ ├── whole_uni_form.html │ │ ├── betterform.html │ │ ├── whole_uni_formset.html │ │ ├── table_inline_formset.html │ │ ├── field.html │ │ └── field.html.bk │ └── uni_form │ │ ├── layout │ │ ├── div.html │ │ ├── buttonholder.html │ │ ├── fieldset.html │ │ ├── baseinput.html │ │ └── multifield.html │ │ ├── uni_formset.html │ │ ├── display_form.html │ │ ├── errors.html │ │ ├── errors_formset.html │ │ ├── uni_form.html │ │ ├── whole_uni_form.html │ │ ├── betterform.html │ │ ├── multifield.html │ │ ├── whole_uni_formset.html │ │ ├── field.strict.html │ │ └── field.html ├── compatibility.py ├── exceptions.py ├── LICENSE ├── base.py ├── layout_slice.py └── utils.py ├── requirements.txt ├── Makefile ├── MANIFEST.in ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── setup.py ├── CONTRIBUTORS.txt └── README.rst /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /crispy_forms/models.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /crispy_forms/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -i http://simple.crate.io/ 2 | django 3 | -------------------------------------------------------------------------------- /crispy_forms/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __version__ = '1.5.0' 4 | -------------------------------------------------------------------------------- /docs/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/.DS_Store -------------------------------------------------------------------------------- /docs/images/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/alert.png -------------------------------------------------------------------------------- /crispy_forms/tests/templates/crispy_render_template.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | 3 | {% crispy form %} 4 | -------------------------------------------------------------------------------- /docs/images/appended_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/appended_text.png -------------------------------------------------------------------------------- /docs/images/form_actions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/form_actions.png -------------------------------------------------------------------------------- /docs/images/inline_radios.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/inline_radios.jpg -------------------------------------------------------------------------------- /docs/images/prepended_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/prepended_text.png -------------------------------------------------------------------------------- /docs/images/strict_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/strict_button.png -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/button.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/button.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crispy_forms/tests/templates/custom_field_template.html: -------------------------------------------------------------------------------- 1 |

Special custom field

2 | {% include 'bootstrap/field.html' %} 3 | -------------------------------------------------------------------------------- /docs/images/field_with_buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/field_with_buttons.png -------------------------------------------------------------------------------- /docs/images/inline_checkboxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/inline_checkboxes.png -------------------------------------------------------------------------------- /docs/images/tab_and_tabholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/tab_and_tabholder.jpg -------------------------------------------------------------------------------- /crispy_forms/tests/templates/custom_form_template.html: -------------------------------------------------------------------------------- 1 |

Special custom form

2 | {% include "bootstrap/whole_uni_form.html" %} 3 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/accordion.html: -------------------------------------------------------------------------------- 1 |
2 | {{ content|safe }} 3 |
4 | -------------------------------------------------------------------------------- /docs/images/appended_prepended_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/appended_prepended_text.png -------------------------------------------------------------------------------- /docs/images/bootstrap3_inline_form.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/bootstrap3_inline_form.jpg -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/accordion.html: -------------------------------------------------------------------------------- 1 |
2 | {{ content|safe }} 3 |
4 | -------------------------------------------------------------------------------- /docs/images/bootstrap3_horizontal_form.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/bootstrap3_horizontal_form.jpg -------------------------------------------------------------------------------- /docs/images/accordiongroup_and_accordion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/variable/django-crispy-forms/dev/docs/images/accordiongroup_and_accordion.jpg -------------------------------------------------------------------------------- /docs/_themes/kr/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | pygments_style = flask_theme_support.FlaskyStyle 5 | 6 | [options] 7 | touch_icon = 8 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/formactions.html: -------------------------------------------------------------------------------- 1 | 2 | {{ fields_output|safe }} 3 | 4 | -------------------------------------------------------------------------------- /crispy_forms/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from .test_tags import * 2 | from .test_layout import * 3 | from .test_layout_objects import * 4 | from .test_form_helper import * 5 | from .test_dynamic_api import * 6 | -------------------------------------------------------------------------------- /docs/api_helpers.rst: -------------------------------------------------------------------------------- 1 | .. _`helpers api`: 2 | 3 | =================================== 4 | API helpers 5 | =================================== 6 | 7 | 8 | .. automodule:: helper 9 | :members: 10 | -------------------------------------------------------------------------------- /docs/api_layout.rst: -------------------------------------------------------------------------------- 1 | .. _`layout api`: 2 | 3 | =================================== 4 | API Layout 5 | =================================== 6 | 7 | 8 | .. automodule:: layout 9 | :members: 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: develop test 2 | 3 | develop: 4 | pip install -q "file://`pwd`#egg=django-crispy-forms[tests]" 5 | pip install -q -e . --use-mirrors 6 | 7 | test: develop 8 | cd crispy_forms/tests && python runtests.py -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/tab.html: -------------------------------------------------------------------------------- 1 | 2 | {{ links|safe }} 3 | 4 |
5 | {{ content|safe }} 6 |
7 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/tab-link.html: -------------------------------------------------------------------------------- 1 |
  • {{ link.name|capfirst }}{% if tab.errors %}!{% endif %}
  • 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include CONTRIBUTORS.txt 2 | include LICENSE.txt 3 | include MANIFEST.in 4 | include README.rst 5 | recursive-include crispy_forms/static * 6 | recursive-include crispy_forms/templates * 7 | recursive-include crispy_forms/tests * 8 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/div.html: -------------------------------------------------------------------------------- 1 |
    3 | {{ fields|safe }} 4 |
    5 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/div.html: -------------------------------------------------------------------------------- 1 |
    3 | {{ fields|safe }} 4 |
    5 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/tab-link.html: -------------------------------------------------------------------------------- 1 |
  • {{ link.name|capfirst }}{% if tab.errors %}!{% endif %}
  • 2 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/tab.html: -------------------------------------------------------------------------------- 1 | 2 | {{ links|safe }} 3 | 4 |
    5 | {{ content|safe }} 6 |
    7 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/layout/div.html: -------------------------------------------------------------------------------- 1 |
    3 | {{ fields|safe }} 4 |
    5 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | nosidebar = true 5 | pygments_style = flask_theme_support.FlaskyStyle 6 | 7 | [options] 8 | index_logo = '' 9 | index_logo_height = 120px 10 | github_fork = '' 11 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/layout/buttonholder.html: -------------------------------------------------------------------------------- 1 |
    3 | {{ fields_output|safe }} 4 |
    5 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/field_errors.html: -------------------------------------------------------------------------------- 1 | {% if form_show_errors and field.errors %} 2 | {% for error in field.errors %} 3 | {{ error }} 4 | {% endfor %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/field_errors_block.html: -------------------------------------------------------------------------------- 1 | {% if form_show_errors and field.errors %} 2 | {% for error in field.errors %} 3 |

    {{ error }}

    4 | {% endfor %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/field_errors.html: -------------------------------------------------------------------------------- 1 | {% if form_show_errors and field.errors %} 2 | {% for error in field.errors %} 3 | {{ error }} 4 | {% endfor %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/field_errors_block.html: -------------------------------------------------------------------------------- 1 | {% if form_show_errors and field.errors %} 2 | {% for error in field.errors %} 3 |

    {{ error }}

    4 | {% endfor %} 5 | {% endif %} 6 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/uni_formset.html: -------------------------------------------------------------------------------- 1 | {% with formset.management_form as form %} 2 | {% include 'uni_form/uni_form.html' %} 3 | {% endwith %} 4 | {% for form in formset %} 5 |
    6 | {% include 'uni_form/uni_form.html' %} 7 |
    8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/uni_formset.html: -------------------------------------------------------------------------------- 1 | {% with formset.management_form as form %} 2 | {% include 'bootstrap/uni_form.html' %} 3 | {% endwith %} 4 | {% for form in formset %} 5 |
    6 | {% include 'bootstrap/uni_form.html' %} 7 |
    8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/uni_formset.html: -------------------------------------------------------------------------------- 1 | {% with formset.management_form as form %} 2 | {% include 'bootstrap3/uni_form.html' %} 3 | {% endwith %} 4 | {% for form in formset %} 5 |
    6 | {% include 'bootstrap3/uni_form.html' %} 7 |
    8 | {% endfor %} 9 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/alert.html: -------------------------------------------------------------------------------- 1 | 2 | {% if dismiss %}{% endif %} 3 | {{ content|safe }} 4 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/alert.html: -------------------------------------------------------------------------------- 1 | 2 | {% if dismiss %}{% endif %} 3 | {{ content|safe }} 4 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/display_form.html: -------------------------------------------------------------------------------- 1 | {% if form.form_html %} 2 | {{ form.media }} 3 | {% if form_show_errors %} 4 | {% include "uni_form/errors.html" %} 5 | {% endif %} 6 | {{ form.form_html }} 7 | {% else %} 8 | {% include "uni_form/uni_form.html" %} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/display_form.html: -------------------------------------------------------------------------------- 1 | {% if form.form_html %} 2 | {{ form.media }} 3 | {% if form_show_errors %} 4 | {% include "bootstrap/errors.html" %} 5 | {% endif %} 6 | {{ form.form_html }} 7 | {% else %} 8 | {% include "bootstrap/uni_form.html" %} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/display_form.html: -------------------------------------------------------------------------------- 1 | {% if form.form_html %} 2 | {{ form.media }} 3 | {% if form_show_errors %} 4 | {% include "bootstrap3/errors.html" %} 5 | {% endif %} 6 | {{ form.form_html }} 7 | {% else %} 8 | {% include "bootstrap3/uni_form.html" %} 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/errors.html: -------------------------------------------------------------------------------- 1 | {% if form.errors and form.non_field_errors %} 2 |
    3 | {% if form_error_title %}

    {{ form_error_title }}

    {% endif %} 4 |
      5 | {{ form.non_field_errors|unordered_list }} 6 |
    7 |
    8 | {% endif %} 9 | -------------------------------------------------------------------------------- /crispy_forms/tests/urls.py: -------------------------------------------------------------------------------- 1 | import django 2 | 3 | if django.get_version() >= '1.5': 4 | from django.conf.urls import patterns, url 5 | else: 6 | from django.conf.urls.defaults import patterns, url 7 | 8 | urlpatterns = patterns('', 9 | url(r'^simple/action/$', 'simpleAction', name = 'simpleAction'), 10 | ) 11 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/errors_formset.html: -------------------------------------------------------------------------------- 1 | {% if formset.non_form_errors %} 2 |
    3 | {% if formset_error_title %}

    {{ formset_error_title }}

    {% endif %} 4 |
      5 | {{ formset.non_form_errors|unordered_list }} 6 |
    7 |
    8 | {% endif %} 9 | 10 | -------------------------------------------------------------------------------- /crispy_forms/tests/runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | cmds = [ 7 | 'python runtests_bootstrap.py', 8 | 'python runtests_bootstrap3.py', 9 | 'python runtests_uniform.py', 10 | ] 11 | 12 | for cmd in cmds: 13 | retval = os.system(cmd) 14 | if retval: 15 | sys.exit(1) 16 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/errors.html: -------------------------------------------------------------------------------- 1 | {% if form.non_field_errors %} 2 |
    3 | {% if form_error_title %}

    {{ form_error_title }}

    {% endif %} 4 |
      5 | {{ form.non_field_errors|unordered_list }} 6 |
    7 |
    8 | {% endif %} 9 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/errors.html: -------------------------------------------------------------------------------- 1 | {% if form.non_field_errors %} 2 |
    3 | {% if form_error_title %}

    {{ form_error_title }}

    {% endif %} 4 |
      5 | {{ form.non_field_errors|unordered_list }} 6 |
    7 |
    8 | {% endif %} 9 | -------------------------------------------------------------------------------- /crispy_forms/compatibility.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | PY2 = sys.version_info[0] == 2 5 | if not PY2: 6 | text_type = str 7 | binary_type = bytes 8 | string_types = (str,) 9 | integer_types = (int,) 10 | else: 11 | text_type = unicode 12 | binary_type = str 13 | string_types = basestring 14 | integer_types = (int, long) 15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/help_text.html: -------------------------------------------------------------------------------- 1 | {% if field.help_text %} 2 | {% if help_text_inline %} 3 | {{ field.help_text|safe }} 4 | {% else %} 5 |

    {{ field.help_text|safe }}

    6 | {% endif %} 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/help_text.html: -------------------------------------------------------------------------------- 1 | {% if field.help_text %} 2 | {% if help_text_inline %} 3 | {{ field.help_text|safe }} 4 | {% else %} 5 |

    {{ field.help_text|safe }}

    6 | {% endif %} 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/uni_form.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_utils %} 2 | 3 | {% specialspaceless %} 4 | {{ form.media }} 5 | {% if form_show_errors %} 6 | {% include "bootstrap/errors.html" %} 7 | {% endif %} 8 | {% for field in form %} 9 | {% include "bootstrap/field.html" %} 10 | {% endfor %} 11 | {% endspecialspaceless %} 12 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/formactions.html: -------------------------------------------------------------------------------- 1 | 2 | {% if label_class %} 3 |
    4 | {% endif %} 5 | 6 |
    7 | {{ fields_output|safe }} 8 |
    9 | 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/uni_form.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_utils %} 2 | 3 | {% specialspaceless %} 4 | {{ form.media }} 5 | {% if form_show_errors %} 6 | {% include "bootstrap3/errors.html" %} 7 | {% endif %} 8 | {% for field in form %} 9 | {% include "bootstrap3/field.html" %} 10 | {% endfor %} 11 | {% endspecialspaceless %} 12 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/errors_formset.html: -------------------------------------------------------------------------------- 1 | {% if formset.non_form_errors %} 2 |
    3 | {% if formset_error_title %}

    {{ formset_error_title }}

    {% endif %} 4 |
      5 | {{ formset.non_form_errors|unordered_list }} 6 |
    7 |
    8 | {% endif %} 9 | 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/fieldset.html: -------------------------------------------------------------------------------- 1 |
    4 | {% if legend %}{{ legend|safe }}{% endif %} 5 | {{ fields|safe }} 6 |
    7 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/errors_formset.html: -------------------------------------------------------------------------------- 1 | {% if formset.non_form_errors %} 2 |
    3 | {% if formset_error_title %}

    {{ formset_error_title }}

    {% endif %} 4 |
      5 | {{ formset.non_form_errors|unordered_list }} 6 |
    7 |
    8 | {% endif %} 9 | 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/fieldset.html: -------------------------------------------------------------------------------- 1 |
    4 | {% if legend %}{{ legend|safe }}{% endif %} 5 | {{ fields|safe }} 6 |
    7 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/layout/fieldset.html: -------------------------------------------------------------------------------- 1 |
    4 | {% if legend %}{{ legend|safe }}{% endif %} 5 | {{ fields|safe }} 6 |
    7 | -------------------------------------------------------------------------------- /docs/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | SITE_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__))) 4 | 5 | TEMPLATE_LOADERS = ( 6 | 'django.template.loaders.filesystem.Loader', 7 | 'django.template.loaders.app_directories.Loader', 8 | ) 9 | 10 | TEMPLATE_DIRS = (os.path.join(SITE_ROOT, 'templates')) 11 | 12 | INSTALLED_APPS = ( 13 | 'crispy_forms' 14 | ) 15 | SECRET_KEY = "secretkey" 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | setuptools_git-0.3.4-py2.6.egg 2 | #buildout's specific 3 | .installed.cfg 4 | eggs/* 5 | bin/* 6 | build/* 7 | dist/* 8 | develop-eggs/* 9 | parts/* 10 | downloads/* 11 | 12 | #egg's specific 13 | *.egg-info 14 | 15 | #defaults 16 | *.pyc 17 | *.pyo 18 | __pycache__ 19 | 20 | #temp file 21 | tempfile 22 | *.swp 23 | 24 | # coverage 25 | .coverage 26 | _build 27 | 28 | # OSX 29 | *.DS_Store 30 | -------------------------------------------------------------------------------- /crispy_forms/exceptions.py: -------------------------------------------------------------------------------- 1 | class CrispyError(Exception): 2 | pass 3 | 4 | 5 | class FormHelpersException(CrispyError): 6 | """ 7 | This is raised when building a form via helpers throws an error. 8 | We want to catch form helper errors as soon as possible because 9 | debugging templatetags is never fun. 10 | """ 11 | pass 12 | 13 | 14 | class DynamicError(CrispyError): 15 | pass 16 | -------------------------------------------------------------------------------- /docs/api_templatetags.rst: -------------------------------------------------------------------------------- 1 | ========================================== 2 | API templatetags 3 | ========================================== 4 | 5 | .. automodule:: templatetags.crispy_forms_tags 6 | :members: 7 | :undoc-members: 8 | 9 | .. automodule:: templatetags.crispy_forms_filters 10 | :members: 11 | :undoc-members: 12 | 13 | .. automodule:: templatetags.crispy_forms_field 14 | :members: 15 | :undoc-members: 16 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/inputs.html: -------------------------------------------------------------------------------- 1 | {% if inputs %} 2 |
    3 | {% if label_class %} 4 |
    5 | {% endif %} 6 | 7 |
    8 | {% for input in inputs %} 9 | {% include "bootstrap3/layout/baseinput.html" %} 10 | {% endfor %} 11 |
    12 |
    13 | {% endif %} 14 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/uni_form.html: -------------------------------------------------------------------------------- 1 | {{ form.media }} 2 | {% if form_show_errors %} 3 | {% include "uni_form/errors.html" %} 4 | {% endif %} 5 | 6 | {% if form_style == "" or form_style %} 7 |
    8 | 9 | {% endif %} 10 | 11 | {% for field in form %} 12 | {% include "uni_form/field.html" %} 13 | {% endfor %} 14 | 15 | {% if form_style == "" or form_style %} 16 |
    17 | {% endif %} 18 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/help_text_and_errors.html: -------------------------------------------------------------------------------- 1 | {% if help_text_inline and not error_text_inline %} 2 | {% include 'bootstrap/layout/help_text.html' %} 3 | {% endif %} 4 | 5 | {% if error_text_inline %} 6 | {% include 'bootstrap/layout/field_errors.html' %} 7 | {% else %} 8 | {% include 'bootstrap/layout/field_errors_block.html' %} 9 | {% endif %} 10 | 11 | {% if not help_text_inline %} 12 | {% include 'bootstrap/layout/help_text.html' %} 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/help_text_and_errors.html: -------------------------------------------------------------------------------- 1 | {% if help_text_inline and not error_text_inline %} 2 | {% include 'bootstrap3/layout/help_text.html' %} 3 | {% endif %} 4 | 5 | {% if error_text_inline %} 6 | {% include 'bootstrap3/layout/field_errors.html' %} 7 | {% else %} 8 | {% include 'bootstrap3/layout/field_errors_block.html' %} 9 | {% endif %} 10 | 11 | {% if not help_text_inline %} 12 | {% include 'bootstrap3/layout/help_text.html' %} 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/accordion-group.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | {{ div.name }} 4 |
    5 |
    6 |
    7 | {{ fields|safe }} 8 |
    9 |
    10 |
    11 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/baseinput.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/baseinput.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.6" 4 | - "2.7" 5 | - "3.3" 6 | env: 7 | - DJANGO=1.3.7 8 | - DJANGO=1.4.5 9 | - DJANGO=1.5.1 10 | install: 11 | - pip install Django==$DJANGO 12 | - pip install "file://`pwd`#egg=django-crispy-forms[tests]" 13 | - pip install -e . --use-mirrors 14 | script: 15 | - make test 16 | notifications: 17 | email: false 18 | matrix: 19 | exclude: 20 | - python: "3.3" 21 | env: DJANGO=1.4.5 22 | - python: "3.3" 23 | env: DJANGO=1.3.7 24 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/layout/baseinput.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/accordion-group.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    4 | {{ div.name }} 5 |

    6 |
    7 |
    8 |
    9 | {{ fields|safe }} 10 |
    11 |
    12 |
    13 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/whole_uni_form.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_utils %} 2 | 3 | {% specialspaceless %} 4 | {% if form_tag %}
    {% endif %} 5 | {% if form_method|lower == 'post' and not disable_csrf %} 6 | {% csrf_token %} 7 | {% endif %} 8 | 9 | {% include "bootstrap3/display_form.html" %} 10 | 11 | {% include "bootstrap3/inputs.html" %} 12 | 13 | {% if form_tag %}
    {% endif %} 14 | {% endspecialspaceless %} 15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/uneditable_input.html: -------------------------------------------------------------------------------- 1 |
    2 |
    {{ field.label|safe }}{% if field.field.required %}*{% endif %}
    3 |
    4 | {% if field.value %}{{ field.value }}{% endif %} 5 | {% include 'bootstrap/layout/help_text.html' %} 6 |
    7 |
    8 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/whole_uni_form.html: -------------------------------------------------------------------------------- 1 | {% if form_tag %}
    {% endif %} 2 | {% if form_method|lower == 'post' and not disable_csrf %} 3 | {% csrf_token %} 4 | {% endif %} 5 | 6 | {% include "uni_form/display_form.html" %} 7 | 8 | {% if inputs %} 9 |
    10 | {% for input in inputs %} 11 | {% include "uni_form/layout/baseinput.html" %} 12 | {% endfor %} 13 |
    14 | {% endif %} 15 | {% if form_tag %}
    {% endif %} 16 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/uneditable_input.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | 4 |
    5 | 6 |
    7 | {% crispy_field field 'disabled' 'disabled' %} 8 | {% include 'bootstrap3/layout/help_text.html' %} 9 |
    10 |
    11 | -------------------------------------------------------------------------------- /docs/_themes/kr/relations.html: -------------------------------------------------------------------------------- 1 |

    Related Topics

    2 | 20 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/multifield.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 | 7 | {% if field.label %} 8 | 25 | {% endif %} 26 | 27 | {% endif %} 28 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/multifield.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 | 7 | {% if field.label %} 8 | 25 | {% endif %} 26 | 27 | {% endif %} 28 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/radioselect_inline.html: -------------------------------------------------------------------------------- 1 | {% if field.is_hidden %} 2 | {{ field }} 3 | {% else %} 4 |
    5 | 6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | {% include 'bootstrap/layout/radioselect.html' %} 13 |
    14 | {% endif %} 15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/checkboxselectmultiple_inline.html: -------------------------------------------------------------------------------- 1 | {% if field.is_hidden %} 2 | {{ field }} 3 | {% else %} 4 |
    5 | 6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | {% include 'bootstrap/layout/checkboxselectmultiple.html' %} 13 |
    14 | {% endif %} 15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/whole_uni_form.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_utils %} 2 | 3 | {% specialspaceless %} 4 | {% if form_tag %}
    {% endif %} 5 | {% if form_method|lower == 'post' and not disable_csrf %} 6 | {% csrf_token %} 7 | {% endif %} 8 | 9 | {% include "bootstrap/display_form.html" %} 10 | 11 | {% if inputs %} 12 |
    13 | {% for input in inputs %} 14 | {% include "bootstrap/layout/baseinput.html" %} 15 | {% endfor %} 16 |
    17 | {% endif %} 18 | {% if form_tag %}
    {% endif %} 19 | {% endspecialspaceless %} 20 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple_inline.html: -------------------------------------------------------------------------------- 1 | {% if field.is_hidden %} 2 | {{ field }} 3 | {% else %} 4 |
    5 | 6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | {% include 'bootstrap3/layout/checkboxselectmultiple.html' %} 13 |
    14 | {% endif %} 15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/betterform.html: -------------------------------------------------------------------------------- 1 | {% for fieldset in form.fieldsets %} 2 |
    3 | {% if fieldset.legend %} 4 | {{ fieldset.legend }} 5 | {% endif %} 6 | 7 | {% if fieldset.description %} 8 |

    {{ fieldset.description }}

    9 | {% endif %} 10 | 11 | {% for field in fieldset %} 12 | {% if field.is_hidden %} 13 | {{ field }} 14 | {% else %} 15 | {% include "bootstrap/field.html" %} 16 | {% endif %} 17 | {% endfor %} 18 | {% if not forloop.last or not fieldset_open %} 19 |
    20 | {% endif %} 21 | {% endfor %} 22 | 23 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/betterform.html: -------------------------------------------------------------------------------- 1 | {% for fieldset in form.fieldsets %} 2 |
    3 | {% if fieldset.legend %} 4 | {{ fieldset.legend }} 5 | {% endif %} 6 | 7 | {% if fieldset.description %} 8 |

    {{ fieldset.description }}

    9 | {% endif %} 10 | 11 | {% for field in fieldset %} 12 | {% if field.is_hidden %} 13 | {{ field }} 14 | {% else %} 15 | {% include "bootstrap3/field.html" %} 16 | {% endif %} 17 | {% endfor %} 18 | {% if not forloop.last or not fieldset_open %} 19 |
    20 | {% endif %} 21 | {% endfor %} 22 | 23 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/betterform.html: -------------------------------------------------------------------------------- 1 | {% for fieldset in form.fieldsets %} 2 |
    3 | {% if fieldset.legend %} 4 | {{ fieldset.legend }} 5 | {% endif %} 6 | 7 | {% if fieldset.description %} 8 |

    {{ fieldset.description }}

    9 | {% endif %} 10 | 11 | {% for field in fieldset %} 12 | {% if field.is_hidden %} 13 | {{ field }} 14 | {% else %} 15 | {% include "uni_form/field.html" %} 16 | {% endif %} 17 | {% endfor %} 18 | {% if not forloop.last or not fieldset_open %} 19 |
    20 | {% endif %} 21 | {% endfor %} 22 | 23 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% block header %} 3 | {{ super() }} 4 | {% if pagename == 'index' %} 5 |
    6 | {% endif %} 7 | {% endblock %} 8 | {% block footer %} 9 | {% if pagename == 'index' %} 10 |
    11 | {% endif %} 12 | {% endblock %} 13 | {# do not display relbars #} 14 | {% block relbar1 %}{% endblock %} 15 | {% block relbar2 %} 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {% endblock %} 21 | {% block sidebar1 %}{% endblock %} 22 | {% block sidebar2 %}{% endblock %} 23 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/radioselect_inline.html: -------------------------------------------------------------------------------- 1 | {% if field.is_hidden %} 2 | {{ field }} 3 | {% else %} 4 |
    5 | 6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | {% include 'bootstrap3/layout/radioselect.html' %} 13 |
    14 | {% endif %} 15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/radioselect.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_filters %} 2 | {% load l10n %} 3 | 4 |
    5 | {% include 'bootstrap/layout/field_errors_block.html' %} 6 | 7 | {% for choice in field.field.choices %} 8 | 11 | {% endfor %} 12 | 13 | {% include 'bootstrap/layout/help_text.html' %} 14 |
    15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/radioselect.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_filters %} 2 | {% load l10n %} 3 | 4 |
    5 | {% include 'bootstrap3/layout/field_errors_block.html' %} 6 | 7 | {% for choice in field.field.choices %} 8 | 11 | {% endfor %} 12 | 13 | {% include 'bootstrap3/layout/help_text.html' %} 14 |
    15 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/multifield.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 |
    7 | {% if form_show_labels and field.label %} 8 | 11 | {% endif %} 12 | 13 | {% crispy_field field %} 14 | 15 | {% if field.help_text %} 16 |
    {{ field.help_text|safe }}
    17 | {% endif %} 18 |
    19 | {% endif %} 20 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/checkboxselectmultiple.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_filters %} 2 | {% load l10n %} 3 | 4 |
    5 | {% include 'bootstrap/layout/field_errors_block.html' %} 6 | 7 | {% for choice in field.field.choices %} 8 | 11 | {% endfor %} 12 | 13 | {% include 'bootstrap/layout/help_text.html' %} 14 |
    15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_filters %} 2 | {% load l10n %} 3 | 4 |
    5 | {% include 'bootstrap3/layout/field_errors_block.html' %} 6 | 7 | {% for choice in field.field.choices %} 8 | 11 | {% endfor %} 12 | 13 | {% include 'bootstrap3/layout/help_text.html' %} 14 |
    15 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/inline_field.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 | {% if field|is_checkbox %} 7 |
    8 | 12 |
    13 | {% else %} 14 |
    15 | 18 | {% crispy_field field 'placeholder' field.label %} 19 |
    20 | {% endif %} 21 | {% endif %} 22 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/whole_uni_formset.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | 3 | {% if formset_tag %} 4 |
    5 | {% endif %} 6 | {% if formset_method|lower == 'post' and not disable_csrf %} 7 | {% csrf_token %} 8 | {% endif %} 9 | 10 |
    11 | {{ formset.management_form|crispy }} 12 |
    13 | 14 | {% include "uni_form/errors_formset.html" %} 15 | 16 | {% for form in formset %} 17 | {% include "uni_form/display_form.html" %} 18 | {% endfor %} 19 | 20 | {% if inputs %} 21 |
    22 | {% for input in inputs %} 23 | {% include "uni_form/layout/baseinput.html" %} 24 | {% endfor %} 25 |
    26 | {% endif %} 27 | {% if formset_tag %}
    {% endif %} 28 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/field_with_buttons.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | 4 | {% if field.label and form_show_labels %} 5 | 8 | {% endif %} 9 | 10 |
    11 |
    12 | {% crispy_field field %} 13 | {{ buttons|safe }} 14 |
    15 | {% include 'bootstrap/layout/help_text_and_errors.html' %} 16 |
    17 | 18 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/whole_uni_formset.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | {% load crispy_forms_utils %} 3 | 4 | {% specialspaceless %} 5 | {% if formset_tag %} 6 |
    7 | {% endif %} 8 | {% if formset_method|lower == 'post' and not disable_csrf %} 9 | {% csrf_token %} 10 | {% endif %} 11 | 12 |
    13 | {{ formset.management_form|crispy }} 14 |
    15 | 16 | {% include "bootstrap/errors_formset.html" %} 17 | 18 | {% for form in formset %} 19 | {% include "bootstrap/display_form.html" %} 20 | {% endfor %} 21 | 22 | {% if inputs %} 23 |
    24 | {% for input in inputs %} 25 | {% include "bootstrap/layout/baseinput.html" %} 26 | {% endfor %} 27 |
    28 | {% endif %} 29 | {% if formset_tag %}
    {% endif %} 30 | {% endspecialspaceless %} 31 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/whole_uni_formset.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | {% load crispy_forms_utils %} 3 | 4 | {% specialspaceless %} 5 | {% if formset_tag %} 6 |
    7 | {% endif %} 8 | {% if formset_method|lower == 'post' and not disable_csrf %} 9 | {% csrf_token %} 10 | {% endif %} 11 | 12 |
    13 | {{ formset.management_form|crispy }} 14 |
    15 | 16 | {% include "bootstrap3/errors_formset.html" %} 17 | 18 | {% for form in formset %} 19 | {% include "bootstrap3/display_form.html" %} 20 | {% endfor %} 21 | 22 | {% if inputs %} 23 |
    24 | {% for input in inputs %} 25 | {% include "bootstrap3/layout/baseinput.html" %} 26 | {% endfor %} 27 |
    28 | {% endif %} 29 | {% if formset_tag %}
    {% endif %} 30 | {% endspecialspaceless %} 31 | -------------------------------------------------------------------------------- /crispy_forms/tests/runtests_uniform.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys 4 | 5 | os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings' 6 | parent = os.path.dirname(os.path.dirname(os.path.dirname( 7 | os.path.abspath(__file__)))) 8 | 9 | sys.path.insert(0, parent) 10 | 11 | from django.test.simple import DjangoTestSuiteRunner 12 | from django.conf import settings 13 | 14 | settings.CRISPY_TEMPLATE_PACK = 'uni_form' 15 | 16 | 17 | def runtests(): 18 | return DjangoTestSuiteRunner(failfast=False).run_tests([ 19 | 'crispy_forms.TestBasicFunctionalityTags', 20 | 'crispy_forms.TestFormHelper', 21 | 'crispy_forms.TestUniformFormHelper', 22 | 'crispy_forms.TestFormLayout', 23 | 'crispy_forms.TestUniformFormLayout', 24 | 'crispy_forms.TestLayoutObjects', 25 | 'crispy_forms.TestDynamicLayouts', 26 | 'crispy_forms.TestUniformDynamicLayouts', 27 | ], verbosity=1, interactive=True) 28 | 29 | 30 | if __name__ == '__main__': 31 | if runtests(): 32 | sys.exit(1) 33 | -------------------------------------------------------------------------------- /crispy_forms/tests/runtests_bootstrap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys 4 | 5 | os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings' 6 | parent = os.path.dirname(os.path.dirname(os.path.dirname( 7 | os.path.abspath(__file__)))) 8 | 9 | sys.path.insert(0, parent) 10 | 11 | from django.test.simple import DjangoTestSuiteRunner 12 | from django.conf import settings 13 | 14 | settings.CRISPY_TEMPLATE_PACK = 'bootstrap' 15 | 16 | 17 | def runtests(): 18 | return DjangoTestSuiteRunner(failfast=False).run_tests([ 19 | 'crispy_forms.TestBasicFunctionalityTags', 20 | 'crispy_forms.TestFormHelper', 21 | 'crispy_forms.TestBootstrapFormHelper', 22 | 'crispy_forms.TestFormLayout', 23 | 'crispy_forms.TestBootstrapFormLayout', 24 | 'crispy_forms.TestLayoutObjects', 25 | 'crispy_forms.TestBootstrapLayoutObjects', 26 | 'crispy_forms.TestDynamicLayouts', 27 | ], verbosity=1, interactive=True) 28 | 29 | 30 | if __name__ == '__main__': 31 | if runtests(): 32 | sys.exit(1) 33 | -------------------------------------------------------------------------------- /docs/_themes/kr/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {% if theme_touch_icon %} 5 | 6 | {% endif %} 7 | 9 | {% endblock %} 10 | {%- block relbar2 %}{% endblock %} 11 | {%- block footer %} 12 | 22 | 23 | 27 | {%- endblock %} 28 | -------------------------------------------------------------------------------- /docs/filters.rst: -------------------------------------------------------------------------------- 1 | crispy filter 2 | ============= 3 | 4 | Crispy filter lets you render a form or formset using django-crispy-forms elegantly div based fields. Let's see a usage example:: 5 | 6 | {% load crispy_forms_tags %} 7 | 8 |
    9 | {{ my_formset|crispy }} 10 |
    11 | 12 | 1. Add ``{% load crispy_forms_tags %}`` to the template. 13 | 2. Append the ``|crispy`` filter to your form or formset context variable. 14 | 3. If you are using ``uni_form`` template pack, don't forget to add the class 'uniForm' to your form. 15 | 4. Refresh and enjoy! 16 | 17 | Using {% crispy %} tag because it rocks 18 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | 20 | As handy as the `|crispy` filter is, think of it as the built-in methods: ``as_table``, ``as_ul`` and ``as_p``. You cannot tune up the output. The best way to make your forms crisp is using the :ref:`crispy tag forms`. It will change how you do forms in Django. 21 | 22 | .. _`original implementation`: http://code.google.com/p/django-uni-form/source/browse/trunk/uni_form/templatetags/uni_form.py?spec=svn2&r=2 23 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/field_with_buttons.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | 4 | {% if field.label and form_show_labels %} 5 | 8 | {% endif %} 9 | 10 |
    11 |
    12 | {% crispy_field field %} 13 | {{ buttons|safe }} 14 |
    15 | {% include 'bootstrap3/layout/help_text_and_errors.html' %} 16 |
    17 | 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011 Miguel Araujo and contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /crispy_forms/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Daniel Greenfeld and contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /crispy_forms/tests/runtests_bootstrap3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys 4 | 5 | os.environ['DJANGO_SETTINGS_MODULE'] = 'test_settings' 6 | parent = os.path.dirname(os.path.dirname(os.path.dirname( 7 | os.path.abspath(__file__)))) 8 | 9 | sys.path.insert(0, parent) 10 | 11 | from django.test.simple import DjangoTestSuiteRunner 12 | from django.conf import settings 13 | 14 | settings.CRISPY_TEMPLATE_PACK = 'bootstrap3' 15 | 16 | 17 | def runtests(): 18 | return DjangoTestSuiteRunner(failfast=False).run_tests([ 19 | 'crispy_forms.TestBasicFunctionalityTags', 20 | 'crispy_forms.TestFormHelper', 21 | 'crispy_forms.TestBootstrapFormHelper', 22 | 'crispy_forms.TestBootstrap3FormHelper', 23 | 'crispy_forms.TestFormLayout', 24 | 'crispy_forms.TestBootstrapFormLayout', 25 | 'crispy_forms.TestBootstrap3FormLayout', 26 | 'crispy_forms.TestLayoutObjects', 27 | 'crispy_forms.TestBootstrapLayoutObjects', 28 | 'crispy_forms.TestDynamicLayouts', 29 | ], verbosity=1, interactive=True) 30 | 31 | 32 | if __name__ == '__main__': 33 | if runtests(): 34 | sys.exit(1) 35 | -------------------------------------------------------------------------------- /crispy_forms/base.py: -------------------------------------------------------------------------------- 1 | def from_iterable(iterables): 2 | """ 3 | Backport of `itertools.chain.from_iterable` compatible with Python 2.5 4 | """ 5 | for it in iterables: 6 | for element in it: 7 | if isinstance(element, dict): 8 | for key in element: 9 | yield key 10 | else: 11 | yield element 12 | 13 | 14 | class KeepContext(object): 15 | """ 16 | Context manager that receives a `django.template.Context` instance and a list of keys 17 | 18 | Once the context manager is exited, it removes `keys` from the context, to avoid 19 | side effects in later layout objects that may use the same context variables. 20 | 21 | Layout objects should use `extra_context` to introduce context variables, never 22 | touch context object themselves, that could introduce side effects. 23 | """ 24 | def __init__(self, context, keys): 25 | self.context = context 26 | self.keys = keys 27 | 28 | def __enter__(self): 29 | pass 30 | 31 | def __exit__(self, type, value, traceback): 32 | for key in list(self.keys): 33 | del self.context[key] 34 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/layout/multifield.html: -------------------------------------------------------------------------------- 1 |
    4 | 5 | {% if form_show_errors %} 6 | {% for field in multifield.bound_fields %} 7 | {% if field.errors %} 8 | {% for error in field.errors %} 9 |

    {{ error }}

    10 | {% endfor %} 11 | {% endif %} 12 | {% endfor %} 13 | {% endif %} 14 | 15 | {% if multifield.label_html %} 16 |

    {{ multifield.label_html|safe }}

    17 | {% endif %} 18 | 19 |
    20 | {{ fields_output|safe }} 21 |
    22 | 23 | {% for field in multifield.bound_fields %} 24 | {% if field.help_text %} 25 |

    {{ field.help_text|safe }}

    26 | {% endif %} 27 | {% endfor %} 28 |
    29 | -------------------------------------------------------------------------------- /docs/_templates/sidebarintro.html: -------------------------------------------------------------------------------- 1 |

    crispy-forms

    2 |

    3 | 5 |

    6 | 7 |

    8 | django-crispy-forms is a Django application that lets you easily build, customize and reuse forms using your favorite CSS framework, without writing template code and without having to take care of annoying details. You are currently looking at the documentation of the development release. 9 |

    10 | 11 | 12 |

    Support

    13 |

    14 | If you love django-crispy-forms, consider making a small donation on Flattr: 15 |

    16 |

    17 | 18 | 20 |

    21 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/field.strict.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 |
    7 | {% for error in field.errors %} 8 |

    9 | {{ error }} 10 |

    11 | {% endfor %} 12 | 13 | {% if field|is_checkbox %} 14 | {% crispy_field field %} 15 | {% endif %} 16 | 17 | {% if field.label %} 18 | 21 | {% endif %} 22 | 23 | {% if not field|is_checkbox %} 24 | {% crispy_field field %} 25 | {% endif %} 26 | 27 | {% if field.help_text %} 28 |

    {{ field.help_text|safe }}

    29 | {% endif %} 30 |
    31 | {% endif %} 32 | -------------------------------------------------------------------------------- /docs/_themes/kr/static/small_flask.css: -------------------------------------------------------------------------------- 1 | /* 2 | * small_flask.css_t 3 | * ~~~~~~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | body { 10 | margin: 0; 11 | padding: 20px 30px; 12 | } 13 | 14 | div.documentwrapper { 15 | float: none; 16 | background: white; 17 | } 18 | 19 | div.sphinxsidebar { 20 | display: block; 21 | float: none; 22 | width: 102.5%; 23 | margin: 50px -30px -20px -30px; 24 | padding: 10px 20px; 25 | background: #333; 26 | color: white; 27 | } 28 | 29 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 30 | div.sphinxsidebar h3 a { 31 | color: white; 32 | } 33 | 34 | div.sphinxsidebar a { 35 | color: #aaa; 36 | } 37 | 38 | div.sphinxsidebar p.logo { 39 | display: none; 40 | } 41 | 42 | div.document { 43 | width: 100%; 44 | margin: 0; 45 | } 46 | 47 | div.related { 48 | display: block; 49 | margin: 0; 50 | padding: 10px 0 20px 0; 51 | } 52 | 53 | div.related ul, 54 | div.related ul li { 55 | margin: 0; 56 | padding: 0; 57 | } 58 | 59 | div.footer { 60 | display: none; 61 | } 62 | 63 | div.bodywrapper { 64 | margin: 0; 65 | } 66 | 67 | div.body { 68 | min-height: 0; 69 | padding: 0; 70 | } 71 | -------------------------------------------------------------------------------- /crispy_forms/tests/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | from django.conf import settings 5 | from django.template import loader 6 | from django.test import TestCase 7 | 8 | from crispy_forms.tests.utils import override_settings 9 | 10 | 11 | class CrispyTestCase(TestCase): 12 | def setUp(self): 13 | template_dirs = [os.path.join(os.path.dirname(__file__), 'templates')] 14 | template_dirs = template_dirs + list(settings.TEMPLATE_DIRS) 15 | template_loaders = ['django.template.loaders.filesystem.Loader'] 16 | template_loaders = template_loaders + list(settings.TEMPLATE_LOADERS) 17 | 18 | # ensuring test templates directory is loaded first 19 | self.__overriden_settings = override_settings(**{ 20 | 'TEMPLATE_LOADERS': template_loaders, 21 | 'TEMPLATE_DIRS': template_dirs, 22 | }) 23 | self.__overriden_settings.enable() 24 | 25 | # resetting template loaders cache 26 | self.__template_source_loaders = loader.template_source_loaders 27 | loader.template_source_loaders = None 28 | 29 | def tearDown(self): 30 | loader.template_source_loaders = self.__template_source_loaders 31 | self.__overriden_settings.disable() 32 | 33 | @property 34 | def current_template_pack(self): 35 | return getattr(settings, 'CRISPY_TEMPLATE_PACK', 'bootstrap') 36 | -------------------------------------------------------------------------------- /crispy_forms/tests/test_settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from crispy_forms.compatibility import text_type 4 | 5 | 6 | BASE_DIR = os.path.dirname(__file__) 7 | 8 | INSTALLED_APPS = ( 9 | 'django.contrib.auth', 10 | 'django.contrib.sessions', 11 | 'django.contrib.contenttypes', 12 | 'django.contrib.admin', 13 | 'crispy_forms', 14 | ) 15 | 16 | DATABASES = { 17 | 'default': { 18 | 'ENGINE': 'django.db.backends.sqlite3', 19 | } 20 | } 21 | 22 | MIDDLEWARE_CLASSES = ( 23 | 'django.middleware.common.CommonMiddleware', 24 | 'django.middleware.csrf.CsrfViewMiddleware', 25 | ) 26 | 27 | ROOT_URLCONF = 'urls' 28 | CRISPY_CLASS_CONVERTERS = {"textinput": "textinput textInput inputtext"} 29 | SECRET_KEY = 'secretkey' 30 | SITE_ROOT = os.path.dirname(os.path.abspath(__file__)) 31 | 32 | 33 | # http://djangosnippets.org/snippets/646/ 34 | class InvalidVarException(object): 35 | def __mod__(self, missing): 36 | try: 37 | missing_str = text_type(missing) 38 | except: 39 | missing_str = 'Failed to create string representation' 40 | raise Exception('Unknown template variable %r %s' % (missing, missing_str)) 41 | 42 | def __contains__(self, search): 43 | if search == '%s': 44 | return True 45 | return False 46 | 47 | 48 | TEMPLATE_DEBUG = True 49 | TEMPLATE_STRING_IF_INVALID = InvalidVarException() 50 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import crispy_forms 2 | 3 | from setuptools import setup, find_packages 4 | 5 | 6 | tests_require = [ 7 | 'Django>=1.3,<1.7', 8 | ] 9 | 10 | setup( 11 | name='django-crispy-forms', 12 | version=crispy_forms.__version__, 13 | description="Best way to have Django DRY forms", 14 | long_description=open('README.rst').read(), 15 | classifiers=[ 16 | "Development Status :: 5 - Production/Stable", 17 | "Environment :: Web Environment", 18 | "Framework :: Django", 19 | "License :: OSI Approved :: BSD License", 20 | "Operating System :: OS Independent", 21 | "Programming Language :: JavaScript", 22 | "Programming Language :: Python :: 2.6", 23 | "Programming Language :: Python :: 2.7", 24 | "Programming Language :: Python :: 3.3", 25 | "Topic :: Internet :: WWW/HTTP", 26 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 27 | "Topic :: Software Development :: Libraries :: Python Modules", 28 | ], 29 | extras_require={ 30 | 'tests': tests_require, 31 | }, 32 | keywords=['forms', 'django', 'crispy', 'DRY'], 33 | author='Miguel Araujo', 34 | author_email='miguel.araujo.perez@gmail.com', 35 | url='http://github.com/maraujop/django-crispy-forms', 36 | license='MIT', 37 | packages=find_packages(exclude=['docs']), 38 | include_package_data=True, 39 | zip_safe=False, 40 | ) 41 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/layout/prepended_appended_text.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 |
    7 | 8 | {% if field.label and form_show_labels %} 9 | 12 | {% endif %} 13 | 14 |
    15 |
    16 | {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} 17 | {% crispy_field field %} 18 | {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %} 19 |
    20 | 21 | {% include 'bootstrap/layout/help_text_and_errors.html' %} 22 |
    23 |
    24 | {% endif %} 25 | -------------------------------------------------------------------------------- /crispy_forms/templates/uni_form/field.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 |
    7 | {% if form_show_errors %} 8 | {% for error in field.errors %} 9 |

    10 | {{ error }} 11 |

    12 | {% endfor %} 13 | {% endif %} 14 | 15 | {% if field.label %} 16 | {% if field|is_checkbox %} 17 | {% crispy_field field %} 18 | {% endif %} 19 | 20 | 23 | {% endif %} 24 | 25 | {% if not field|is_checkbox %} 26 | {% crispy_field field %} 27 | {% endif %} 28 | 29 | {% if field.help_text %} 30 |
    {{ field.help_text|safe }}
    31 | {% endif %} 32 |
    33 | {% endif %} 34 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/field.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 | <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="control-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} error{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> 7 | {% if field.label and not field|is_checkbox and form_show_labels %} 8 | 11 | {% endif %} 12 | 13 | {% if field|is_checkboxselectmultiple %} 14 | {% include 'bootstrap/layout/checkboxselectmultiple.html' %} 15 | {% endif %} 16 | 17 | {% if field|is_radioselect %} 18 | {% include 'bootstrap/layout/radioselect.html' %} 19 | {% endif %} 20 | 21 | {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} 22 |
    23 | {% if field|is_checkbox and form_show_labels %} 24 | 29 | {% else %} 30 | {% crispy_field field %} 31 | {% include 'bootstrap/layout/help_text_and_errors.html' %} 32 | {% endif %} 33 |
    34 | {% endif %} 35 | 36 | {% endif %} 37 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/layout/prepended_appended_text.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 |
    7 | 8 | {% if field.label and form_show_labels %} 9 | 12 | {% endif %} 13 | 14 |
    15 | {% if field|is_select %} 16 | {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} 17 | {% crispy_field field %} 18 | {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %} 19 | {% else %} 20 |
    21 | {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} 22 | {% crispy_field field %} 23 | {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %} 24 |
    25 | {% endif %} 26 | 27 | {% include 'bootstrap3/layout/help_text_and_errors.html' %} 28 |
    29 |
    30 | {% endif %} 31 | -------------------------------------------------------------------------------- /docs/_themes/LICENSE: -------------------------------------------------------------------------------- 1 | Modifications: 2 | 3 | Copyright (c) 2010 Kenneth Reitz. 4 | 5 | 6 | Original Project: 7 | 8 | Copyright (c) 2010 by Armin Ronacher. 9 | 10 | 11 | Some rights reserved. 12 | 13 | Redistribution and use in source and binary forms of the theme, with or 14 | without modification, are permitted provided that the following conditions 15 | are met: 16 | 17 | * Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | * Redistributions in binary form must reproduce the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer in the documentation and/or other materials provided 23 | with the distribution. 24 | 25 | * The names of the contributors may not be used to endorse or 26 | promote products derived from this software without specific 27 | prior written permission. 28 | 29 | We kindly ask you to only use these themes in an unmodified manner just 30 | for Flask and Flask-related products, not for unrelated projects. If you 31 | like the visual style and want to use it for your own projects, please 32 | consider making some larger changes to the themes (such as changing 33 | font faces, sizes, colors or margins). 34 | 35 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 36 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 39 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/table_inline_formset.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | {% load crispy_forms_utils %} 3 | {% load crispy_forms_field %} 4 | 5 | {% specialspaceless %} 6 | {% if formset_tag %} 7 |
    8 | {% endif %} 9 | {% if formset_method|lower == 'post' and not disable_csrf %} 10 | {% csrf_token %} 11 | {% endif %} 12 | 13 |
    14 | {{ formset.management_form|crispy }} 15 |
    16 | 17 | 18 | 19 | {% if formset.readonly and not formset.queryset.exists %} 20 | {% else %} 21 | 22 | {% for field in formset.forms.0 %} 23 | {% if field.label and not field|is_checkbox and not field.is_hidden %} 24 | 25 | {{ field.label|safe }}{% if field.field.required %}*{% endif %} 26 | 27 | {% endif %} 28 | {% endfor %} 29 | 30 | {% endif %} 31 | 32 | 33 | 34 | {% for form in formset %} 35 | {% if form_show_errors and not form.is_extra %} 36 | {% include "bootstrap3/errors.html" %} 37 | {% endif %} 38 | 39 | 40 | {% for field in form %} 41 | {% include 'bootstrap3/field.html' with tag="th" form_show_labels=False %} 42 | {% endfor %} 43 | 44 | {% endfor %} 45 | 46 | 47 | 48 | {% include "bootstrap3/inputs.html" %} 49 | 50 | {% if formset_tag %}{% endif %} 51 | {% endspecialspaceless %} 52 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. django-crispy-forms documentation master file, created by 2 | sphinx-quickstart on Tue Nov 1 19:01:02 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Forms have never been this crispy 7 | ================================= 8 | 9 | django-crispy-forms provides you with a ``|crispy`` filter and ``{% crispy %}`` tag that will let you control the rendering behavior of your Django_ forms in a very elegant and DRY way. Have full control without writing custom form templates. All this without breaking the standard way of doing things in Django_, so it plays nice with any other form application. 10 | 11 | User Guide 12 | ~~~~~~~~~~ 13 | 14 | Get the most out of django-crispy-forms 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | 19 | install 20 | filters 21 | crispy_tag_forms 22 | form_helper 23 | layouts 24 | template_packs 25 | crispy_tag_formsets 26 | dynamic_layouts 27 | 28 | .. toctree:: 29 | :maxdepth: 1 30 | 31 | faq 32 | 33 | * See who's contributed to the project at `crispy-forms contributors`_ 34 | * You can find a detailed history of the project in `Github's CHANGELOG`_ 35 | 36 | .. _`crispy-forms contributors`: https://github.com/maraujop/django-crispy-forms/blob/dev/CONTRIBUTORS.txt 37 | .. _`Github's CHANGELOG`: https://github.com/maraujop/django-crispy-forms/blob/dev/CHANGELOG.md 38 | 39 | API documentation 40 | ~~~~~~~~~~~~~~~~~ 41 | 42 | If you are looking for information on a specific function, class or method, this part of the documentation is for you. 43 | 44 | .. toctree:: 45 | :maxdepth: 2 46 | 47 | api_helpers 48 | api_layout 49 | api_templatetags 50 | 51 | Developer Guide 52 | ~~~~~~~~~~~~~~~ 53 | 54 | Think this is awesome and want to make it better? Read our contribution page, make it better, and you will be added to the `contributors`_ list! 55 | 56 | .. toctree:: 57 | :maxdepth: 2 58 | 59 | contributing 60 | 61 | .. _contributors: https://github.com/maraujop/django-crispy-forms/blob/dev/CONTRIBUTORS.txt 62 | .. _Django: http://djangoproject.com 63 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap/table_inline_formset.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_tags %} 2 | {% load crispy_forms_utils %} 3 | {% load crispy_forms_field %} 4 | 5 | {% specialspaceless %} 6 | {% if formset_tag %} 7 |
    8 | {% endif %} 9 | {% if formset_method|lower == 'post' and not disable_csrf %} 10 | {% csrf_token %} 11 | {% endif %} 12 | 13 |
    14 | {{ formset.management_form|crispy }} 15 |
    16 | 17 | 18 | 19 | {% if formset.readonly and not formset.queryset.exists %} 20 | {% else %} 21 | 22 | {% for field in formset.forms.0 %} 23 | {% if field.label and not field.is_hidden %} 24 | 25 | {{ field.label|safe }}{% if field.field.required and not field|is_checkbox %}*{% endif %} 26 | 27 | {% endif %} 28 | {% endfor %} 29 | 30 | {% endif %} 31 | 32 | 33 | 34 | {% for form in formset %} 35 | {% if form_show_errors and not form.is_extra %} 36 | {% include "bootstrap/errors.html" %} 37 | {% endif %} 38 | 39 | 40 | {% for field in form %} 41 | {% include 'bootstrap/field.html' with tag="th" form_show_labels=False %} 42 | {% endfor %} 43 | 44 | {% endfor %} 45 | 46 | 47 | 48 | {% if inputs %} 49 |
    50 | {% for input in inputs %} 51 | {% include "bootstrap/layout/baseinput.html" %} 52 | {% endfor %} 53 |
    54 | {% endif %} 55 | {% if formset_tag %}{% endif %} 56 | {% endspecialspaceless %} 57 | -------------------------------------------------------------------------------- /docs/concepts.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Concepts 3 | ======== 4 | 5 | Form Helpers 6 | ------------- 7 | 8 | The biggest advantage of this library are :ref:`form helpers` and layouts. The advantage of these tools is that they let you build forms with most of the coding done in Python, rather than HTML. We **strongly** suggest you study and learn the examples in the :ref:`form helpers` documentation. 9 | 10 | Don't Repeat Yourself 11 | --------------------- 12 | 13 | It has been written that if you do anything twice in code, you should wrap it up 14 | in a function or method. This project was born out of the desire to not have to 15 | rewrite similar forms multiple times over the same project. Just like we use the 16 | Django ORM to avoid writing simple queries again and again, it is advantageous to 17 | our sanity and code quality to not have to write `
    ` 30 times across a project. 18 | 19 | The problem with building a form this way multiple times is that it is ripe for error. What about hidden fields? What if you forget the `{% csrf_token %}` token? 20 | What if you don't set the form method correctly? 21 | 22 | Think of django-uni-form like an ORM, it handles the small details so you can 23 | focus on the big picture of your project - the business logic that drives your 24 | site and is probably a lot more fun to deal with than the tiny particularities of 25 | forms. 26 | 27 | Section 508 28 | ----------- 29 | 30 | Some years ago the United States congress defined `Section 508`_ as a means to provide enforcement for technology provided or purchased for the government that met a set of specifications so that those with disabilities could use said technologies. Unfortunately, the specification does not normally apply to commercial products not used by the US Government and many US Government projects weasel out of the specification. 31 | 32 | However, following Section 508 (and the World Wide Web Consortium's (W3C) `Web Accessibility Initiative`_ (WAI) is the right thing to do. It doesn't hurt to familiarize yourself with these specifications. 33 | 34 | In the meantime, django-uni-form provides a means to easily render Section 508 compliant forms. How awesome is that? 35 | 36 | .. _`Section 508`: http://en.wikipedia.org/wiki/Section_508 37 | .. _`Web Accessibility Initiative`: http://en.wikipedia.org/wiki/Web_Accessibility_Initiative 38 | -------------------------------------------------------------------------------- /crispy_forms/templatetags/crispy_forms_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | 4 | from django import template 5 | from django.conf import settings 6 | try: # Django < 1.4 7 | from django.utils.encoding import force_unicode as force_text 8 | except ImportError: 9 | from django.utils.encoding import force_text 10 | from django.utils.functional import allow_lazy 11 | 12 | from crispy_forms.compatibility import text_type 13 | 14 | register = template.Library() 15 | TEMPLATE_PACK = getattr(settings, 'CRISPY_TEMPLATE_PACK', 'bootstrap') 16 | 17 | 18 | def selectively_remove_spaces_between_tags(value, template_pack, form_class): 19 | if ( 20 | 'bootstrap' in template_pack 21 | and 'form-inline' in form_class 22 | ): 23 | # More than 3 strict whitespaces, see issue #250 24 | html = re.sub(r'>\s{3,}<', '> <', force_text(value)) 25 | return re.sub(r'/><', r'/> <', force_text(html)) 26 | else: 27 | html = re.sub(r'>\s{3,}<', '> <', force_text(value)) 28 | return re.sub(r'/><', r'/> <', force_text(html)) 29 | return value 30 | selectively_remove_spaces_between_tags = allow_lazy( 31 | selectively_remove_spaces_between_tags, text_type 32 | ) 33 | 34 | 35 | class SpecialSpacelessNode(template.Node): 36 | def __init__(self, nodelist): 37 | self.nodelist = nodelist 38 | 39 | def render(self, context): 40 | try: 41 | template_pack = template.Variable('template_pack').resolve(context) 42 | except: 43 | template_pack = TEMPLATE_PACK 44 | 45 | try: 46 | form_attrs = template.Variable('form_attrs').resolve(context) 47 | except: 48 | form_attrs = {} 49 | 50 | return selectively_remove_spaces_between_tags( 51 | self.nodelist.render(context).strip(), 52 | template_pack, 53 | form_attrs.get('class', ''), 54 | ) 55 | 56 | 57 | @register.tag 58 | def specialspaceless(parser, token): 59 | """ 60 | Removes whitespace between HTML tags, and introduces a whitespace 61 | after buttons an inputs, necessary for Bootstrap to place them 62 | correctly in the layout. 63 | """ 64 | nodelist = parser.parse(('endspecialspaceless',)) 65 | parser.delete_first_token() 66 | 67 | return SpecialSpacelessNode(nodelist) 68 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/field.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 | {% if field|is_checkbox %} 7 |
    8 | {% if label_class %} 9 |
    10 | {% endif %} 11 | {% endif %} 12 | <{% 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 %}"> 13 | {% if field.label and not field|is_checkbox and form_show_labels %} 14 | 17 | {% endif %} 18 | 19 | {% if field|is_checkboxselectmultiple %} 20 | {% include 'bootstrap3/layout/checkboxselectmultiple.html' %} 21 | {% endif %} 22 | 23 | {% if field|is_radioselect %} 24 | {% include 'bootstrap3/layout/radioselect.html' %} 25 | {% endif %} 26 | 27 | {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} 28 | {% if field|is_checkbox and form_show_labels %} 29 | 34 | {% else %} 35 |
    36 | {% crispy_field field %} 37 | {% include 'bootstrap3/layout/help_text_and_errors.html' %} 38 |
    39 | {% endif %} 40 | {% endif %} 41 | 42 | {% if field|is_checkbox %} 43 | {% if label_class %} 44 |
    45 | {% endif %} 46 |
    47 | {% endif %} 48 | {% endif %} 49 | -------------------------------------------------------------------------------- /crispy_forms/templates/bootstrap3/field.html.bk: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 | {% if field.is_hidden %} 4 | {{ field }} 5 | {% else %} 6 | {% if field|is_checkbox %} 7 |
    8 | {% endif %} 9 | <{% 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 %}"> 10 | {% if field.label and not field|is_checkbox and form_show_labels %} 11 | 14 | {% endif %} 15 | 16 | {% if field|is_checkboxselectmultiple %} 17 | {% include 'bootstrap3/layout/checkboxselectmultiple.html' %} 18 | {% endif %} 19 | 20 | {% if field|is_radioselect %} 21 | {% include 'bootstrap3/layout/radioselect.html' %} 22 | {% endif %} 23 | 24 | {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} 25 | {% if field|is_checkbox and form_show_labels %} 26 | {% if label_class %} 27 | 28 |
    29 | {% endif %} 30 | 35 | {% if label_class %} 36 |
    37 | {% endif %} 38 | {% else %} 39 |
    40 | {% crispy_field field %} 41 | {% include 'bootstrap3/layout/help_text_and_errors.html' %} 42 |
    43 | {% endif %} 44 | {% endif %} 45 | 46 | {% if field|is_checkbox %} 47 |
    48 | {% endif %} 49 | {% endif %} 50 | -------------------------------------------------------------------------------- /crispy_forms/tests/utils.py: -------------------------------------------------------------------------------- 1 | __all__ = ('override_settings',) 2 | 3 | 4 | try: 5 | from django.test.utils import override_settings 6 | except ImportError: 7 | # we are in Django 1.3 8 | from django.conf import settings, UserSettingsHolder 9 | from django.utils.functional import wraps 10 | 11 | class override_settings(object): 12 | """ 13 | Acts as either a decorator, or a context manager. If it's a decorator 14 | it takes a function and returns a wrapped function. If it's a 15 | contextmanager it's used with the ``with`` statement. In either event 16 | entering/exiting are called before and after, respectively, 17 | the function/block is executed. 18 | 19 | This class was backported from Django 1.5 20 | 21 | As django.test.signals.setting_changed is not supported in 1.3, 22 | it's not sent on changing settings. 23 | """ 24 | def __init__(self, **kwargs): 25 | self.options = kwargs 26 | self.wrapped = settings._wrapped 27 | 28 | def __enter__(self): 29 | self.enable() 30 | 31 | def __exit__(self, exc_type, exc_value, traceback): 32 | self.disable() 33 | 34 | def __call__(self, test_func): 35 | from django.test import TransactionTestCase 36 | if isinstance(test_func, type): 37 | if not issubclass(test_func, TransactionTestCase): 38 | raise Exception( 39 | "Only subclasses of Django SimpleTestCase " 40 | "can be decorated with override_settings") 41 | original_pre_setup = test_func._pre_setup 42 | original_post_teardown = test_func._post_teardown 43 | 44 | def _pre_setup(innerself): 45 | self.enable() 46 | original_pre_setup(innerself) 47 | 48 | def _post_teardown(innerself): 49 | original_post_teardown(innerself) 50 | self.disable() 51 | test_func._pre_setup = _pre_setup 52 | test_func._post_teardown = _post_teardown 53 | return test_func 54 | else: 55 | @wraps(test_func) 56 | def inner(*args, **kwargs): 57 | with self: 58 | return test_func(*args, **kwargs) 59 | return inner 60 | 61 | def enable(self): 62 | override = UserSettingsHolder(settings._wrapped) 63 | for key, new_value in self.options.items(): 64 | setattr(override, key, new_value) 65 | settings._wrapped = override 66 | 67 | def disable(self): 68 | settings._wrapped = self.wrapped 69 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributors 3 | ============ 4 | 5 | django-crispy-forms Project Lead 6 | ================================ 7 | 8 | * Miguel Araujo 9 | 10 | django-uni-form Project Founder 11 | =============================== 12 | 13 | * Daniel Greenfeld 14 | 15 | Contributors 16 | ============ 17 | 18 | * Alison Rowland 19 | * Bojan Mihelac 20 | * bjunix 21 | * Casper S. Jensen 22 | * Chris Adams 23 | * Eddy Mulyono 24 | * j0hnsmith 25 | * James Pic 26 | * James Tauber 27 | * Karl Bowden 28 | * Marcin Grzybowski 29 | * Michael Lind Mortensen 30 | * mirumee 31 | * mvaerle 32 | * Nagy Viktor 33 | * Patrick Lauber 34 | * Patryk Zawadzki 35 | * Skylar Saveland 36 | * Stepan Rakhimov 37 | * John Maxwell 38 | * Richard Marko 39 | * Victor Nagy 40 | * Antti Kaihola 41 | * J. Javier Maestro 42 | * Issac Kelly 43 | * John Debs 44 | * Adam Cupiał 45 | * Nicolas Patry 46 | * Jonas Obrist 47 | * Charlie Denton 48 | * Jason Culverhouse 49 | * James Turnbull 50 | * Patrick Toal 51 | * David Bennett 52 | * bitrut 53 | * ximi 54 | * Christopher Petrilli 55 | * James Friedman 56 | * Jeroen Vloothuis 57 | * Daniel Izquierdo 58 | * gaftech 59 | * Michal Kuffa 60 | * Paul Oswald 61 | * Rudy Mutter 62 | * Samuel Goldszmidt 63 | * Andrei Antoukh 64 | * 65 | * Rivo Laks 66 | * Lloyd Philbrook 67 | * Piet Delport 68 | * 69 | * Markus Hametner 70 | * Thomas Grainger 71 | * Lee Semel 72 | * 73 | * Alex Yakovlev 74 | * Si Feng 75 | * Igor Katson 76 | * Ben Delevingne 77 | * 78 | * Evan Borgstrom 79 | * Daniel Shapiro 80 | * 81 | * Stefan "hr" Berder (白峰) 82 | * Suleyman Melikoglu 83 | * Vladislav Mitov 84 | * Nemesis Fixx 85 | * Chris Vigelius 86 | * David Cramer 87 | * Stas Rudakou 88 | * Tom Yam 89 | * Svyatoslav Bulbakha 90 | * Andres Vargas 91 | * Gabe Jackson 92 | * Camilo Nova 93 | * 94 | * Daniel Mascarenhas 95 | * Paras Kuhad 96 | * Kevin Trad 97 | * Steven Klass 98 | * David Fischer 99 | * Stefan Tjarks 100 | * Jan Dittberner 101 | * Michael Nielsen 102 | * Stephen Mitchell 103 | * 104 | * Christopher Adams 105 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | 9 | # Internal variables. 10 | PAPEROPT_a4 = -D latex_paper_size=a4 11 | PAPEROPT_letter = -D latex_paper_size=letter 12 | ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 13 | 14 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 15 | 16 | help: 17 | @echo "Please use \`make ' where is one of" 18 | @echo " html to make standalone HTML files" 19 | @echo " dirhtml to make HTML files named index.html in directories" 20 | @echo " pickle to make pickle files" 21 | @echo " json to make JSON files" 22 | @echo " htmlhelp to make HTML files and a HTML help project" 23 | @echo " qthelp to make HTML files and a qthelp project" 24 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 25 | @echo " changes to make an overview of all changed/added/deprecated items" 26 | @echo " linkcheck to check all external links for integrity" 27 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 28 | 29 | clean: 30 | -rm -rf _build/* 31 | 32 | html: 33 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html 34 | @echo 35 | @echo "Build finished. The HTML pages are in _build/html." 36 | 37 | dirhtml: 38 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml 39 | @echo 40 | @echo "Build finished. The HTML pages are in _build/dirhtml." 41 | 42 | pickle: 43 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle 44 | @echo 45 | @echo "Build finished; now you can process the pickle files." 46 | 47 | json: 48 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json 49 | @echo 50 | @echo "Build finished; now you can process the JSON files." 51 | 52 | htmlhelp: 53 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp 54 | @echo 55 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 56 | ".hhp project file in _build/htmlhelp." 57 | 58 | qthelp: 59 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp 60 | @echo 61 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 62 | ".qhcp project file in _build/qthelp, like this:" 63 | @echo "# qcollectiongenerator _build/qthelp/django-uni-form.qhcp" 64 | @echo "To view the help file:" 65 | @echo "# assistant -collectionFile _build/qthelp/django-uni-form.qhc" 66 | 67 | latex: 68 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex 69 | @echo 70 | @echo "Build finished; the LaTeX files are in _build/latex." 71 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 72 | "run these through (pdf)latex." 73 | 74 | changes: 75 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes 76 | @echo 77 | @echo "The overview file is in _build/changes." 78 | 79 | linkcheck: 80 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck 81 | @echo 82 | @echo "Link check complete; look for any errors in the above output " \ 83 | "or in _build/linkcheck/output.txt." 84 | 85 | doctest: 86 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest 87 | @echo "Testing of doctests in the sources finished, look at the " \ 88 | "results in _build/doctest/output.txt." 89 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | django-crispy-forms 3 | =================== 4 | 5 | .. image:: https://travis-ci.org/maraujop/django-crispy-forms.png?branch=master 6 | :alt: Build Status 7 | :target: https://travis-ci.org/maraujop/django-crispy-forms 8 | 9 | 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. 10 | 11 | The application mainly provides: 12 | 13 | * A filter named ``|crispy`` that will render elegant div based forms. Think of it as the built-in methods: ``as_table``, ``as_ul`` and ``as_p``. You cannot tune up the output, but it is easy to start using it. 14 | * A tag named ``{% crispy %}`` that will render a form based on your configuration and specific layout setup. This gives you amazing power without much hassle, helping you save tons of time. 15 | 16 | Django-crispy-forms supports several frontend frameworks, such as Twitter `Bootstrap`_ (versions 2 and 3), `Uni-form`_ and Foundation. You can also easily adapt your custom company's one, creating your own, `see the docs`_ for more information. You can easily switch among them using ``CRISPY_TEMPLATE_PACK`` setting variable. 17 | 18 | .. _`Uni-form`: http://sprawsm.com/uni-form 19 | .. _`Bootstrap`: http://twitter.github.com/bootstrap/index.html 20 | .. _`see the docs`: http://django-crispy-forms.rtfd.org 21 | 22 | Authors 23 | ======= 24 | 25 | 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. 26 | 27 | If you are upgrading from django-uni-form, we have `instructions`_ for helping you. 28 | 29 | * Lead developer: `Miguel Araujo`_ 30 | 31 | .. _`Daniel Greenfeld`: https://github.com/pydanny 32 | .. _`Miguel Araujo`: https://github.com/maraujop 33 | .. _`instructions`: http://django-crispy-forms.readthedocs.org/en/1.1.1/migration.html 34 | 35 | Example 36 | ======= 37 | 38 | This is a teaser of what you can do with latest django-crispy-forms. `Find here the gist`_ for generating this form: 39 | 40 | .. image:: http://i.imgur.com/LSREg.png 41 | 42 | .. _`Find here the gist`: https://gist.github.com/1838193 43 | 44 | Documentation 45 | ============= 46 | 47 | For extensive documentation see the ``docs`` folder or `read it on readthedocs`_ 48 | 49 | .. _`read it on readthedocs`: http://django-crispy-forms.readthedocs.org/en/latest/index.html 50 | 51 | Special thanks 52 | ============== 53 | 54 | * To Daniel Greenfeld (`@pydanny`_) for his support, time and the opportunity given to me to do this. 55 | * The name of the project was suggested by the fantastic Audrey Roy (`@audreyr`_) 56 | * To Kenneth Love (`@kennethlove`_) for creating django-uni-form-contrib from which bootstrap template pack was started. 57 | 58 | .. _`@audreyr`: https://github.com/audreyr 59 | .. _`@pydanny`: https://github.com/pydanny 60 | .. _`@kennethlove`: https://github.com/kennethlove 61 | 62 | Note 63 | ---- 64 | 65 | django-crispy-forms supports Django 1.3 or higher with Python 2.6.x, Python 2.7.x and Python 3.3.x. If you need to support Python 2.5 or Django 1.2 you will need to use a version of django-crispy-forms less than 1.3. For earlier versions of Django or Python you will need to use django-uni-form 0.7.0. 66 | 67 | .. _Django: http://djangoproject.com 68 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | set SPHINXBUILD=sphinx-build 6 | set ALLSPHINXOPTS=-d _build/doctrees %SPHINXOPTS% . 7 | if NOT "%PAPER%" == "" ( 8 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 9 | ) 10 | 11 | if "%1" == "" goto help 12 | 13 | if "%1" == "help" ( 14 | :help 15 | echo.Please use `make ^` where ^ is one of 16 | echo. html to make standalone HTML files 17 | echo. dirhtml to make HTML files named index.html in directories 18 | echo. pickle to make pickle files 19 | echo. json to make JSON files 20 | echo. htmlhelp to make HTML files and a HTML help project 21 | echo. qthelp to make HTML files and a qthelp project 22 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 23 | echo. changes to make an overview over all changed/added/deprecated items 24 | echo. linkcheck to check all external links for integrity 25 | echo. doctest to run all doctests embedded in the documentation if enabled 26 | goto end 27 | ) 28 | 29 | if "%1" == "clean" ( 30 | for /d %%i in (_build\*) do rmdir /q /s %%i 31 | del /q /s _build\* 32 | goto end 33 | ) 34 | 35 | if "%1" == "html" ( 36 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% _build/html 37 | echo. 38 | echo.Build finished. The HTML pages are in _build/html. 39 | goto end 40 | ) 41 | 42 | if "%1" == "dirhtml" ( 43 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% _build/dirhtml 44 | echo. 45 | echo.Build finished. The HTML pages are in _build/dirhtml. 46 | goto end 47 | ) 48 | 49 | if "%1" == "pickle" ( 50 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% _build/pickle 51 | echo. 52 | echo.Build finished; now you can process the pickle files. 53 | goto end 54 | ) 55 | 56 | if "%1" == "json" ( 57 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% _build/json 58 | echo. 59 | echo.Build finished; now you can process the JSON files. 60 | goto end 61 | ) 62 | 63 | if "%1" == "htmlhelp" ( 64 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% _build/htmlhelp 65 | echo. 66 | echo.Build finished; now you can run HTML Help Workshop with the ^ 67 | .hhp project file in _build/htmlhelp. 68 | goto end 69 | ) 70 | 71 | if "%1" == "qthelp" ( 72 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% _build/qthelp 73 | echo. 74 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 75 | .qhcp project file in _build/qthelp, like this: 76 | echo.^> qcollectiongenerator _build\qthelp\django-uni-form.qhcp 77 | echo.To view the help file: 78 | echo.^> assistant -collectionFile _build\qthelp\django-uni-form.ghc 79 | goto end 80 | ) 81 | 82 | if "%1" == "latex" ( 83 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% _build/latex 84 | echo. 85 | echo.Build finished; the LaTeX files are in _build/latex. 86 | goto end 87 | ) 88 | 89 | if "%1" == "changes" ( 90 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% _build/changes 91 | echo. 92 | echo.The overview file is in _build/changes. 93 | goto end 94 | ) 95 | 96 | if "%1" == "linkcheck" ( 97 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% _build/linkcheck 98 | echo. 99 | echo.Link check complete; look for any errors in the above output ^ 100 | or in _build/linkcheck/output.txt. 101 | goto end 102 | ) 103 | 104 | if "%1" == "doctest" ( 105 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% _build/doctest 106 | echo. 107 | echo.Testing of doctests in the sources finished, look at the ^ 108 | results in _build/doctest/output.txt. 109 | goto end 110 | ) 111 | 112 | :end 113 | -------------------------------------------------------------------------------- /crispy_forms/tests/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.db import models 3 | 4 | from crispy_forms.helper import FormHelper 5 | 6 | 7 | class TestForm(forms.Form): 8 | is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput()) 9 | email = forms.EmailField(label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email") 10 | password1 = forms.CharField(label="password", max_length=30, required=True, widget=forms.PasswordInput()) 11 | password2 = forms.CharField(label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput()) 12 | first_name = forms.CharField(label="first name", max_length=5, required=True, widget=forms.TextInput()) 13 | last_name = forms.CharField(label="last name", max_length=5, required=True, widget=forms.TextInput()) 14 | datetime_field = forms.DateTimeField(label="date time", widget=forms.SplitDateTimeWidget()) 15 | 16 | def clean(self): 17 | super(TestForm, self).clean() 18 | password1 = self.cleaned_data.get('password1', None) 19 | password2 = self.cleaned_data.get('password2', None) 20 | if not password1 and not password2 or password1 != password2: 21 | raise forms.ValidationError("Passwords dont match") 22 | 23 | return self.cleaned_data 24 | 25 | 26 | class TestForm2(TestForm): 27 | def __init__(self, *args, **kwargs): 28 | super(TestForm2, self).__init__(*args, **kwargs) 29 | self.helper = FormHelper(self) 30 | 31 | 32 | class CheckboxesTestForm(forms.Form): 33 | checkboxes = forms.MultipleChoiceField( 34 | choices = ( 35 | (1, "Option one"), 36 | (2, "Option two"), 37 | (3, "Option three") 38 | ), 39 | initial = (1,), 40 | widget = forms.CheckboxSelectMultiple, 41 | ) 42 | 43 | alphacheckboxes = forms.MultipleChoiceField( 44 | choices = ( 45 | ('option_one', "Option one"), 46 | ('option_two', "Option two"), 47 | ('option_three', "Option three") 48 | ), 49 | initial = ('option_two', 'option_three'), 50 | widget = forms.CheckboxSelectMultiple, 51 | ) 52 | 53 | numeric_multiple_checkboxes = forms.MultipleChoiceField( 54 | choices = ( 55 | (1, "Option one"), 56 | (2, "Option two"), 57 | (3, "Option three") 58 | ), 59 | initial = (1, 2), 60 | widget = forms.CheckboxSelectMultiple, 61 | ) 62 | 63 | inline_radios = forms.ChoiceField( 64 | choices = ( 65 | ('option_one', "Option one"), 66 | ('option_two', "Option two"), 67 | ), 68 | widget = forms.RadioSelect, 69 | initial = 'option_two', 70 | ) 71 | 72 | 73 | class CrispyTestModel(models.Model): 74 | email = models.CharField(max_length=20) 75 | password = models.CharField(max_length=20) 76 | 77 | 78 | class TestForm3(forms.ModelForm): 79 | class Meta: 80 | model = CrispyTestModel 81 | fields = ['email', 'password'] 82 | exclude = ['password'] 83 | 84 | def __init__(self, *args, **kwargs): 85 | super(TestForm3, self).__init__(*args, **kwargs) 86 | self.helper = FormHelper(self) 87 | 88 | 89 | class TestForm4(forms.ModelForm): 90 | class Meta: 91 | model = CrispyTestModel 92 | 93 | 94 | class TestForm5(forms.Form): 95 | choices = [ 96 | (1, 1), 97 | (2, 2), 98 | (1000, 1000), 99 | ] 100 | checkbox_select_multiple = forms.MultipleChoiceField( 101 | widget=forms.CheckboxSelectMultiple, 102 | choices=choices 103 | ) 104 | radio_select = forms.ChoiceField( 105 | widget=forms.RadioSelect, 106 | choices=choices 107 | ) 108 | pk = forms.IntegerField() 109 | -------------------------------------------------------------------------------- /crispy_forms/templatetags/crispy_forms_filters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.conf import settings 3 | from django.forms import forms 4 | from django.forms.formsets import BaseFormSet 5 | from django.template import Context 6 | from django.template.loader import get_template 7 | from django.utils.functional import memoize 8 | from django.utils.safestring import mark_safe 9 | from django import template 10 | 11 | from crispy_forms.exceptions import CrispyError 12 | from crispy_forms.utils import flatatt 13 | 14 | TEMPLATE_PACK = getattr(settings, 'CRISPY_TEMPLATE_PACK', 'bootstrap') 15 | DEBUG = getattr(settings, 'DEBUG', False) 16 | 17 | 18 | def uni_formset_template(template_pack=TEMPLATE_PACK): 19 | return get_template('%s/uni_formset.html' % template_pack) 20 | uni_formset_template = memoize(uni_formset_template, {}, 1) 21 | 22 | 23 | def uni_form_template(template_pack=TEMPLATE_PACK): 24 | return get_template('%s/uni_form.html' % template_pack) 25 | uni_form_template = memoize(uni_form_template, {}, 1) 26 | 27 | register = template.Library() 28 | 29 | 30 | @register.filter(name='crispy') 31 | def as_crispy_form(form, template_pack=TEMPLATE_PACK, label_class="", field_class=""): 32 | """ 33 | The original and still very useful way to generate a div elegant form/formset:: 34 | 35 | {% load crispy_forms_tags %} 36 | 37 |
    38 | {% csrf_token %} 39 | {{ myform|crispy }} 40 |
    41 | 42 | or, if you want to explicitly set the template pack:: 43 | 44 | {{ myform|crispy:"bootstrap" }} 45 | 46 | In ``bootstrap3`` for horizontal forms you can do:: 47 | 48 | {{ myform|label_class:"col-lg-2",field_class:"col-lg-8" }} 49 | """ 50 | if isinstance(form, BaseFormSet): 51 | template = uni_formset_template(template_pack) 52 | c = Context({ 53 | 'formset': form, 54 | 'form_show_errors': True, 55 | 'form_show_labels': True, 56 | 'label_class': label_class, 57 | 'field_class': field_class, 58 | }) 59 | else: 60 | template = uni_form_template(template_pack) 61 | c = Context({ 62 | 'form': form, 63 | 'form_show_errors': True, 64 | 'form_show_labels': True, 65 | 'label_class': label_class, 66 | 'field_class': field_class, 67 | }) 68 | return template.render(c) 69 | 70 | 71 | @register.filter(name='as_crispy_errors') 72 | def as_crispy_errors(form, template_pack=TEMPLATE_PACK): 73 | """ 74 | Renders only form errors the same way as django-crispy-forms:: 75 | 76 | {% load crispy_forms_tags %} 77 | {{ form|as_crispy_errors }} 78 | 79 | or:: 80 | 81 | {{ form|as_crispy_errors:"bootstrap" }} 82 | """ 83 | if isinstance(form, BaseFormSet): 84 | template = get_template('%s/errors_formset.html' % template_pack) 85 | c = Context({'formset': form}) 86 | else: 87 | template = get_template('%s/errors.html' % template_pack) 88 | c = Context({'form': form}) 89 | return template.render(c) 90 | 91 | 92 | @register.filter(name='as_crispy_field') 93 | def as_crispy_field(field, template_pack=TEMPLATE_PACK): 94 | """ 95 | Renders a form field like a django-crispy-forms field:: 96 | 97 | {% load crispy_forms_tags %} 98 | {{ form.field|as_crispy_field }} 99 | 100 | or:: 101 | 102 | {{ form.field|as_crispy_field:"bootstrap" }} 103 | """ 104 | if not isinstance(field, forms.BoundField) and DEBUG: 105 | raise CrispyError('|as_crispy_field got passed an invalid or inexistent field') 106 | 107 | template = get_template('%s/field.html' % template_pack) 108 | c = Context({'field': field, 'form_show_errors': True, 'form_show_labels': True}) 109 | return template.render(c) 110 | 111 | 112 | @register.filter(name='flatatt') 113 | def flatatt_filter(attrs): 114 | return mark_safe(flatatt(attrs)) 115 | -------------------------------------------------------------------------------- /docs/template_packs.rst: -------------------------------------------------------------------------------- 1 | ===================================== 2 | How to create your own template packs 3 | ===================================== 4 | 5 | First you will have to name your template pack, for this you can't use the name of one of the available template packs in crispy-forms, due to name collisions. For example, let's say in the company we work for, a designer has come up with a CSS bootstrap internally known as ``chocolat``. The company has a Django project that needs to start using ``chocolat``, therefore we need to create a folder named ``chocolat`` within our templates directory. Check your ``TEMPLATE_DIRS`` setting in Django and pick your preferred path. 6 | 7 | Once we have that folder created, we will have to create a concrete directory hierarchy so that crispy-forms can pick it up. This is what bootstrap template pack (v2) looks like:: 8 | 9 | . 10 | ├── accordion-group.html 11 | ├── accordion.html 12 | ├── betterform.html 13 | ├── display_form.html 14 | ├── errors.html 15 | ├── errors_formset.html 16 | ├── * field.html 17 | ├── layout 18 | │   ├── alert.html 19 | │   ├── * baseinput.html 20 | │   ├── button.html 21 | │   ├── checkboxselectmultiple.html 22 | │   ├── checkboxselectmultiple_inline.html 23 | │   ├── div.html 24 | │   ├── field_errors.html 25 | │   ├── field_errors_block.html 26 | │   ├── field_with_buttons.html 27 | │   ├── fieldset.html 28 | │   ├── formactions.html 29 | │   ├── help_text.html 30 | │   ├── help_text_and_errors.html 31 | │   ├── multifield.html 32 | │   ├── prepended_appended_text.html 33 | │   ├── radioselect.html 34 | │   ├── radioselect_inline.html 35 | │   ├── tab-link.html 36 | │   ├── tab.html 37 | │   └── uneditable_input.html 38 | ├── table_inline_formset.html 39 | ├── * uni_form.html 40 | ├── uni_formset.html 41 | ├── * whole_uni_form.html 42 | └── whole_uni_formset.html 43 | 44 | Take it easy, don't panic, we won't need this many templates for our template pack. Templates are also quite simple to follow if you understand what problem crispy-forms solves. The bare minimum would be the templates marked with an asterisk. 45 | 46 | Fundamentals 47 | ~~~~~~~~~~~~ 48 | 49 | First, since crispy-forms 1.5.0, template packs are self contained, you cannot reference a template from a different template pack. 50 | 51 | crispy-forms has many features, but maybe you don't need your template pack to cover all of them. ``{% crispy %}`` templatetag renders forms using a global structure contained within ``whole_uni_form.html``. However, ``|crispy`` filter uses ``uni_form.html``. As you've probably guessed, the name of the templates comes from the old days of django-uni-form. Anyway, as an example, if we don't use ``|crispy`` filter, we don't really need to maintain a ``uni_form.html`` template within our template pack. 52 | 53 | If we are planning on using formsets + ``{% crispy %}`` we will need a ``whole_uni_formset.html``, instead if we use formsets + ``|crispy`` we will need ``uni_formset.html``. 54 | 55 | All of these templates use a tag named ``{% crispy_field %}`` that is loaded doing ``{% load crispy_forms_field %}``, that generates the html for ```` using ``field.html`` template, but previously doing Python preparation beforehand. In case you wonder the code for this tag lives in ``crispy_forms.templatetags.crispy_forms_field``, together with some other stuff. 56 | 57 | So a template pack for a very basic example covering only forms and the usage of ``{% crispy %}`` tag, would need 2 templates: ``whole_uni_form.html``, ``field.html``. Well, that's not completely true, because every layout object has a template attached. So if we wanted to use ``Div``, we would need ``div.html``. Some are not that obvious, if you need ``Submit``, you will need ``baseinput.html``. Some layout objects, don't really have a template attached, like ``HTML``. 58 | 59 | In the previous template tree, there are some templates that are there for DRY purposes, they are not really compulsory or part of a layout object, so don't worry too much. 60 | 61 | Starting 62 | ~~~~~~~~ 63 | 64 | Now your best bet is probably start copying some or all of the templates under an existing crispy-forms template pack, such as ``bootstrap3``, then drop the ones you don't need. Next step is edit those templates, and adjust the HTML/CSS making it align with ``chocolat``, that sometimes means dropping/adding divs, classes and other stuff around. You can always create a form in your application, with a helper attached to that new template pack and start trying out your adaptation right away. 65 | 66 | Currently, there is an existing template pack for crispy-forms that doesn't live in core, developed by David Thenon as an external pluggable application named `crispy-forms-foundation`_, it's also a good reference to check out. 67 | 68 | Beware that crispy-forms evolves and adds new ``FormHelper.attributes``, if you want to use those in the future you will have to adapt your templates adding those variables and its handling. 69 | 70 | .. _`crispy-forms-foundation`: https://github.com/sveetch/crispy-forms-foundation 71 | -------------------------------------------------------------------------------- /docs/_themes/flask_theme_support.py: -------------------------------------------------------------------------------- 1 | # flasky extensions. flasky pygments style based on tango style 2 | from pygments.style import Style 3 | from pygments.token import Keyword, Name, Comment, String, Error, \ 4 | Number, Operator, Generic, Whitespace, Punctuation, Other, Literal 5 | 6 | 7 | class FlaskyStyle(Style): 8 | background_color = "#f8f8f8" 9 | default_style = "" 10 | 11 | styles = { 12 | # No corresponding class for the following: 13 | #Text: "", # class: '' 14 | Whitespace: "underline #f8f8f8", # class: 'w' 15 | Error: "#a40000 border:#ef2929", # class: 'err' 16 | Other: "#000000", # class 'x' 17 | 18 | Comment: "italic #8f5902", # class: 'c' 19 | Comment.Preproc: "noitalic", # class: 'cp' 20 | 21 | Keyword: "bold #004461", # class: 'k' 22 | Keyword.Constant: "bold #004461", # class: 'kc' 23 | Keyword.Declaration: "bold #004461", # class: 'kd' 24 | Keyword.Namespace: "bold #004461", # class: 'kn' 25 | Keyword.Pseudo: "bold #004461", # class: 'kp' 26 | Keyword.Reserved: "bold #004461", # class: 'kr' 27 | Keyword.Type: "bold #004461", # class: 'kt' 28 | 29 | Operator: "#582800", # class: 'o' 30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 31 | 32 | Punctuation: "bold #000000", # class: 'p' 33 | 34 | # because special names such as Name.Class, Name.Function, etc. 35 | # are not recognized as such later in the parsing, we choose them 36 | # to look the same as ordinary variables. 37 | Name: "#000000", # class: 'n' 38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 39 | Name.Builtin: "#004461", # class: 'nb' 40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 41 | Name.Class: "#000000", # class: 'nc' - to be revised 42 | Name.Constant: "#000000", # class: 'no' - to be revised 43 | Name.Decorator: "#888", # class: 'nd' - to be revised 44 | Name.Entity: "#ce5c00", # class: 'ni' 45 | Name.Exception: "bold #cc0000", # class: 'ne' 46 | Name.Function: "#000000", # class: 'nf' 47 | Name.Property: "#000000", # class: 'py' 48 | Name.Label: "#f57900", # class: 'nl' 49 | Name.Namespace: "#000000", # class: 'nn' - to be revised 50 | Name.Other: "#000000", # class: 'nx' 51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 52 | Name.Variable: "#000000", # class: 'nv' - to be revised 53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 56 | 57 | Number: "#990000", # class: 'm' 58 | 59 | Literal: "#000000", # class: 'l' 60 | Literal.Date: "#000000", # class: 'ld' 61 | 62 | String: "#4e9a06", # class: 's' 63 | String.Backtick: "#4e9a06", # class: 'sb' 64 | String.Char: "#4e9a06", # class: 'sc' 65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 66 | String.Double: "#4e9a06", # class: 's2' 67 | String.Escape: "#4e9a06", # class: 'se' 68 | String.Heredoc: "#4e9a06", # class: 'sh' 69 | String.Interpol: "#4e9a06", # class: 'si' 70 | String.Other: "#4e9a06", # class: 'sx' 71 | String.Regex: "#4e9a06", # class: 'sr' 72 | String.Single: "#4e9a06", # class: 's1' 73 | String.Symbol: "#4e9a06", # class: 'ss' 74 | 75 | Generic: "#000000", # class: 'g' 76 | Generic.Deleted: "#a40000", # class: 'gd' 77 | Generic.Emph: "italic #000000", # class: 'ge' 78 | Generic.Error: "#ef2929", # class: 'gr' 79 | Generic.Heading: "bold #000080", # class: 'gh' 80 | Generic.Inserted: "#00A000", # class: 'gi' 81 | Generic.Output: "#888", # class: 'go' 82 | Generic.Prompt: "#745334", # class: 'gp' 83 | Generic.Strong: "bold #000000", # class: 'gs' 84 | Generic.Subheading: "bold #800080", # class: 'gu' 85 | Generic.Traceback: "bold #a40000", # class: 'gt' 86 | } 87 | -------------------------------------------------------------------------------- /crispy_forms/tests/test_tags.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.conf import settings 3 | from django.forms.forms import BoundField 4 | from django.forms.models import formset_factory 5 | from django.template import loader, Context 6 | 7 | from .base import CrispyTestCase 8 | from .forms import TestForm 9 | from crispy_forms.templatetags.crispy_forms_field import crispy_addon 10 | 11 | 12 | 13 | class TestBasicFunctionalityTags(CrispyTestCase): 14 | def test_as_crispy_errors_form_without_non_field_errors(self): 15 | template = loader.get_template_from_string(u""" 16 | {% load crispy_forms_tags %} 17 | {{ form|as_crispy_errors }} 18 | """) 19 | form = TestForm({'password1': "god", 'password2': "god"}) 20 | form.is_valid() 21 | 22 | c = Context({'form': form}) 23 | html = template.render(c) 24 | self.assertFalse("errorMsg" in html or "alert" in html) 25 | 26 | def test_as_crispy_errors_form_with_non_field_errors(self): 27 | template = loader.get_template_from_string(u""" 28 | {% load crispy_forms_tags %} 29 | {{ form|as_crispy_errors }} 30 | """) 31 | form = TestForm({'password1': "god", 'password2': "wargame"}) 32 | form.is_valid() 33 | 34 | c = Context({'form': form}) 35 | html = template.render(c) 36 | self.assertTrue("errorMsg" in html or "alert" in html) 37 | self.assertTrue("
  • Passwords dont match
  • " in html) 38 | self.assertFalse("

    " in html) 39 | 40 | def test_crispy_filter_with_form(self): 41 | template = loader.get_template_from_string(u""" 42 | {% load crispy_forms_tags %} 43 | {{ form|crispy }} 44 | """) 45 | c = Context({'form': TestForm()}) 46 | html = template.render(c) 47 | 48 | self.assertTrue("" not in html) 49 | self.assertTrue("id_is_company" in html) 50 | self.assertEqual(html.count(' 53 | {% crispy formset helper %} 54 |
    55 | 56 |
    57 | 58 | 59 | Finally, model formsets and inline formsets are rendered exactly the same as formsets, the only difference is how you build them in your Django code. 60 | 61 | .. _`Django official formset docs`: https://docs.djangoproject.com/en/dev/topics/forms/formsets/ 62 | 63 | Extra context 64 | ~~~~~~~~~~~~~ 65 | 66 | Rendering any kind of formset with crispy injects some extra context in the layout rendering so that you can do things like:: 67 | 68 | HTML("{% if forloop.first %}Message displayed only in the first form of a formset forms list{% endif %}", 69 | Fieldset("Item {{ forloop.counter }}", 'field-1', [...]) 70 | 71 | Basically you can access a ``forloop`` Django node, as if you were rendering your formsets forms using a for loop. 72 | 73 | 74 | Custom templates and table inline formsets 75 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 76 | 77 | Default formset template will render your formset's form using divs, but many times people prefer tables for formsets. Don't worry, crispy-forms's got you covered. ``FormHelper`` has an attribute named ``template`` that can be used to specify a custom template for rendering a form or formset, in this case a formset. Obviously when we specify a ``template`` attribute, we are making that helper only usuable with forms or formsets. 78 | 79 | The name of the template to use is **table_inline_formset.html** and you use it doing:: 80 | 81 | helper.template = 'bootstrap/table_inline_formset.html' 82 | 83 | The best part is that if this template doesn't do exactly what you want, you can copy it into your templates folder, customize it and then link your helper to your alternative version. If you think what you are missing would be valuable to others, then please submit a pull request at github. 84 | 85 | .. warning :: 86 | 87 | This template doesn't currently take into account any layout you have specified and only works with bootstrap template pack. 88 | 89 | 90 | Formset forms with different layouts 91 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 92 | 93 | By default crispy-forms formset rendering shares the same layout among all formset's forms. This is the case 99% of the times. But maybe you want to render your formset's forms using different layouts that you cannot achieve using the extra context injected, for that you will have to create and use a custom template. Most likely you will want to do:: 94 | 95 | {{ formset.management_form|crispy }} 96 | {% for form in formset %} 97 | {% crispy form %} 98 | {% endfor %} 99 | 100 | Where every ``form`` has a ``helper`` attribute from which crispy will grab the layout. In your view you will need to change the layout or use a different help for every formset's form. Make sure that you have ``form_tag`` attribute set to ``False``, otherwise you will get 3 individual forms rendered. 101 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- flasky theme based on nature theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | color: #000; 20 | background: white; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 40px auto 0 auto; 32 | width: 700px; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #ffffff; 41 | color: #3E4349; 42 | padding: 0 30px 30px 30px; 43 | } 44 | 45 | img.floatingflask { 46 | padding: 0 0 10px 10px; 47 | float: right; 48 | } 49 | 50 | div.footer { 51 | text-align: right; 52 | color: #888; 53 | padding: 10px; 54 | font-size: 14px; 55 | width: 650px; 56 | margin: 0 auto 40px auto; 57 | } 58 | 59 | div.footer a { 60 | color: #888; 61 | text-decoration: underline; 62 | } 63 | 64 | div.related { 65 | line-height: 32px; 66 | color: #888; 67 | } 68 | 69 | div.related ul { 70 | padding: 0 0 0 10px; 71 | } 72 | 73 | div.related a { 74 | color: #444; 75 | } 76 | 77 | /* -- body styles ----------------------------------------------------------- */ 78 | 79 | a { 80 | color: #004B6B; 81 | text-decoration: underline; 82 | } 83 | 84 | a:hover { 85 | color: #6D4100; 86 | text-decoration: underline; 87 | } 88 | 89 | div.body { 90 | padding-bottom: 40px; /* saved for footer */ 91 | } 92 | 93 | div.body h1, 94 | div.body h2, 95 | div.body h3, 96 | div.body h4, 97 | div.body h5, 98 | div.body h6 { 99 | font-family: 'Garamond', 'Georgia', serif; 100 | font-weight: normal; 101 | margin: 30px 0px 10px 0px; 102 | padding: 0; 103 | } 104 | 105 | {% if theme_index_logo %} 106 | div.indexwrapper h1 { 107 | text-indent: -999999px; 108 | background: url({{ theme_index_logo }}) no-repeat center center; 109 | height: {{ theme_index_logo_height }}; 110 | } 111 | {% endif %} 112 | 113 | div.body h2 { font-size: 180%; } 114 | div.body h3 { font-size: 150%; } 115 | div.body h4 { font-size: 130%; } 116 | div.body h5 { font-size: 100%; } 117 | div.body h6 { font-size: 100%; } 118 | 119 | a.headerlink { 120 | color: white; 121 | padding: 0 4px; 122 | text-decoration: none; 123 | } 124 | 125 | a.headerlink:hover { 126 | color: #444; 127 | background: #eaeaea; 128 | } 129 | 130 | div.body p, div.body dd, div.body li { 131 | line-height: 1.4em; 132 | } 133 | 134 | div.admonition { 135 | background: #fafafa; 136 | margin: 20px -30px; 137 | padding: 10px 30px; 138 | border-top: 1px solid #ccc; 139 | border-bottom: 1px solid #ccc; 140 | } 141 | 142 | div.admonition p.admonition-title { 143 | font-family: 'Garamond', 'Georgia', serif; 144 | font-weight: normal; 145 | font-size: 24px; 146 | margin: 0 0 10px 0; 147 | padding: 0; 148 | line-height: 1; 149 | } 150 | 151 | div.admonition p.last { 152 | margin-bottom: 0; 153 | } 154 | 155 | div.highlight{ 156 | background-color: white; 157 | } 158 | 159 | dt:target, .highlight { 160 | background: #FAF3E8; 161 | } 162 | 163 | div.note { 164 | background-color: #eee; 165 | border: 1px solid #ccc; 166 | } 167 | 168 | div.seealso { 169 | background-color: #ffc; 170 | border: 1px solid #ff6; 171 | } 172 | 173 | div.topic { 174 | background-color: #eee; 175 | } 176 | 177 | div.warning { 178 | background-color: #ffe4e4; 179 | border: 1px solid #f66; 180 | } 181 | 182 | p.admonition-title { 183 | display: inline; 184 | } 185 | 186 | p.admonition-title:after { 187 | content: ":"; 188 | } 189 | 190 | pre, tt { 191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 192 | font-size: 0.85em; 193 | } 194 | 195 | img.screenshot { 196 | } 197 | 198 | tt.descname, tt.descclassname { 199 | font-size: 0.95em; 200 | } 201 | 202 | tt.descname { 203 | padding-right: 0.08em; 204 | } 205 | 206 | img.screenshot { 207 | -moz-box-shadow: 2px 2px 4px #eee; 208 | -webkit-box-shadow: 2px 2px 4px #eee; 209 | box-shadow: 2px 2px 4px #eee; 210 | } 211 | 212 | table.docutils { 213 | border: 1px solid #888; 214 | -moz-box-shadow: 2px 2px 4px #eee; 215 | -webkit-box-shadow: 2px 2px 4px #eee; 216 | box-shadow: 2px 2px 4px #eee; 217 | } 218 | 219 | table.docutils td, table.docutils th { 220 | border: 1px solid #888; 221 | padding: 0.25em 0.7em; 222 | } 223 | 224 | table.field-list, table.footnote { 225 | border: none; 226 | -moz-box-shadow: none; 227 | -webkit-box-shadow: none; 228 | box-shadow: none; 229 | } 230 | 231 | table.footnote { 232 | margin: 15px 0; 233 | width: 100%; 234 | border: 1px solid #eee; 235 | } 236 | 237 | table.field-list th { 238 | padding: 0 0.8em 0 0; 239 | } 240 | 241 | table.field-list td { 242 | padding: 0; 243 | } 244 | 245 | table.footnote td { 246 | padding: 0.5em; 247 | } 248 | 249 | dl { 250 | margin: 0; 251 | padding: 0; 252 | } 253 | 254 | dl dd { 255 | margin-left: 30px; 256 | } 257 | 258 | pre { 259 | padding: 0; 260 | margin: 15px -30px; 261 | padding: 8px; 262 | line-height: 1.3em; 263 | padding: 7px 30px; 264 | background: #eee; 265 | border-radius: 2px; 266 | -moz-border-radius: 2px; 267 | -webkit-border-radius: 2px; 268 | } 269 | 270 | dl pre { 271 | margin-left: -60px; 272 | padding-left: 60px; 273 | } 274 | 275 | tt { 276 | background-color: #ecf0f3; 277 | color: #222; 278 | /* padding: 1px 2px; */ 279 | } 280 | 281 | tt.xref, a tt { 282 | background-color: #FBFBFB; 283 | } 284 | 285 | a:hover tt { 286 | background: #EEE; 287 | } 288 | -------------------------------------------------------------------------------- /crispy_forms/templatetags/crispy_forms_field.py: -------------------------------------------------------------------------------- 1 | try: 2 | from itertools import izip 3 | except ImportError: 4 | izip = zip 5 | 6 | from django import forms 7 | from django import template 8 | from django.template import loader, Context 9 | from django.conf import settings 10 | 11 | from crispy_forms.utils import TEMPLATE_PACK 12 | 13 | register = template.Library() 14 | 15 | class_converter = { 16 | "textinput": "textinput textInput", 17 | "fileinput": "fileinput fileUpload", 18 | "passwordinput": "textinput textInput", 19 | } 20 | class_converter.update(getattr(settings, 'CRISPY_CLASS_CONVERTERS', {})) 21 | 22 | 23 | @register.filter 24 | def is_checkbox(field): 25 | return isinstance(field.field.widget, forms.CheckboxInput) 26 | 27 | 28 | @register.filter 29 | def is_password(field): 30 | return isinstance(field.field.widget, forms.PasswordInput) 31 | 32 | 33 | @register.filter 34 | def is_radioselect(field): 35 | return isinstance(field.field.widget, forms.RadioSelect) 36 | 37 | 38 | @register.filter 39 | def is_select(field): 40 | return isinstance(field.field.widget, forms.Select) 41 | 42 | 43 | @register.filter 44 | def is_checkboxselectmultiple(field): 45 | return isinstance(field.field.widget, forms.CheckboxSelectMultiple) 46 | 47 | 48 | @register.filter 49 | def is_file(field): 50 | return isinstance(field.field.widget, forms.ClearableFileInput) 51 | 52 | 53 | @register.filter 54 | def classes(field): 55 | """ 56 | Returns CSS classes of a field 57 | """ 58 | return field.widget.attrs.get('class', None) 59 | 60 | 61 | @register.filter 62 | def css_class(field): 63 | """ 64 | Returns widgets class name in lowercase 65 | """ 66 | return field.field.widget.__class__.__name__.lower() 67 | 68 | 69 | def pairwise(iterable): 70 | "s -> (s0,s1), (s2,s3), (s4, s5), ..." 71 | a = iter(iterable) 72 | return izip(a, a) 73 | 74 | 75 | class CrispyFieldNode(template.Node): 76 | def __init__(self, field, attrs): 77 | self.field = field 78 | self.attrs = attrs 79 | self.html5_required = 'html5_required' 80 | 81 | def render(self, context): 82 | # Nodes are not threadsafe so we must store and look up our instance 83 | # variables in the current rendering context first 84 | if self not in context.render_context: 85 | context.render_context[self] = ( 86 | template.Variable(self.field), 87 | self.attrs, 88 | template.Variable(self.html5_required) 89 | ) 90 | 91 | field, attrs, html5_required = context.render_context[self] 92 | field = field.resolve(context) 93 | try: 94 | html5_required = html5_required.resolve(context) 95 | except template.VariableDoesNotExist: 96 | html5_required = False 97 | 98 | # If template pack has been overriden in FormHelper we can pick it from context 99 | template_pack = context.get('template_pack', TEMPLATE_PACK) 100 | 101 | widgets = getattr(field.field.widget, 'widgets', [field.field.widget]) 102 | 103 | if isinstance(attrs, dict): 104 | attrs = [attrs] * len(widgets) 105 | 106 | for widget, attr in zip(widgets, attrs): 107 | class_name = widget.__class__.__name__.lower() 108 | class_name = class_converter.get(class_name, class_name) 109 | css_class = widget.attrs.get('class', '') 110 | if css_class: 111 | if css_class.find(class_name) == -1: 112 | css_class += " %s" % class_name 113 | else: 114 | css_class = class_name 115 | 116 | if ( 117 | template_pack == 'bootstrap3' 118 | and not is_checkbox(field) 119 | and not is_file(field) 120 | ): 121 | css_class += ' form-control' 122 | 123 | widget.attrs['class'] = css_class 124 | 125 | # HTML5 required attribute 126 | if html5_required and field.field.required and 'required' not in widget.attrs: 127 | if field.field.widget.__class__.__name__ is not 'RadioSelect': 128 | widget.attrs['required'] = 'required' 129 | 130 | for attribute_name, attribute in attr.items(): 131 | attribute_name = template.Variable(attribute_name).resolve(context) 132 | 133 | if attribute_name in widget.attrs: 134 | widget.attrs[attribute_name] += " " + template.Variable(attribute).resolve(context) 135 | else: 136 | widget.attrs[attribute_name] = template.Variable(attribute).resolve(context) 137 | 138 | return field 139 | 140 | 141 | @register.tag(name="crispy_field") 142 | def crispy_field(parser, token): 143 | """ 144 | {% crispy_field field attrs %} 145 | """ 146 | token = token.split_contents() 147 | field = token.pop(1) 148 | attrs = {} 149 | 150 | # We need to pop tag name, or pairwise would fail 151 | token.pop(0) 152 | for attribute_name, value in pairwise(token): 153 | attrs[attribute_name] = value 154 | 155 | return CrispyFieldNode(field, attrs) 156 | 157 | 158 | @register.simple_tag() 159 | def crispy_addon(field, append="", prepend="", form_show_labels=True): 160 | """ 161 | Renders a form field using bootstrap's prepended or appended text:: 162 | 163 | {% crispy_addon form.my_field prepend="$" append=".00" %} 164 | 165 | You can also just prepend or append like so 166 | 167 | {% crispy_addon form.my_field prepend="$" %} 168 | {% crispy_addon form.my_field append=".00" %} 169 | """ 170 | if (field): 171 | context = Context({ 172 | 'field': field, 173 | 'form_show_errors': True, 174 | 'form_show_labels': form_show_labels, 175 | }) 176 | template = loader.get_template('%s/layout/prepended_appended_text.html' % TEMPLATE_PACK) 177 | context['crispy_prepended_text'] = prepend 178 | context['crispy_appended_text'] = append 179 | 180 | if not prepend and not append: 181 | raise TypeError("Expected a prepend and/or append argument") 182 | 183 | return template.render(context) 184 | 185 | -------------------------------------------------------------------------------- /crispy_forms/layout_slice.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from crispy_forms.compatibility import integer_types, string_types 3 | from crispy_forms.exceptions import DynamicError 4 | from crispy_forms.layout import Fieldset, MultiField 5 | from crispy_forms.bootstrap import Container 6 | 7 | 8 | class LayoutSlice(object): 9 | # List of layout objects that need args passed first before fields 10 | args_first = (Fieldset, MultiField, Container) 11 | 12 | def __init__(self, layout, key): 13 | self.layout = layout 14 | if isinstance(key, integer_types): 15 | self.slice = slice(key, key+1, 1) 16 | else: 17 | self.slice = key 18 | 19 | def wrapped_object(self, LayoutClass, fields, *args, **kwargs): 20 | """ 21 | Returns a layout object of type `LayoutClass` with `args` and `kwargs` that 22 | wraps `fields` inside. 23 | """ 24 | if args: 25 | if isinstance(fields, list): 26 | fields= tuple(fields) 27 | else: 28 | fields = (fields,) 29 | 30 | if LayoutClass in self.args_first: 31 | arguments = args + fields 32 | else: 33 | arguments = fields + args 34 | 35 | return LayoutClass(*arguments, **kwargs) 36 | else: 37 | if isinstance(fields, list): 38 | return LayoutClass(*fields, **kwargs) 39 | else: 40 | return LayoutClass(fields, **kwargs) 41 | 42 | def pre_map(self, function): 43 | """ 44 | Iterates over layout objects pointed in `self.slice` executing `function` on them. 45 | It passes `function` penultimate layout object and the position where to find last one 46 | """ 47 | if isinstance(self.slice, slice): 48 | for i in range(*self.slice.indices(len(self.layout.fields))): 49 | function(self.layout, i) 50 | 51 | elif isinstance(self.slice, list): 52 | # A list of pointers Ex: [[[0, 0], 'div'], [[0, 2, 3], 'field_name']] 53 | for pointer in self.slice: 54 | position = pointer[0] 55 | 56 | # If it's pointing first level 57 | if len(position) == 1: 58 | function(self.layout, position[-1]) 59 | else: 60 | layout_object = self.layout.fields[position[0]] 61 | for i in position[1:-1]: 62 | layout_object = layout_object.fields[i] 63 | 64 | try: 65 | function(layout_object, position[-1]) 66 | except IndexError: 67 | # We could avoid this exception, recalculating pointers. 68 | # However this case is most of the time an undesired behavior 69 | raise DynamicError("Trying to wrap a field within an already wrapped field, \ 70 | recheck your filter or layout") 71 | 72 | def wrap(self, LayoutClass, *args, **kwargs): 73 | """ 74 | Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with 75 | `args` and `kwargs` passed. 76 | """ 77 | def wrap_object(layout_object, j): 78 | layout_object.fields[j] = self.wrapped_object( 79 | LayoutClass, layout_object.fields[j], *args, **kwargs 80 | ) 81 | 82 | self.pre_map(wrap_object) 83 | 84 | def wrap_once(self, LayoutClass, *args, **kwargs): 85 | """ 86 | Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with 87 | `args` and `kwargs` passed, unless layout object's parent is already a subclass of 88 | `LayoutClass`. 89 | """ 90 | def wrap_object_once(layout_object, j): 91 | if not isinstance(layout_object, LayoutClass): 92 | layout_object.fields[j] = self.wrapped_object( 93 | LayoutClass, layout_object.fields[j], *args, **kwargs 94 | ) 95 | 96 | self.pre_map(wrap_object_once) 97 | 98 | def wrap_together(self, LayoutClass, *args, **kwargs): 99 | """ 100 | Wraps all layout objects pointed in `self.slice` together under a `LayoutClass` 101 | instance with `args` and `kwargs` passed. 102 | """ 103 | if isinstance(self.slice, slice): 104 | # The start of the slice is replaced 105 | start = self.slice.start if self.slice.start is not None else 0 106 | self.layout.fields[start] = self.wrapped_object( 107 | LayoutClass, self.layout.fields[self.slice], *args, **kwargs 108 | ) 109 | 110 | # The rest of places of the slice are removed, as they are included in the previous 111 | for i in reversed(range(*self.slice.indices(len(self.layout.fields)))): 112 | if i != start: 113 | del self.layout.fields[i] 114 | 115 | elif isinstance(self.slice, list): 116 | raise DynamicError("wrap_together doesn't work with filter, only with [] operator") 117 | 118 | def map(self, function): 119 | """ 120 | Iterates over layout objects pointed in `self.slice` executing `function` on them 121 | It passes `function` last layout object 122 | """ 123 | if isinstance(self.slice, slice): 124 | for i in range(*self.slice.indices(len(self.layout.fields))): 125 | function(self.layout.fields[i]) 126 | 127 | elif isinstance(self.slice, list): 128 | # A list of pointers Ex: [[[0, 0], 'div'], [[0, 2, 3], 'field_name']] 129 | for pointer in self.slice: 130 | position = pointer[0] 131 | 132 | layout_object = self.layout.fields[position[0]] 133 | for i in position[1:]: 134 | previous_layout_object = layout_object 135 | layout_object = layout_object.fields[i] 136 | 137 | # If update_attrs is applied to a string, we call to its wrapping layout object 138 | if ( 139 | function.__name__ == 'update_attrs' 140 | and isinstance(layout_object, string_types) 141 | ): 142 | function(previous_layout_object) 143 | else: 144 | function(layout_object) 145 | 146 | def update_attributes(self, **kwargs): 147 | """ 148 | Updates attributes of every layout object pointed in `self.slice` using kwargs 149 | """ 150 | def update_attrs(layout_object): 151 | if hasattr(layout_object, 'attrs'): 152 | layout_object.attrs.update(kwargs) 153 | 154 | self.map(update_attrs) 155 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | .. _`install`: 6 | 7 | Installing django-crispy-forms 8 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | 10 | Install latest stable version into your python path using pip or easy_install:: 11 | 12 | pip install --upgrade django-crispy-forms 13 | 14 | If you want to install development version (unstable), you can do so doing:: 15 | 16 | pip install git+git://github.com/maraujop/django-crispy-forms.git@dev#egg=django-crispy-forms 17 | 18 | Or, if you'd like to install the development version as a git repository (so 19 | you can ``git pull`` updates, use the ``-e`` flag with ``pip install``, like 20 | so:: 21 | 22 | pip install -e git+git://github.com/maraujop/django-crispy-forms.git@dev#egg=django-crispy-forms 23 | 24 | Add ``crispy_forms`` to your ``INSTALLED_APPS`` in settings.py:: 25 | 26 | INSTALLED_APPS = ( 27 | ... 28 | 'crispy_forms', 29 | ) 30 | 31 | In production environments, always activate Django template cache loader. This is available since Django 1.2 and what it does is basically load templates once, then cache the result for every subsequent render. This leads to a significant performance improvement. See how to set it up using fabulous `Django docs page`_. 32 | 33 | .. _`Django docs page`: https://docs.djangoproject.com/en/1.5/ref/templates/api/#django.template.loaders.cached.Loader 34 | 35 | Template packs 36 | ~~~~~~~~~~~~~~ 37 | 38 | Since version 1.1.0, django-crispy-forms has built-in support for different CSS frameworks, known as template packs within django-crispy-forms: 39 | 40 | * ``bootstrap`` `Bootstrap`_ is crispy-forms's default template pack, version 2 of the popular simple and flexible HTML, CSS, and Javascript for user interfaces from Twitter. 41 | * ``bootstrap3`` Twitter Bootstrap version 3. 42 | * ``uni-form`` `Uni-form`_ is a nice looking, well structured, highly customizable, accessible and usable forms. 43 | * ``foundation`` `Foundation`_ In creators words "The most advanced responsive front-end framework in the world". This template pack is externally available through `crispy-forms-foundation`_ 44 | 45 | If your form CSS framework is not supported and it's open source, you can create a template pack for it and submit a pull request in Github or create a ``crispy-forms-templatePackName`` project and let me know, so I can link it. 46 | 47 | You can set your default template pack for your project using ``CRISPY_TEMPLATE_PACK`` Django settings variable, setting it to one of the previous keywords:: 48 | 49 | CRISPY_TEMPLATE_PACK = 'uni_form' 50 | 51 | .. _`Bootstrap`: http://twitter.github.com/bootstrap/index.html 52 | .. _`Foundation`: http://foundation.zurb.com/ 53 | .. _`crispy-forms-foundation`: https://github.com/sveetch/crispy-forms-foundation 54 | 55 | Setting media files 56 | ~~~~~~~~~~~~~~~~~~~ 57 | 58 | crispy-forms no longer includes static files. You will need to include yourself the proper corresponding media files, depending on what CSS framework (Template pack) you are using. This might involve one or more CSS and JS files. Read CSS framework's docs for help on how to set it up. 59 | 60 | Moving from django-uni-form to django-crispy-forms 61 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 62 | 63 | django-crispy-forms was started as a friendly fork of django-uni-form. We are aware that a name change implies some hassle updating imports and templates, here is some help mitigating it. This is what you should replace when upgrading: 64 | 65 | 1. Your ``ÌNSTALLED_APPS`` should point to ``crispy_forms`` instead of ``uni_form`` 66 | 67 | 2. All imports have to be done from crispy forms:: 68 | 69 | from uni_form.helper import FormHelper 70 | from crispy_forms.helper import FormHelper 71 | 72 | In Linux You can use `rpl`_ to easily find and update the proper lines. Run in the root of your project the following command. It is strongly recommended that you have your project in a VCS or a backup, so you can rollback if something goes wrong:: 73 | 74 | rpl -R uni_form. crispy_forms. . 75 | 76 | .. _`rpl`: http://www.laffeycomputer.com/rpl.html 77 | 78 | 3. All tags loading needs to be updated:: 79 | 80 | {% load uni_form_tags %} 81 | {% load crispy_forms_tags %} 82 | 83 | Using rpl:: 84 | 85 | rpl -R "{% load uni_form_tags %}" "{% load crispy_forms_tags %}" . 86 | 87 | 4. Until version 1.2.0 former tags and filters names worked without changing them, current versions will force updating your filters and tags:: 88 | 89 | |as_uni_form -----> |crispy 90 | {% uni_form %} ---> {% crispy %} 91 | |as_uni_errors ---> |as_crispy_errors 92 | |as_uni_field ----> |as_crispy_field 93 | 94 | Using rpl:: 95 | 96 | rpl -R "|as_uni_form" "|crispy" . 97 | rpl -R "{% uni_form" "{% crispy" . 98 | rpl -R "|as_uni_errors" "|as_crispy_errors" . 99 | rpl -R "|as_uni_field" "|as_crispy_field" . 100 | 101 | There is one filter that has been turned into a tag for extra layout power, so former filter name will not work. You will only need to update this if you have custom or overriden templates in your project:: 102 | 103 | field|with_class ------> {% crispy_field field %} 104 | 105 | 5. If you have ``UNIFORM_FAIL_SILENTLY`` setting variable defined, you have to rename it to ``CRISPY_FAIL_SILENTLY``. 106 | 107 | 6. crispy-forms renders your layouts strictly, exactly the fields mentioned, if you want crispy-forms to work the same way as django-uni-form you can set new ``FormHelper`` attribute ``render_unmentioned_fields`` to ``True``. 108 | 109 | 110 | Uni-form static files 111 | ~~~~~~~~~~~~~~~~~~~~~ 112 | 113 | `Uni-form`_ files are composed of two CSS files and one JS library based on jQuery. Uni-form's JS library provides some nice interactions, but you will need to link a copy of jQuery. Preferably you should use a `version hosted`_ on Google's CDN servers since the user's browser might already have it cached. 114 | 115 | .. _`version hosted`: http://scriptsrc.net/. 116 | 117 | For linking `Uni-form`_ static files add the proper lines to your HTML head. This is an example on how to do it if you are using ``STATIC_URL``:: 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | Activate uni-form.jquery 126 | ~~~~~~~~~~~~~~~~~~~~~~~~ 127 | 128 | If you link `Uni-form`_ JS library do not forget to activate given forms:: 129 | 130 | 135 | 136 | 137 | .. _Django: http://djangoproject.com 138 | .. _`Uni-form`: http://sprawsm.com/uni-form 139 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Setup 6 | ===== 7 | 8 | Fork on github 9 | -------------- 10 | 11 | Before you do anything else, login/signup on Github.com and fork django-crispy-forms from https://github.com/maraujop/django-crispy-forms. 12 | 13 | Clone your fork locally 14 | ----------------------- 15 | 16 | If you have git-scm installed, you now clone your git repo using the following command-line argument where is your account name on github:: 17 | 18 | git clone git@github.com//django-crispy-forms.git 19 | 20 | Setting up topic branches and generating pull requests 21 | ====================================================== 22 | 23 | While it's handy to provide useful code snippets in an issue, it is better for 24 | you as a developer to submit pull requests. By submitting pull request your 25 | contribution to django-crispy-forms will be recorded by Github. 26 | 27 | In git it is best to isolate each topic or feature into a "topic branch". While 28 | individual commits allow you control over how small individual changes are made 29 | to the code, branches are a great way to group a set of commits all related to 30 | one feature together, or to isolate different efforts when you might be working 31 | on multiple topics at the same time. 32 | 33 | While it takes some experience to get the right feel about how to break up 34 | commits, a topic branch **must** be limited in scope to a single ``issue`` as 35 | submitted to an issue tracker. 36 | 37 | Also since github pegs and syncs a pull request to a specific branch, it is the 38 | **ONLY** way that you can submit more than one fix at a time. If you submit 39 | a pull from your master branch, you can't make any more commits to your master 40 | without those getting added to the pull. 41 | 42 | To create a topic branch, its easiest to use the convenient ``-b`` argument to ``git 43 | checkout``:: 44 | 45 | git checkout -b fix-broken-thing 46 | Switched to a new branch 'fix-broken-thing' 47 | 48 | You should use a verbose enough name for your branch so it is clear what it is 49 | about. Now you can commit your changes and regularly merge in the upstream 50 | master as described below. 51 | 52 | When you are ready to generate a pull request, either for preliminary review, 53 | or for consideration of merging into the project you must first push your local 54 | topic branch back up to github:: 55 | 56 | git push origin fix-broken-thing 57 | 58 | Now when you go to your fork on github, you will see this branch listed under 59 | the "Source" tab where it says "Switch Branches". Go ahead and select your 60 | topic branch from this list, and then click the "Pull request" button. 61 | 62 | Here you can add a comment about your branch. If this in response to 63 | a submitted issue, it is good to put a link to that issue in this initial 64 | comment. The repo managers will be notified of your pull request and it will 65 | be reviewed (see below for best practices). Note that you can continue to add 66 | commits to your topic branch (and push them up to github) either if you see 67 | something that needs changing, or in response to a reviewer's comments. If 68 | a reviewer asks for changes, you do not need to close the pull and reissue it 69 | after making changes. Just make the changes locally, push them to github, then 70 | add a comment to the discussion section of the pull request. 71 | 72 | Pull upstream changes into your fork regularly 73 | ================================================== 74 | 75 | django-crispy-forms is worked on by a lot of people. It is therefore critical that you pull upstream changes from trunk into your fork on a regular basis. Nothing is worse than putting in days of hard work into a pull request only to have it rejected because it has diverged too far from trunk. 76 | 77 | To pull in upstream changes:: 78 | 79 | git remote add trunk git://github.com/maraujop/django-crispy-forms.git 80 | git fetch trunk 81 | 82 | Check the log to be sure that you actually want the changes, before merging:: 83 | 84 | git log ..django-crispy-forms/master 85 | 86 | Then merge the changes that you fetched:: 87 | 88 | git merge django-crispy-forms/master 89 | 90 | For more info, see http://help.github.com/fork-a-repo/ 91 | 92 | How to get your Pull Request accepted 93 | ===================================== 94 | 95 | We want your submission. But we also want to provide a stable experience for our users and the community. Follow these rules and you should succeed without a problem! 96 | 97 | Run the tests! 98 | -------------- 99 | 100 | Before you submit a pull request, please run the entire django-crispy-forms test suite via:: 101 | 102 | cd crispy_forms/tests 103 | ./runtests.py 104 | 105 | The first thing the core committers will do is run this command. Any pull request that fails this test suite will be **rejected**. 106 | 107 | It's always good to add tests! 108 | ------------------------------ 109 | 110 | We've learned the hard way that code without tests is undependable. If your pull request comes with tests, it's got a greater chance to be included. Otherwise the lead will ask you to code them or will help you doing so. 111 | 112 | We use the Django Test framework (based on unittest). 113 | 114 | Also, keep your tests as simple as possible. Complex tests end up requiring their own tests. We would rather see duplicated assertions across test methods then cunning utility methods that magically determine which assertions are needed at a particular stage. Remember: `Explicit is better than implicit`. 115 | 116 | Don't mix code changes with whitespace cleanup 117 | ---------------------------------------------- 118 | 119 | If you change two lines of code and correct 200 lines of whitespace issues in a file the diff on that pull request is functionally unreadable and will be **rejected**. Whitespace cleanups need to be in their own pull request. 120 | 121 | Keep your pull requests limited to a single issue 122 | -------------------------------------------------- 123 | 124 | django-crispy-forms pull requests should be as small/atomic as possible. Large, wide-sweeping changes in a pull request will be **rejected**, with comments to isolate the specific code in your pull request. Some examples: 125 | 126 | #. If you are fixing a bug in one helper class don't '*cleanup*' unrelated helpers. That cleanup belongs in another pull request. 127 | #. Changing permissions on a file should be in its own pull request with explicit reasons why. 128 | 129 | Keep your code simple! 130 | ---------------------- 131 | 132 | Memorize the Zen of Python:: 133 | 134 | >>> python -c 'import this' 135 | 136 | Please keep your code as clean and straightforward as possible. When we see more than one or two functions/methods starting with `_my_special_function` or things like `__builtins__.object = str` we start to get worried. Rather than try and figure out your brilliant work we'll just **reject** it and send along a request for simplification. 137 | 138 | Furthermore, the pixel shortage is over. We want to see: 139 | 140 | * `helper` instead of `hpr` 141 | * `django-crispy-forms` instead of `dcf` 142 | * `my_function_that_does_things` instead of `mftdt` 143 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-crispy-forms documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Mar 8 22:42:02 2010. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('.')) 20 | sys.path.insert(0, os.path.abspath('..')) 21 | sys.path.insert(0, os.path.abspath('../crispy_forms')) 22 | sys.path.insert(0, os.path.abspath('../crispy_forms/templatetags')) 23 | sys.path.append(os.path.abspath('_themes')) 24 | 25 | from django.conf import settings 26 | settings.configure() 27 | 28 | 29 | # -- General configuration ----------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be extensions 32 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 33 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] 34 | 35 | # Add any paths that contain templates here, relative to this directory. 36 | templates_path = ['_templates'] 37 | 38 | # The suffix of source filenames. 39 | source_suffix = '.rst' 40 | 41 | # The encoding of source files. 42 | #source_encoding = 'utf-8' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'django-crispy-forms' 49 | copyright = u'2009-2013, Miguel Araujo and Daniel Greenfeld' 50 | 51 | # The version info for the project you're documenting, acts as replacement for 52 | # |version| and |release|, also used in various other places throughout the 53 | # built documents. 54 | # 55 | # The short X.Y version. 56 | version = '1.0' 57 | # The full version, including alpha/beta/rc tags. 58 | release = '1.0.0' 59 | 60 | # The language for content autogenerated by Sphinx. Refer to documentation 61 | # for a list of supported languages. 62 | #language = None 63 | 64 | # There are two options for replacing |today|: either, you set today to some 65 | # non-false value, then it is used: 66 | #today = '' 67 | # Else, today_fmt is used as the format for a strftime call. 68 | #today_fmt = '%B %d, %Y' 69 | 70 | # List of documents that shouldn't be included in the build. 71 | #unused_docs = [] 72 | 73 | # List of directories, relative to source directory, that shouldn't be searched 74 | # for source files. 75 | exclude_trees = ['_build'] 76 | 77 | # The reST default role (used for this markup: `text`) to use for all documents. 78 | #default_role = None 79 | 80 | # If true, '()' will be appended to :func: etc. cross-reference text. 81 | #add_function_parentheses = True 82 | 83 | # If true, the current module name will be prepended to all description 84 | # unit titles (such as .. function::). 85 | #add_module_names = True 86 | 87 | # If true, sectionauthor and moduleauthor directives will be shown in the 88 | # output. They are ignored by default. 89 | #show_authors = False 90 | 91 | # The name of the Pygments (syntax highlighting) style to use. 92 | pygments_style = 'sphinx' 93 | 94 | # A list of ignored prefixes for module index sorting. 95 | #modindex_common_prefix = [] 96 | 97 | 98 | # -- Options for HTML output --------------------------------------------------- 99 | 100 | # The theme to use for HTML and HTML Help pages. Major themes that come with 101 | # Sphinx are currently 'default' and 'sphinxdoc'. 102 | html_theme = 'kr' 103 | 104 | # Theme options are theme-specific and customize the look and feel of a theme 105 | # further. For a list of options available for each theme, see the 106 | # documentation. 107 | #html_theme_options = {} 108 | 109 | # Add any paths that contain custom themes here, relative to this directory. 110 | html_theme_path = ['_themes'] 111 | 112 | # The name for this set of Sphinx documents. If None, it defaults to 113 | # " v documentation". 114 | #html_title = None 115 | 116 | # A shorter title for the navigation bar. Default is the same as html_title. 117 | #html_short_title = None 118 | 119 | # The name of an image file (relative to this directory) to place at the top 120 | # of the sidebar. 121 | #html_logo = None 122 | 123 | # The name of an image file (within the static path) to use as favicon of the 124 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 125 | # pixels large. 126 | #html_favicon = None 127 | 128 | # Add any paths that contain custom static files (such as style sheets) here, 129 | # relative to this directory. They are copied after the builtin static files, 130 | # so a file named "default.css" will overwrite the builtin "default.css". 131 | html_static_path = ['_static'] 132 | 133 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 134 | # using the given strftime format. 135 | #html_last_updated_fmt = '%b %d, %Y' 136 | 137 | # If true, SmartyPants will be used to convert quotes and dashes to 138 | # typographically correct entities. 139 | #html_use_smartypants = True 140 | 141 | # Custom sidebar templates, maps document names to template names. 142 | html_sidebars = { 143 | 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], 144 | '**': ['sidebarintro.html', 'localtoc.html', 'relations.html', 145 | 'sourcelink.html', 'searchbox.html'] 146 | } 147 | 148 | # Additional templates that should be rendered to pages, maps page names to 149 | # template names. 150 | #html_additional_pages = {} 151 | 152 | # If false, no module index is generated. 153 | #html_use_modindex = True 154 | 155 | # If false, no index is generated. 156 | #html_use_index = True 157 | 158 | # If true, the index is split into individual pages for each letter. 159 | #html_split_index = False 160 | 161 | # If true, links to the reST sources are added to the pages. 162 | #html_show_sourcelink = True 163 | 164 | # If true, an OpenSearch description file will be output, and all pages will 165 | # contain a tag referring to it. The value of this option must be the 166 | # base URL from which the finished HTML is served. 167 | #html_use_opensearch = '' 168 | 169 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 170 | #html_file_suffix = '' 171 | 172 | # Output file base name for HTML help builder. 173 | htmlhelp_basename = 'django-crispy-formdoc' 174 | 175 | 176 | # -- Options for LaTeX output -------------------------------------------------- 177 | 178 | # The paper size ('letter' or 'a4'). 179 | #latex_paper_size = 'letter' 180 | 181 | # The font size ('10pt', '11pt' or '12pt'). 182 | #latex_font_size = '10pt' 183 | 184 | # Grouping the document tree into LaTeX files. List of tuples 185 | # (source start file, target name, title, author, documentclass [howto/manual]). 186 | latex_documents = [ 187 | ('index', 'django-crispy-forms.tex', u'django-crispy-forms Documentation', 188 | u'Miguel Araujo', 'manual'), 189 | ] 190 | 191 | # The name of an image file (relative to this directory) to place at the top of 192 | # the title page. 193 | #latex_logo = None 194 | 195 | # For "manual" documents, if this is true, then toplevel headings are parts, 196 | # not chapters. 197 | #latex_use_parts = False 198 | 199 | # Additional stuff for the LaTeX preamble. 200 | #latex_preamble = '' 201 | 202 | # Documents to append as an appendix to all manuals. 203 | #latex_appendices = [] 204 | 205 | # If false, no module index is generated. 206 | #latex_use_modindex = True 207 | -------------------------------------------------------------------------------- /crispy_forms/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | import inspect 3 | import logging 4 | import sys 5 | 6 | from django.conf import settings 7 | from django.forms.forms import BoundField 8 | from django.template import Context 9 | from django.template.loader import get_template 10 | from django.utils.html import conditional_escape 11 | from django.utils.functional import memoize 12 | 13 | from .base import KeepContext 14 | from .compatibility import text_type, PY2 15 | 16 | # Global field template, default template used for rendering a field. 17 | 18 | TEMPLATE_PACK = getattr(settings, 'CRISPY_TEMPLATE_PACK', 'bootstrap') 19 | 20 | 21 | # By memoizeing we avoid loading the template every time render_field 22 | # is called without a template 23 | def default_field_template(template_pack=TEMPLATE_PACK): 24 | return get_template("%s/field.html" % template_pack) 25 | default_field_template = memoize(default_field_template, {}, 1) 26 | 27 | 28 | def render_field( 29 | field, form, form_style, context, template=None, labelclass=None, 30 | layout_object=None, attrs=None, template_pack=TEMPLATE_PACK, 31 | extra_context=None, **kwargs 32 | ): 33 | """ 34 | Renders a django-crispy-forms field 35 | 36 | :param field: Can be a string or a Layout object like `Row`. If it's a layout 37 | object, we call its render method, otherwise we instantiate a BoundField 38 | and render it using default template 'CRISPY_TEMPLATE_PACK/field.html' 39 | The field is added to a list that the form holds called `rendered_fields` 40 | to avoid double rendering fields. 41 | :param form: The form/formset to which that field belongs to. 42 | :param form_style: A way to pass style name to the CSS framework used. 43 | :template: Template used for rendering the field. 44 | :layout_object: If passed, it points to the Layout object that is being rendered. 45 | We use it to store its bound fields in a list called `layout_object.bound_fields` 46 | :attrs: Attributes for the field's widget 47 | :template_pack: Name of the template pack to be used for rendering `field` 48 | :extra_context: Dictionary to be added to context, added variables by the layout object 49 | """ 50 | added_keys = [] if extra_context is None else extra_context.keys() 51 | with KeepContext(context, added_keys): 52 | if field is None: 53 | return '' 54 | 55 | FAIL_SILENTLY = getattr(settings, 'CRISPY_FAIL_SILENTLY', True) 56 | 57 | if hasattr(field, 'render'): 58 | return field.render( 59 | form, form_style, context, template_pack=template_pack, 60 | ) 61 | else: 62 | # In Python 2 form field names cannot contain unicode characters without ASCII mapping 63 | if PY2: 64 | # This allows fields to be unicode strings, always they don't use non ASCII 65 | try: 66 | if isinstance(field, text_type): 67 | field = field.encode('ascii').decode() 68 | # If `field` is not unicode then we turn it into a unicode string, otherwise doing 69 | # str(field) would give no error and the field would not be resolved, causing confusion 70 | else: 71 | field = text_type(field) 72 | 73 | except (UnicodeEncodeError, UnicodeDecodeError): 74 | raise Exception("Field '%s' is using forbidden unicode characters" % field) 75 | 76 | try: 77 | # Injecting HTML attributes into field's widget, Django handles rendering these 78 | field_instance = form.fields[field] 79 | if attrs is not None: 80 | widgets = getattr(field_instance.widget, 'widgets', [field_instance.widget]) 81 | 82 | # We use attrs as a dictionary later, so here we make a copy 83 | list_attrs = attrs 84 | if isinstance(attrs, dict): 85 | list_attrs = [attrs] * len(widgets) 86 | 87 | for index, (widget, attr) in enumerate(zip(widgets, list_attrs)): 88 | if hasattr(field_instance.widget, 'widgets'): 89 | if 'type' in attr and attr['type'] == "hidden": 90 | field_instance.widget.widgets[index].is_hidden = True 91 | field_instance.widget.widgets[index] = field_instance.hidden_widget() 92 | 93 | field_instance.widget.widgets[index].attrs.update(attr) 94 | else: 95 | if 'type' in attr and attr['type'] == "hidden": 96 | field_instance.widget.is_hidden = True 97 | field_instance.widget = field_instance.hidden_widget() 98 | 99 | field_instance.widget.attrs.update(attr) 100 | 101 | except KeyError: 102 | if not FAIL_SILENTLY: 103 | raise Exception("Could not resolve form field '%s'." % field) 104 | else: 105 | field_instance = None 106 | logging.warning("Could not resolve form field '%s'." % field, exc_info=sys.exc_info()) 107 | 108 | if hasattr(form, 'rendered_fields'): 109 | if not field in form.rendered_fields: 110 | form.rendered_fields.add(field) 111 | else: 112 | if not FAIL_SILENTLY: 113 | raise Exception("A field should only be rendered once: %s" % field) 114 | else: 115 | logging.warning("A field should only be rendered once: %s" % field, exc_info=sys.exc_info()) 116 | 117 | if field_instance is None: 118 | html = '' 119 | else: 120 | bound_field = BoundField(form, field_instance, field) 121 | 122 | if template is None: 123 | if form.crispy_field_template is None: 124 | template = default_field_template(template_pack) 125 | else: # FormHelper.field_template set 126 | template = get_template(form.crispy_field_template) 127 | else: 128 | template = get_template(template) 129 | 130 | # We save the Layout object's bound fields in the layout object's `bound_fields` list 131 | if layout_object is not None: 132 | if hasattr(layout_object, 'bound_fields') and isinstance(layout_object.bound_fields, list): 133 | layout_object.bound_fields.append(bound_field) 134 | else: 135 | layout_object.bound_fields = [bound_field] 136 | 137 | context.update({ 138 | 'field': bound_field, 139 | 'labelclass': labelclass, 140 | 'flat_attrs': flatatt(attrs if isinstance(attrs, dict) else {}), 141 | }) 142 | if extra_context is not None: 143 | context.update(extra_context) 144 | html = template.render(context) 145 | 146 | return html 147 | 148 | 149 | def flatatt(attrs): 150 | """ 151 | Taken from django.core.utils 152 | Convert a dictionary of attributes to a single string. 153 | The returned string will contain a leading space followed by key="value", 154 | XML-style pairs. It is assumed that the keys do not need to be XML-escaped. 155 | If the passed dictionary is empty, then return an empty string. 156 | """ 157 | return u''.join([u' %s="%s"' % (k.replace('_', '-'), conditional_escape(v)) for k, v in attrs.items()]) 158 | 159 | 160 | def render_crispy_form(form, helper=None, context=None): 161 | """ 162 | Renders a form and returns its HTML output. 163 | 164 | This function wraps the template logic in a function easy to use in a Django view. 165 | """ 166 | from crispy_forms.templatetags.crispy_forms_tags import CrispyFormNode 167 | 168 | if helper is not None: 169 | node = CrispyFormNode('form', 'helper') 170 | else: 171 | node = CrispyFormNode('form', None) 172 | 173 | node_context = Context(context) 174 | node_context.update({ 175 | 'form': form, 176 | 'helper': helper 177 | }) 178 | 179 | return node.render(node_context) 180 | -------------------------------------------------------------------------------- /docs/_themes/kr/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | {% set page_width = '940px' %} 10 | {% set sidebar_width = '220px' %} 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro'; 18 | font-size: 17px; 19 | background-color: white; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | width: {{ page_width }}; 27 | margin: 30px auto 0 auto; 28 | } 29 | 30 | div.documentwrapper { 31 | float: left; 32 | width: 100%; 33 | } 34 | 35 | div.bodywrapper { 36 | margin: 0 0 0 {{ sidebar_width }}; 37 | } 38 | 39 | div.sphinxsidebar { 40 | width: {{ sidebar_width }}; 41 | } 42 | 43 | hr { 44 | border: 1px solid #B1B4B6; 45 | } 46 | 47 | div.body { 48 | background-color: #ffffff; 49 | color: #3E4349; 50 | padding: 0 30px 0 30px; 51 | } 52 | 53 | img.floatingflask { 54 | padding: 0 0 10px 10px; 55 | float: right; 56 | } 57 | 58 | div.footer { 59 | width: {{ page_width }}; 60 | margin: 20px auto 30px auto; 61 | font-size: 14px; 62 | color: #888; 63 | text-align: right; 64 | } 65 | 66 | div.footer a { 67 | color: #888; 68 | } 69 | 70 | div.related { 71 | display: none; 72 | } 73 | 74 | div.sphinxsidebar a { 75 | color: #444; 76 | text-decoration: none; 77 | border-bottom: 1px dotted #999; 78 | } 79 | 80 | div.sphinxsidebar a:hover { 81 | border-bottom: 1px solid #999; 82 | } 83 | 84 | div.sphinxsidebar { 85 | font-size: 14px; 86 | line-height: 1.5; 87 | } 88 | 89 | div.sphinxsidebarwrapper { 90 | padding: 0px 10px; 91 | } 92 | 93 | div.sphinxsidebarwrapper p.logo { 94 | padding: 0 0 20px 0; 95 | margin: 0; 96 | text-align: center; 97 | } 98 | 99 | div.sphinxsidebar h3, 100 | div.sphinxsidebar h4 { 101 | font-family: 'Garamond', 'Georgia', serif; 102 | color: #555; 103 | font-size: 24px; 104 | font-weight: normal; 105 | margin: 0 0 5px 0; 106 | padding: 0; 107 | } 108 | 109 | div.sphinxsidebar h4 { 110 | font-size: 20px; 111 | } 112 | 113 | div.sphinxsidebar h3 a { 114 | color: #444; 115 | } 116 | 117 | div.sphinxsidebar p.logo a, 118 | div.sphinxsidebar h3 a, 119 | div.sphinxsidebar p.logo a:hover, 120 | div.sphinxsidebar h3 a:hover { 121 | border: none; 122 | } 123 | 124 | div.sphinxsidebar p { 125 | color: #555; 126 | margin: 10px 0; 127 | } 128 | 129 | div.sphinxsidebar ul { 130 | margin: 10px 0; 131 | padding: 0; 132 | color: #000; 133 | } 134 | 135 | div.sphinxsidebar input[type="text"] { 136 | width: 160px!important; 137 | } 138 | div.sphinxsidebar input { 139 | border: 1px solid #ccc; 140 | font-family: 'Georgia', serif; 141 | font-size: 1em; 142 | } 143 | 144 | /* -- body styles ----------------------------------------------------------- */ 145 | 146 | a { 147 | color: #004B6B; 148 | text-decoration: underline; 149 | } 150 | 151 | a:hover { 152 | color: #6D4100; 153 | text-decoration: underline; 154 | } 155 | 156 | div.body h1, 157 | div.body h2, 158 | div.body h3, 159 | div.body h4, 160 | div.body h5, 161 | div.body h6 { 162 | font-family: 'Garamond', 'Georgia', serif; 163 | font-weight: normal; 164 | margin: 30px 0px 10px 0px; 165 | padding: 0; 166 | } 167 | 168 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 169 | div.body h2 { font-size: 180%; } 170 | div.body h3 { font-size: 150%; } 171 | div.body h4 { font-size: 130%; } 172 | div.body h5 { font-size: 100%; } 173 | div.body h6 { font-size: 100%; } 174 | 175 | a.headerlink { 176 | color: #ddd; 177 | padding: 0 4px; 178 | text-decoration: none; 179 | } 180 | 181 | a.headerlink:hover { 182 | color: #444; 183 | background: #eaeaea; 184 | } 185 | 186 | div.body p, div.body dd, div.body li { 187 | line-height: 1.4em; 188 | } 189 | 190 | div.admonition { 191 | background: #fafafa; 192 | margin: 20px -30px; 193 | padding: 10px 30px; 194 | border-top: 1px solid #ccc; 195 | border-bottom: 1px solid #ccc; 196 | } 197 | 198 | div.admonition tt.xref, div.admonition a tt { 199 | border-bottom: 1px solid #fafafa; 200 | } 201 | 202 | dd div.admonition { 203 | margin-left: -60px; 204 | padding-left: 60px; 205 | } 206 | 207 | div.admonition p.admonition-title { 208 | font-family: 'Garamond', 'Georgia', serif; 209 | font-weight: normal; 210 | font-size: 24px; 211 | margin: 0 0 10px 0; 212 | padding: 0; 213 | line-height: 1; 214 | } 215 | 216 | div.admonition p.last { 217 | margin-bottom: 0; 218 | } 219 | 220 | div.highlight { 221 | background-color: white; 222 | } 223 | 224 | dt:target, .highlight { 225 | background: #FAF3E8; 226 | } 227 | 228 | div.note { 229 | background-color: #eee; 230 | border: 1px solid #ccc; 231 | } 232 | 233 | div.seealso { 234 | background-color: #ffc; 235 | border: 1px solid #ff6; 236 | } 237 | 238 | div.topic { 239 | background-color: #eee; 240 | } 241 | 242 | p.admonition-title { 243 | display: inline; 244 | } 245 | 246 | p.admonition-title:after { 247 | content: ":"; 248 | } 249 | 250 | pre, tt { 251 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 252 | font-size: 0.9em; 253 | } 254 | 255 | img.screenshot { 256 | } 257 | 258 | tt.descname, tt.descclassname { 259 | font-size: 0.95em; 260 | } 261 | 262 | tt.descname { 263 | padding-right: 0.08em; 264 | } 265 | 266 | img.screenshot { 267 | -moz-box-shadow: 2px 2px 4px #eee; 268 | -webkit-box-shadow: 2px 2px 4px #eee; 269 | box-shadow: 2px 2px 4px #eee; 270 | } 271 | 272 | table.docutils { 273 | border: 1px solid #888; 274 | -moz-box-shadow: 2px 2px 4px #eee; 275 | -webkit-box-shadow: 2px 2px 4px #eee; 276 | box-shadow: 2px 2px 4px #eee; 277 | } 278 | 279 | table.docutils td, table.docutils th { 280 | border: 1px solid #888; 281 | padding: 0.25em 0.7em; 282 | } 283 | 284 | table.field-list, table.footnote { 285 | border: none; 286 | -moz-box-shadow: none; 287 | -webkit-box-shadow: none; 288 | box-shadow: none; 289 | } 290 | 291 | table.footnote { 292 | margin: 15px 0; 293 | width: 100%; 294 | border: 1px solid #eee; 295 | background: #fdfdfd; 296 | font-size: 0.9em; 297 | } 298 | 299 | table.footnote + table.footnote { 300 | margin-top: -15px; 301 | border-top: none; 302 | } 303 | 304 | table.field-list th { 305 | padding: 0 0.8em 0 0; 306 | } 307 | 308 | table.field-list td { 309 | padding: 0; 310 | } 311 | 312 | table.footnote td.label { 313 | width: 0px; 314 | padding: 0.3em 0 0.3em 0.5em; 315 | } 316 | 317 | table.footnote td { 318 | padding: 0.3em 0.5em; 319 | } 320 | 321 | dl { 322 | margin: 0; 323 | padding: 0; 324 | } 325 | 326 | dl dd { 327 | margin-left: 30px; 328 | } 329 | 330 | blockquote { 331 | margin: 0 0 0 30px; 332 | padding: 0; 333 | } 334 | 335 | ul, ol { 336 | margin: 10px 0 10px 30px; 337 | padding: 0; 338 | } 339 | 340 | pre { 341 | background: #eee; 342 | padding: 7px 30px; 343 | margin: 15px -30px; 344 | line-height: 1.3em; 345 | } 346 | 347 | dl pre, blockquote pre, li pre { 348 | margin-left: -60px; 349 | padding-left: 60px; 350 | } 351 | 352 | dl dl pre { 353 | margin-left: -90px; 354 | padding-left: 90px; 355 | } 356 | 357 | tt { 358 | background-color: #ecf0f3; 359 | color: #222; 360 | /* padding: 1px 2px; */ 361 | } 362 | 363 | tt.xref, a tt { 364 | background-color: #FBFBFB; 365 | border-bottom: 1px solid white; 366 | } 367 | 368 | a.reference { 369 | text-decoration: none; 370 | border-bottom: 1px dotted #004B6B; 371 | } 372 | 373 | a.reference:hover { 374 | border-bottom: 1px solid #6D4100; 375 | } 376 | 377 | a.footnote-reference { 378 | text-decoration: none; 379 | font-size: 0.7em; 380 | vertical-align: top; 381 | border-bottom: 1px dotted #004B6B; 382 | } 383 | 384 | a.footnote-reference:hover { 385 | border-bottom: 1px solid #6D4100; 386 | } 387 | 388 | a:hover tt { 389 | background: #EEE; 390 | } 391 | 392 | 393 | /* scrollbars */ 394 | 395 | ::-webkit-scrollbar { 396 | width: 6px; 397 | height: 6px; 398 | } 399 | 400 | ::-webkit-scrollbar-button:start:decrement, 401 | ::-webkit-scrollbar-button:end:increment { 402 | display: block; 403 | height: 10px; 404 | } 405 | 406 | ::-webkit-scrollbar-button:vertical:increment { 407 | background-color: #fff; 408 | } 409 | 410 | ::-webkit-scrollbar-track-piece { 411 | background-color: #eee; 412 | -webkit-border-radius: 3px; 413 | } 414 | 415 | ::-webkit-scrollbar-thumb:vertical { 416 | height: 50px; 417 | background-color: #ccc; 418 | -webkit-border-radius: 3px; 419 | } 420 | 421 | ::-webkit-scrollbar-thumb:horizontal { 422 | width: 50px; 423 | background-color: #ccc; 424 | -webkit-border-radius: 3px; 425 | } 426 | 427 | /* misc. */ 428 | 429 | .revsys-inline { 430 | display: none!important; 431 | } 432 | 433 | a.toc-backref { 434 | color: #3E4349; 435 | text-decoration: none; 436 | } 437 | -------------------------------------------------------------------------------- /docs/form_helper.rst: -------------------------------------------------------------------------------- 1 | .. _`form helpers`: 2 | 3 | ========== 4 | FormHelper 5 | ========== 6 | 7 | What is a ``FormHelper`` and how to use it, is throughly explained in a previous section :ref:`crispy tag forms`. 8 | 9 | 10 | .. _`helper form attached`: 11 | 12 | FormHelper with a form attached (Default layout) 13 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | 15 | Since version 1.2.0 ``FormHelper`` optionally can be passed an instance of a form. You would do it this way:: 16 | 17 | class ExampleForm(forms.Form): 18 | def __init__(self, *args, **kwargs): 19 | super(ExampleForm, self).__init__(*args, **kwargs) 20 | self.helper = FormHelper(self) 21 | 22 | 23 | When you do this crispy-forms builds a default layout using ``form.fields`` for you, so you don't have to manually list them all if your form is huge. If you later need to manipulate some bits of a big layout, using dynamic layouts is highly recommended, check :ref:`dynamic layouts`. 24 | 25 | Also, now the helper is able to cross match the layout with the form instance, being able to search by widget type if you are using dynamic API. 26 | 27 | .. _`helper attributes`: 28 | 29 | Helper attributes you can set 30 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 | 32 | **template_pack** 33 | Allows you to set what template pack you want to use at ``FormHelper`` level. This is useful for example when a website needs to render different styling forms for different use cases, like desktop website vs smarphone website. 34 | 35 | **template** 36 | When set allows you to render a form/formset using a custom template. Default template is at ``{{ TEMPLATE_PACK }}/[whole_uni_form.html|whole_uni_formset.html]`` 37 | 38 | **field_template** 39 | When set allows you to render a form/formset using a custom field template. Default template is at ``{{ TEMPLATE_PACK }}/field.html``. Beware that this is only effective when setting a ``FormHelper.layout``. 40 | 41 | **form_method = 'POST'** 42 | Specifies form method attribute. You can see it to ‘POST’ or ‘GET’. Defaults to ‘POST’ 43 | 44 | **form_action** 45 | Applied to the form action attribute. Can be a named url in your URLconf that can be executed via the {% url %} template tag. Example: ‘show_my_profile’. In your URLconf you could have something like:: 46 | 47 | url(r'^show/profile/$', 'show_my_profile_view', name='show_my_profile') 48 | 49 | You can also point it to a URL ‘/whatever/blabla/’. 50 | 51 | Sometimes you may want to add arguments to the URL, for that you will have to do in your view:: 52 | 53 | from django.core.urlresolvers import reverse 54 | form.helper.form_action = reverse('url_name', args=[event.id]) 55 | form.helper.form_action = reverse('url_name', kwargs={'book_id': book.id}) 56 | 57 | **attrs** 58 | Added in 1.2.0, a dictionary to set any kind of form attributes. Underscores in keys are translated into hyphens. The recommended way when you need to set several form attributes in order to keep your helper tidy:: 59 | 60 | ``{'id': 'form-id', 'data_id': '/whatever'}`` 61 |
    62 | 63 | **form_id** 64 | Specifies form DOM id attribute. If no id provided then no id attribute is created on the form. 65 | 66 | **form_class** 67 | String containing separated CSS clases to be applied to form class attribute. The form will always have by default ‘uniForm’ class. 68 | 69 | **form_tag = True** 70 | It specifies if ``
    `` tags should be rendered when using a Layout. If set to ``False`` it renders the form without the ``
    `` tags. Defaults to ``True``. 71 | 72 | **disable_csrf = False** 73 | Disable CSRF token, when done, crispy-forms won't use ``{% crsf_token %}`` tag. This is useful when rendering several forms using ``{% crispy %}`` tag and ``form_tag = False`` csrf_token gets rendered several times. 74 | 75 | **form_error_title** 76 | If you are rendering a form using ``{% crispy %}`` tag and it has ``non_field_errors`` to display, they are rendered in a div. You can set the title of the div with this attribute. Example: “Form Errors”. 77 | 78 | **formset_error_title** 79 | If you are rendering a formset using ``{% crispy %}`` tag and it has ``non_form_errors`` to display, they are rendered in a div. You can set the title of the div with this attribute. Example: “Formset Errors”. 80 | 81 | **form_style = 'default'** 82 | Helper attribute for uni_form template pack. Uni-form has two different form styles built-in. You can choose which one to use, setting this variable to ``default`` or ``inline``. 83 | 84 | **form_show_errors = True** 85 | Default set to ``True``. It decides wether to render or not form errors. If set to ``False``, form.errors will not be visible even if they happen. You have to manually render them customizing your template. This allows you to customize error output. 86 | 87 | **render_unmentioned_fields = False** 88 | By default django-crispy-forms renders the layout specified if it exists strictly, which means it only renders what the layout mentions, unless your form has ``Meta.fields`` and ``Meta.exclude`` defined, in that case it uses them. If you want to render unmentioned fields (all form fields), for example if you are worried about forgetting mentioning them you have to set this property to ``True``. It defaults to ``False``. 89 | 90 | **render_hidden_fields = False** 91 | By default django-crispy-forms renders the layout specified if it exists strictly. Sometimes you might be interested in rendering all form's hidden fields no matter if they are mentioned or not. It defaults to ``False``. 92 | 93 | **render_required_fields = False** 94 | By default django-crispy-forms renders the layout specified if it exists strictly. Sometimes you might be interested in rendering all form's hidden required fields no matter if they are mentioned or not. It defaults to ``False``. 95 | 96 | 97 | Bootstrap Helper attributes 98 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 99 | 100 | There are currently some helper attributes that only have functionality for a specific template pack. This doesn't necessarily mean that they won't be supported for other template packs in the future. 101 | 102 | **help_text_inline = False** 103 | Sets whether help texts should be rendered inline or block. If set to ``True`` help texts will be rendered ``help-inline`` class, otherwise using ``help-block``. By default text messages are rendered in block mode. 104 | 105 | **error_text_inline = True** 106 | Sets whether to render error messages inline or block. If set to ``True`` errors will be rendered using ``help-inline`` class, otherwise using ``help-block``. By default error messages are rendered in inline mode. 107 | 108 | **html5_required = False** 109 | When set to ``True`` all required fields inputs will be rendered with HTML5 ``required=required`` attribute. 110 | 111 | **form_show_labels = True** 112 | Default set to ``True``. It decides wether to render or not form's fields labels. 113 | 114 | 115 | Bootstrap 3 Helper attributes 116 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 117 | 118 | All previous, ``bootstrap`` (version 2) attributes are also settable in bootstrap 3 template pack ``FormHelpers``. Here are listed the ones, that are only availble in ``bootstrap3`` template pack: 119 | 120 | **label_class = ''** 121 | Default set to ``''``. This class will be applied to every label, this is very useful to do horizontal forms. Set it for example like this ``label_class = col-lg-2``. 122 | 123 | **field_class = ''** 124 | Default set to ``''``. This class will be applied to every div ``controls`` wrapping a field. This is useful for doing horizontal forms. Set it for example like this ``field_class = col-lg-8``. 125 | 126 | Custom Helper attributes 127 | ~~~~~~~~~~~~~~~~~~~~~~~~ 128 | 129 | Maybe you would like that ``FormHelper`` did some extra thing that is not currently supported or maybe you have a very specific use case. The good part is that you can add extra attributes and crispy-forms will automagically inject them within template context. Let's see an example, to make things clear. 130 | 131 | We want some forms to have labels uppercase, for that we would like to set a helper attribute name ``labels_uppercase`` to ``True`` or ``False``. So we go and set in our helper:: 132 | 133 | helper.labels_uppercase = True 134 | 135 | What will happen is that crispy-forms will inject a Django template variable named ``{{ labels_uppercase }}`` with its corresponding value within its templates, including ``field.html``, which is the template in charge of rendering a field when using crispy-forms. So we can go into that template and customize it. We will need to get familiar with it, but it's quite easy to follow, in the end it's only a Django template. 136 | 137 | When we find where labels get rendered, this chunk of code to be more precise:: 138 | 139 | {% if field.label and not field|is_checkbox and form_show_labels %} 140 | 143 | {% endif %} 144 | 145 | The line that we would change wold end up like this:: 146 | 147 | {% if not labels_uppercase %}{{ field.label|safe }}{% else %}{{ field.label|safe|upper }}{% endif %}{% if field.field.required %} 148 | 149 | Now we only need to override field template, for that you may want to check section :ref:`override templates`. 150 | 151 | .. warning :: 152 | 153 | Be careful, depending on what you aim to do, sometimes using dynamic layouts is a better option, check section :ref:`dynamic layouts`. 154 | --------------------------------------------------------------------------------