pax_global_header00006660000000000000000000000064126435435200014516gustar00rootroot0000000000000052 comment=b1f68af959d790794b035c3239eb654114e6b32c django-crispy-forms-1.6.0/000077500000000000000000000000001264354352000154175ustar00rootroot00000000000000django-crispy-forms-1.6.0/.coveragerc000066400000000000000000000000241264354352000175340ustar00rootroot00000000000000[run] branch = True django-crispy-forms-1.6.0/.editorconfig000066400000000000000000000004411264354352000200730ustar00rootroot00000000000000# http://editorconfig.org [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.{html,py,rst}] indent_style = space indent_size = 4 [*.{html,rst}] trim_trailing_whitespace = false [Makefile] indent_style = tab insert_final_newline = false django-crispy-forms-1.6.0/.gitignore000066400000000000000000000005001264354352000174020ustar00rootroot00000000000000setuptools_git-0.3.4-py2.6.egg #buildout's specific .installed.cfg eggs/* bin/* build/* dist/* develop-eggs/* parts/* downloads/* #egg's specific *.egg-info #defaults *.pyc *.pyo __pycache__ #temp file tempfile *.swp # coverage .coverage htmlcov _build # OSX *.DS_Store # editors .project .pydevproject .settings/django-crispy-forms-1.6.0/.travis.yml000066400000000000000000000017221264354352000175320ustar00rootroot00000000000000language: python sudo: false python: - "2.7" - "3.2" - "3.3" - "3.4" - "3.5" env: - DJANGO='django>=1.8.0,<1.9.0' - DJANGO='django>=1.9.0,<1.10.0' - DJANGO='https://github.com/django/django/archive/master.tar.gz' install: - pip install $DJANGO - pip install -e . script: - make test notifications: email: false matrix: exclude: - python: "3.2" env: DJANGO='https://github.com/django/django/archive/master.tar.gz' - python: "3.2" env: DJANGO='django>=1.9.0,<1.10.0' - python: "3.3" env: DJANGO='https://github.com/django/django/archive/master.tar.gz' - python: "3.3" env: DJANGO='django>=1.9.0,<1.10.0' - python: "3.4" env: DJANGO='https://github.com/django/django/archive/master.tar.gz' - python: "3.5" env: DJANGO='django>=1.7.0,<1.8.0' allow_failures: - env: DJANGO='https://github.com/django/django/archive/master.tar.gz' after_success: - bash <(curl -s https://codecov.io/bash) django-crispy-forms-1.6.0/CHANGELOG.md000066400000000000000000000673561264354352000172510ustar00rootroot00000000000000# CHANGELOG for django-crispy-forms ## 1.6.0 (2016/1/7) * Fixed compatibility with Django 1.9 * Added Bootstrap 4 template pack * Other small fixes. See [1.6.0 Milestone](https://github.com/maraujop/django-crispy-forms/issues?q=milestone%3A1.6.0+is%3Aclosed) for full issue list. ## 1.5.2 (2015/9/10) * Fix: KeyError: u'wrapper_class' when clearing template context. #511, #512 * Fix: Alignment of stacked checkboxes and radio buttons in Bootstrap 3 #358 See [1.5.2 Milestone](https://github.com/maraujop/django-crispy-forms/issues?utf8=✓&q=milestone%3A1.5.2+) for full issue list. ## 1.5.1 (2015/8/21) Special thanks in this release to Dmitry Dygalo @Stranger6667 for a marathon effort updating the test suite and code base. * Switched to py.test. Modernised test suite. Enabled tox, code coverage and Travis for all supported Python/Django versions. [See list of Merged "Testing/Process" PRs here](https://github.com/maraujop/django-crispy-forms/pulls?q=is%3Apr+is%3Amerged+milestone%3A1.5.1+label%3ATesting%2FProcess) * Added compatibility with Python 3.2 * Fix: Allow LayoutObject & BaseInputs to accept custom template #493 ## 1.5.0 (2015/8/16) Special thanks in this release for all the **supporters and donators**. * Fixed compatibility with: Python 3, Django 1.7, Django 1.8 and Django 1.4.16. Merged PR #417, but thanks to PRs #369, #368 and #310. Closes also #383. * Updated test suite for compatibility with all supported Django versions 1.4, 1.7, 1.8 and run CI against these and Django `master`. See #451, #455. * Bug fix for specifying `template_pack` in `{% crispy %}` tag, `bootstrap3` couldn't be set that way. * New doc section for creating custom template packs. * Fixed Bootstrap3 checkbox alignment issues with label texts, see #275. * First `AccordionGroup` can now be set to `active=False`, see #246. * Fixed Bootstrap3 checkbox alignment issues for all device sizes, see #225 and #267. * All forms are now rendered with {{ form.media }}, this makes forms containing widgets with `Media` meta class work, see #263. * Adjusted `{% specialspaceless %}` to avoid breaking intended spaces and be less aggressive, see #250. * Fixed inputs rendering for bootstrap3 and redo `FormActions` layout object bootstrap3 template for correct alignment, see #279. * `MultiField` now obeys `form_show_labels`, see #278. * Added `wrapper_class` to `bootstrap.InlineRadios`, see #272. * Render label for checkboxes within `table_inline_formset.html`, see #262. * Removed deprecated layout object `AppendedPrependedText`, replaced by `PrependedAppendedText`. * Fixed `PrependedAppendedText` when rendering a select widget, in bootstrap and bootstrap3 template packs, see #258. * Added support to `{% crispy_addon %}` tag for `form_show_labels`, see #256. * Major cleanup and refactor of the template pack system thanks to @tepez, see #217 and #237: - Template packs are now self contained/independent, removing dangerous cross references. If you have a custom template pack this can cause backwards incompatibility, but it will pay in the future to adjust to this changes. - `MultiField` cannot be used with `bootstrap` or `bootstrap3` template packs. - Added `template_pack` `FormHelper` attribute, so that template packs can be specified at form helper level. This changes layout objects `render` behavior. * Default template pack is now `bootstrap3`. * Make `CRISPY_TEMPLATE_PACK` setting optional, see #237 and #244. ## 1.4.0 (2013/9/1) Special thanks in this release to **James Friedman @jamesmfriedman**, for his amazing support in PR #213, adding initial Bootstrap 3 support. * Bootstrap 3 inline forms support and docs for inline forms with Bootstrap 3, see #233. * `update_attributes` can now work with a field name, see docs. * Adjusted unicode checkings for Python 3, see #231. * Adjusted how bootstrap `Tab` layout object attributes are applied to make sense, see #228. * Major refactor of testing suite architecture, breaking huge `tests.py` file into several and also splitting tests for different template packs into different tests cases, runners for template packs updated. * Added support for horizontal forms in bootstrap3, see #209. * Fixed spaces missing when rendering several submit inputs continued, see #211. * Fixed checkboxes and radios for Bootstrap3, adjusted multiple inline radios and checkboxes, see #225. * Update accordion markup for bootstrap3 compatibility, see #229. * Moved `UneditableField` to bootstrap module, place where it should live, no backwards compatible import left behind. * Added `bootstrap3` template pack thanks to James Friedman, see #213 and #209. * `RadioSelect` and `CheckboxSelectMultiple` widget values and texts were being localized, when they shouldn't bee, see #214. * If Django widget attrs where set for `RadioSelect` or `CheckboxSelectMultiple` they were not being rendered by crispy-forms, see #206. * `form_show_labels` wasn't working correctly with some layout objects, see #193. ## 1.3.2 (2013/6/23) * Labels were not being rendered with `|crispy` filter, see #202. ## 1.3.1 (2013/6/17) * Fix default value for `form_show_labels` in case FormHelper doesn't define it, see #199. * Added a backported version of `override_settings` for testing django 1.3.7 and adding it to Travis-ci matrix. ## 1.3.0 (2013/6/16) Special thanks in this release to **Charlie Denton @meshy**, for his amazing support in PRs #189 and #190, long due. * Deprecated `Tab` and `TabHolder` imports from `layout.py` module, they now live in `bootstrap.py` module. * Removed Python 2.5 compatibility. * Added `disable_csrf` helper attribute, see docs. * Travis-ci support thanks to Charlie Denton, see #190. * Python 3 compatibility thanks to Charlie Denton, see #189 and #190. * Added a settings variable named `CRISPY_ALLOWED_TEMPLATE_PACKS` for adding easily support for new template packs, see #192. * Added `{% crispy_addon %}` tag, see #195. * Make `CRISPY_TEMPLATE_PACK` optional for tests * Make tests run the same exactly way with `runtests.py` and `manage.py test`, see #183. * Bug fix for `wrap_together` when using it with partial slices. * Fixes for `KeepContext` context manager, see #180. * Added `FormHelper.field_template` attribute, for easily override field template for a specific form/formset, see docs. * Added a template for rendering formsets inline within tables named `bootstrap/table_inline_formset.html`, that can be easily used in conjunction with `FormHelper.template`. * Added `FormHelper.template` attribute, that easily allows to override form/formset general structure template, see docs. * Added `form_show_labels` helper attribute. * Redoing filters to use `isinstance` instead of hacky internal name checking, this way subclasses of standard fields will work out of the box with crispy-forms, see #176. ## 1.2.8 (2013/5/10) * Bug fix for `KeepContext` context manager, when crispy-forms used with Jingo/Jinja2 templates, see #179. * Some formset tests were breaking in some Django versions. Also added a `make test` for easily running project tests, see #178. ## 1.2.7 (2013/5/6) * Bug fix for model formsets and inline formsets, when being rendered with a `FormHelper` with a layout set, where some hidden formset management fields would be missing, breaking saving to database. * Added `render_hidden_fields` attribute to `FormHelper`, see docs. * Added `render_hidden_fields` attribute to `FormHelper`, see docs. ## 1.2.6 (2013/5/1) * Fixes to `Container` and `TabHolder`, affecting class variable helpers with layouts containing `TabHolder` or `AccordionGroup` objects, see #172. * Bug fix for `KeepContext` context manager, see #172. ## 1.2.5 (2013/4/25) * Avoid raising Exceptions in `|as_crispy_field` filter when not in DEBUG mode. * Popping `css_id` to avoid having a css-id attribute, see #167. * Fixed a bug in dynamic layout API, when wrapping layout objects that had arguments passed after fields. Moving `LayoutSlice` to `layout_slice` module. * Fixed test failing when tests runned from manage.py test. * Fixed testing name conflict, see #130. ## 1.2.4 (2013/4/13) * Added `wrapper_class` kwarg to `Field` layout object, see #163. * Added `Accordion` and `AccordionGroup` bootstrap layout objects, see #162. * Bug fix in `render_crispy_form` to avoid override existing context, see #153. * Use formset iterator instead of `forms` list attribute, see #152. * Don't display fieldset legend if empty, see #147 and #155. * Bug fix for pickling crispy forms by powderflask, see #107. * Switched from `django.form.utils.flatatt` to internal `flatatt` utils implementation, this allows adding data-attrs to `FormActions` and `StrictButton`. * `render_field` now uses a context manager to avoid side effects when layout objects update template context. This makes context mutability safe within layout objects. * Added `greedy` kwarg to `filter` dynamic API. * Fixing error logging on `|as_crispy_field` filter, see #135. * Implemented `__delitem__`, `__len__` and `__setitem__` in `LayoutObject` and `DynamicLayoutHandler`, this avoids pylint warnings, see #114. * Docs folder no longer included when installing crispy-forms, see #132. * Added `wrap_once`, `update_attributes`, `map` and `pre_map` to LayoutSlice. ## 1.2.3 (2012/12/4) * Fixed imports to be relative to package, avoiding namespace collisions. * Removed circular dependency between `layout` and `bootstrap`, see #129. * Bug fix, adapted use of inspect module in `utils.py` to avoid breaking Python 2.5 compatibility. ## 1.2.2 (2012/11/30) * Bug fix, reduction of white space in crispy-forms output could mess within tags, see #127, reverting part of this reduction. * Renamed `AppendedPrependedText` to `PrependedAppendedText`. * Moved `Tab` and `TabHolder` to `bootstrap.py`. ## 1.2.1 (2012/11/28) * Bug fix `help_text_inline` set to True, see #117. * New fix for the space between buttons problems, see #62. * Reduced importantly whitespace in html generated by crispy-forms, forms are now more compact. * Added support for specifying a template pack per form, see #66 and #109. * Removed `clearfix` class from bootstrap templates, not necessary anymore, see #105. * Space cleanup in bootstrap templates, thanks to Si Feng, see #122. * Fixed `MultiField` to work with `form_show_errors` helper attribute. * Fixed a bug in `MultiField` that set error class when there were form errors, no matter if the fields with errors were contained within, see #120. * `FieldWithButtons` now supports `Field` layout object as its first parameter, for setting input attributes. * Bug fixes for `FieldWithButtons`, field label, `help_text` or error messages were not being rendered, see #121. * Fixed a bug that was making crispy-forms render extra fields with ModelForms that didn't have `Meta.fields` defined, thanks to Jean-Baptiste Juin for reporting it. * Fixed a bug that was breaking Django 1.2 compatibility when copying context variables, thanks to Alex Yakovlev for submitting a patch, see #108. * Fixed a bug for `AppendedText`, `PrependedText` and `AppendedPrependedText` layout objects, thanks to Bojan Mihelac, see #104. * Fixed a bug in appended and prepended text layout objects for respecting hidden fields, thanks to Bojan Mihelac, see #103. * Added two new bootstrap layout objects: `FieldWithButtons` and `StrictButton`. * Added checks and better error messages for dynamic API. * Fixed `get_layout_objects` recursive call for Python2.5 compatibility, thanks to Can Başçıl for reporting it. ## 1.2.0 (2012/9/24) * Update prepended and appended templates to respect hidden fields, thanks to Bojan Mihelac, see #GH-103. * Added `InlineCheckboxes` to bootstrap layout objects, for rendering checkboxes inline. * `BaseInput` subclasses, like `Submit` can now have its value set to a context variable. * Rendering inputs added with `add_input` in bootstrap using the right templates, see #GH-95. * Improved formsets rendering docs thanks to Samuel Goldszmidt, see #GH-92. * Added `Tab` and `TabHolder` layout objects thanks to david-e, see #GH-91. * Fixed default bootstrap button default classes thanks to david-e, see #GH-90. * Fixed some flaws in new testing structure by Markus Hametner. * Added helper attribute `error_text_inline` thanks to Lee Semel for controlling how to render form errors, as a block or inline, see #GH-87. * Support `ModelMultipleChoiceField` on `checkboxselectmultiple`, see #GH-86. * Redoing testing structure a little bit, to run uni_form and bootstrap tests separately. They share most of the code base, but templates pack are separate and we need to care both have the same quality assurance. * `AppendedText`, `PrependedText` and `AppendedPrependedText` were not respecting `form_show_errors` helper attribute, see #GH-77. * Added a version string to the app under root __init__, see #GH-76. * Added `html5_required` helper attribute for rendering required fields using HTML5 required attribute within the input, see #GH-72. Thanks to Lloyd Philbrook. * Some docs typos and errors fixed, also a major upgrade to docs covering the new functionality. * Adding a `utils.render_crispy_form` function, that renders a form the crispy way in Python code. This might be useful with AJAX, testing or text generation/manipulation, see #GH-64. * Tiny cosmetic fix, that adds an space after a button, see #GH-62. * `MultiField` and `Fieldset` layout objects can now have any kind of attribute defined, thanks to Lloyd Philbrook, see #GH-71. * Making `Fieldset`, `MultiField` & `HTML` contents lazy translatable thanks to Rivo Laks, see #GH-69. * Fixing `radioselect` checked status when used for a FK in a ModelForm, see #GH-68. * Fixing `form.Meta` usage, using instance `fields` instead of static `Meta` definition, so that it works when updating forms on the go, see #GH-59. * Added a low level manipulation API for layout and layout objects. Added a `LayoutObject` base class that creates an interface. This allows to access nested fields easily and use list methods without know internals of the system. * Added a `|classes` filter that returns field's classes. * Now `FormHelper` can accept a form instance as an optional first argument, from which it can build a default layout. * Added an API for manipulating dynamic layouts and programmatic layout building. * Added `UneditableField` bootstrap layout object for uneditable fields. * Support for hiding fields using `Field('field_name', type="hidden")`, see #GH-55. * Avoid template context pollution of variable `form` after using {% crispy %} tag, see #GH-54. * Added an `attrs` helper attribute, for more flexible form attributes, see #GH-48. * New `AppendedPrependedText` layout object thanks to Samuel Goldszmidt, see #GH-45. * Removal of some whitespace in crispy form's HTML generated, see #GH-42. * New `MultiWidgetField` layout object by Michal Kuffa, see #GH-39. ## 1.1.4 (2012/5/24) * Multithread safety improvements for `BasicNode`. * Security fix: Thread safety fixes to `CrispyFieldNode` thanks to Paul Oswald. This avoids leaking information between requests in multithreaded WSGI servers. * Added css class `control-label` to `AppendedText` and `PrependedText` layout object's templates. * `{% crispy field %}` tag can now pass attrs to `MultiWidget` subclasses by Michal Kuffa. `attrs` are set for sub-widgets. Also `attrs` can now be an iterable for passing different attributes to different sub-widgets. For example,this way MultiWidget's widgets get css classes set correctly. * Turning underscores into hyphens for `Field` layout objects. * Fix for `ChoiceFields` using non-string choices with radio buttons thanks to Rudy Mutter. See #GH-46, #GH-43 and #GH-35. ## 1.1.3 (2012/4/21) * `|crispy` and `|as_crispy_field` filters were not rendering errors. Thanks to @ximi for reporting it and submitting a patch. See issue #GH-28. * Fixing a test that was breaking when language was not English. Thanks to @gaftech, see #GH-30. * Fixing `radioselect.html` and `checkboxselectmultiple.html` templates. Thanks to Christopher Petrilli for submitting a patch for `radioselect`. See issue #GH-35. * HTML attributes can now be set in `BaseInput` subclasses like `Button` by @jamesmfriedman. See #GH-32. * Fix for dynamic crispy-forms with Meta classes by Jeroen Vloothuis. See #GH-37. * Labels now use `id_for_label` instead of `auto_id` to avoid ids breaking on multiwidgets. by Daniel Izquierdo. See #GH-38. * Adding a flatatt custom function in `utils.py` for flatting extra HTML attributes. * HTML attributes can now be set in `Div` layout object. * Adding tests for new functionality and bugs. ## 1.1.2 (2012/2/29) * input name attribute is no longer slugified if only one word is provided, respecting caps. * Changes in bootstrap global error templates by David Bennett. * Added class `control-label` to labels, for horizontal layout thanks to bitrut. * Using `{{ field.html_name }}` instead of `{{ field.name }}` in field templates, so that they work with form prefixes (formwizard) by Patrick Toal. * Fixing error rendering in bootstrap AppendedText and PrependedText. * Applying `field.css_classes` in bootstrap `field.html` instead of widget classes. * Fixes for bootstrap simple checkbox input to be wrapped correctly. ## 1.1.1 (2012/2/17) * Fixing a critical bug in bootstrap templates, that was breaking `{% load crispy_forms_tags %}` ## 1.1.0 * Fixing produced html by a checkbox field, closing label the right way and rendering checkbox in the right place. * Passing full context to field rendering, to be consistent and having access in `field.html` template to helper attributes. * Custom helper attributes can now be set and will be part of templates context, this way you can define custom specific behavior. * Adding @kennethlove bootstrap template pack into django-crispy-forms core. * Adding `CRISPY_TEMPLATE_PACK` setting variable to easily switch between different template packs. Default template pack is now bootstrap. * Upgrading bootstrap templates, fixing some bugs and redoing the hierarchy. * Upgrading tests for multiple template packs. * Renaming `UNIFORM_FAIL_SILENTLY` setting variable to `CRISPY_FAIL_SILENTLY`, upgrading migration instructions. * Redoing bootstrap `field.html` template to render `radioselect` and `checkboxselectmultiple` Django widgets a la bootstrap. * Adding a `render_unmentioned_fields` helper attribute, that renders all fields in a form, no matter what the layout is. Default is `False`. * Adding a `|css_class` filter that renders field classes in an elegant way. * Turning `|with_class` filter into `{% crispy_field %}` tag, so that parameters for rendering the field can be passed. * Adding a `help_text_inline` helper attribute, that controls whether to render help texts in bootstrap with "help-inline" or "help-block". * Adding a `flat_attrs` variable to the context passed to `field.html` to be able to do all kind of html attributes when rendering a field, using `Field` layout object. * Adding a `template` kwarg to `Field` layout object that allows to override template used for rendering a field. * Adding a `bootstrap.py` module that holds bootstrap specific layout objects, for higher bootstrap integration. * Adding a `AppendedText`, `PrependedText` and `FormActions` bootstrap layout objects. First two based in polyvalent `Field` layout object. ## 1.0.0 * Using `baseinput.html` template within `whole_uni_form.html`, to be DRY and consistent. * `BaseInput` subclasses like `Submit` can now have ids set, using `css_id` * Adding a simplified alternative syntax for `{% uni_form %}` tag. We can now do `{% uni_form form %}` for rendering a form using a helper, instead of `{% uni_form form form.helper %}`, if the `FormHelper` attribute attached to the form is named `helper`. * Improving `rendered_fields` checking performance. * Layouts are now rendered strictly. We don't render fields missed in the layout. If the form has a Meta class with `fields` or `exclude`, then we follow Django standards. * Added `Field` layout object. You can wrap name fields within and set all kind of attributes easily or override widget template. * Fixed #GH-111 we were not rendering all the classes in `|with_class` filter * Moving django-uni-form to django-crispy-forms. Renaming tags, filters and modules. Updating tests and so on. Adding migration instructions. * More work on simpler and easier docs. * Adding `form_show_errors` helper attribute, that controls whether to render or not `form.errors` * Improving template hierarchy for more template code reusability. # CHANGELOG for django-uni-form ## 0.9.0 You can read on how to use new features included in this version at: http://tothinkornottothink.com/post/10398684502/django-uni-form-0-9-0-is-out-security-fix * Fixed a bug in `|with_class` filter so that it supports `show_hidden_initial`, see #GH-95 to not break. * Fixed a problem on Fieldset's legends internationalization. Thanks to Bojan Mihelac, see #GH-90. * Fixed XSS bug thanks to Charlie Denton, see #GH-98. Errors cannot be rendered safe, because field's input can be part of the error message, that would mean XSS. * Updating and improving docs, adding more use case examples. * Split `helpers.py` file into `helper.py`, `layout.py` and `utils.py`. Added a deprecation warning. * Improved testing coverage, specially for formsets and i18n. * Improved rendering performance of `{% uni_form %}` tag and `|as_uni_form` filter avoiding reloading templates every time, see #GH-81. * Added support for Django `Form.error_css_class` and `Form.required_css_class` custom CSS classes, see #GH-87. * Moved template code in Layout objects into separate files in `uni_form/layout` directory. Layout objects templates can now be easily overridden, see #GH-37. * `form_style` can now be used without having to set a helper Layout, see #GH-85. * `form_action` is not lowered anymore and `form_action` is set to "" by default instead of "." thanks to Jianbo Guo, see #GH-84. * `Multifield` field template `multifield.html` markup fixed, adding `help_text` support and removing `labelclass` from labels. * Fixed testing suite, when run not using `DjangoTestSuiteRunner` provided, thanks to Narsil #GH-82. * Removed test_project from the project. * Improved `MultiField` performance avoiding instantiating BoundFields twice. * Fixed a bug in `MultiField` that raised an exception when internal fields had errors, because of `self.css` not existing. * Added an extra optional parameter to `render_field` called `layout_object`, used for storing in it a list of bound fields. * Refactor all Layout objects to use templates and not having hardcoded HTML in the code, based on Jonas Obrist work. Resolves Issue #GH-37 * Added a Layout object called `Div`. `Row` and `Column` both inherit from `Div` * `Layout` can now be a child of `Layout`, see issue #GH-76. ## 0.8.0 You can read on how to use new features included in this version at: http://tothinkornottothink.com/post/7339670508/new-kung-fu-in-django-uni-form-0-8-0 * Elevated Miguel Araujo to project lead! * Added a forloop simulator for formset forms rendering. * `ButtonHolder` Layout object added for holding `HTML` and buttons: `Submit`, `Reset`, `Button`. * Turned BaseInput inherited objects like: `Submit`, `Reset`, `Hidden` and `Button` into Layout objects. * Fixed a bug with `rendered_fields` when no fields where in the Layout. * `Fieldset` legends are now templates full context aware. * Based on @issackelly's and @johnthedebs's work a template called `betterform` has been added for supporting @carljm's form-utils BetterForms. * `FormHelper` method `get_attr` has been renamed to `get_attributes` * `uni_form_tags` has been split into two different files: `uni_form_tags` and `uni_form_filters`. * Removing i18n tags from the templates, as they are not necessary anymore. * Removed all the internationalized hardcoded text, in favor of template variables: `form_error_title` and `formset_error_title`, both can be set as helper's attributes. * `as_uni_errors` filter can now render formset's `non_form_errors` uni-form way. * Moved `{% uni_form_setup %}` tag to use STATIC_URL instead of MEDIA_URL * Added the possibility to specify a helper for formsets too. * Renamed media directory to static, to be compatible with Django 1.3 staticfiles. * Added a `form_style` FormHelper attribute for setting global style of a form: inline or default. * Turning `HTML` into a full context aware django template field, having access to the whole context of the template in which the form is rendered. * Turning `Layout` and `Fieldset` fields attributes into lists, so that they can be changed dynamically. * Changing formHints from paragraphs to divs, so ul or ol can be placed within. * Removing slugify filter from form ids, so they can be set as user's preferences. * Added CSS class 'asteriskField' for asterisks. Added CSS class 'fieldRequired' for required input labels. * `UNIFORM_FAIL_SILENTLY` variable setting has been added for making django-uni-form log errors and fail silently, based on Adam Cupiał's work. * Several bug fixes in `MultiField`. * Added unicode support for layout field names and improved error handling. * Refactored testing system and raised testing coverage. * Clean part of the code base and comments. All old CSRF code for supporting old versions of Django has been removed. * Refactored BasicNode for better readability and reducing lines of code. * Added formsets support based on Victor Nagy's (nagyv) and Antti Kaihola's (akahiola) work. * Bug fix in `{% uni_form %}` tag that didn't work without a helper and it was meant to be optional. * CSS classes can be set in Submit buttons. * Thanks to J. Javier Maestro (jjmaestro) now we can set ids and classes for `Fieldset`, `MultiField`, `Row` and `Column`. * Thanks to Richard Marko (sorki) changed CSS class of PasswordInput widget. * Removing `Toggle` class as it wasn't being used anywhere. * Moved `BaseInput` to helpers and removed `util.py` file. * Removed `{% uni_form_jquery %}` tag * Removed `namify` function from tags, as It wasn't being used anywhere. * Improved internal documentation * form methods generated by FormHelper are in lowercase (http://github.com/pydanny/django-uni-form/issues#issue/20) * Thanks to Nagy Viktor added form_tag attribute to FormHelper. Now you can use the uni_form tag without the leading and trailing form tags. * Thanks for Alison Rowland for giving django-uni-form sphinx docs * Incorporated uni-form 1.4 by Dragan Babic * Provide better adherence to uni-form specification of error messages * mirumee provided some great work for making FormHelper more subclassable. * django-uni-form 0.8 and higher lays out the HTML for the uni_form tag differently. The errorMsg div is now outside the fieldset as it should be. * Thanks to Casper S. Jensen django-uni-form now supports 1.2 style csrf_token. * csrf_token does not break earlier versions of Django. This will change when no version of django does not support csrf_token. * Thanks to j0hnsmith changed {{ error }} to {{ error|safe }} so that html (eg links) can be added to error messages. * Thanks to j0hnsmith changed {{ field.label }} to {{ field.label|safe }} so that html (eg links) can be added to field labels * Kudos to Stepan Rakhimov fixed an admin datetime issue. * Thanks to patrys (Patryk Zawadzki) FormHelper class is now easily subclass-able. * Sorki (Richard Marko) made it so things work better in direct_to_template. ## 0.7.0 * Removed a
from the layout module. * Changed templatetags/uni_form.py to templatetags/uni_form_tags.py. Yes, this breaks backwards compatibility but fixes a namespace problems in Django with naming a templatetag library after the parent application. * Changed form_action attribute to accept not just named URLs but also any old URL. * Added in uni_form_setup tag. * Added tests * Added several new contributors including Dragan Babic * Added Danish language translation django-crispy-forms-1.6.0/CONTRIBUTORS.txt000066400000000000000000000050451264354352000201210ustar00rootroot00000000000000============ Contributors ============ django-crispy-forms Project Maintainers ======================================= * Miguel Araujo * Carlton Gibson django-uni-form Project Founder =============================== * Daniel Greenfeld Contributors ============ * Alison Rowland * Bojan Mihelac * bjunix * Casper S. Jensen * Chris Adams * Eddy Mulyono * j0hnsmith * James Pic * James Tauber * Karl Bowden * Marcin Grzybowski * Michael Lind Mortensen * mirumee * mvaerle * Nagy Viktor * Patrick Lauber * Patryk Zawadzki * Skylar Saveland * Stepan Rakhimov * John Maxwell * Richard Marko * Victor Nagy * Antti Kaihola * J. Javier Maestro * Issac Kelly * John Debs * Adam Cupiał * Nicolas Patry * Jonas Obrist * Charlie Denton * Jason Culverhouse * James Turnbull * Patrick Toal * David Bennett * bitrut * ximi * Christopher Petrilli * James Friedman * Jeroen Vloothuis * Daniel Izquierdo * gaftech * Michal Kuffa * Paul Oswald * Rudy Mutter * Samuel Goldszmidt * Andrei Antoukh * * Rivo Laks * Lloyd Philbrook * Piet Delport * * Markus Hametner * Thomas Grainger * Lee Semel * * Alex Yakovlev * Si Feng * Igor Katson * Ben Delevingne * * Evan Borgstrom * Daniel Shapiro * * Stefan "hr" Berder (白峰) * Suleyman Melikoglu * Vladislav Mitov * Nemesis Fixx * Chris Vigelius * David Cramer * Stas Rudakou * Tom Yam * Svyatoslav Bulbakha * Andres Vargas * Gabe Jackson * Camilo Nova * * Daniel Mascarenhas * Paras Kuhad * Kevin Trad * Steven Klass * David Fischer * Stefan Tjarks * Jan Dittberner * Michael Nielsen * Stephen Mitchell * * Christopher Adams * John Comeau * Yoong Kang Lim * Dmitry Dygalo django-crispy-forms-1.6.0/LICENSE.txt000066400000000000000000000020671264354352000172470ustar00rootroot00000000000000Copyright (c) 2009-2015 Miguel Araujo and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.django-crispy-forms-1.6.0/MANIFEST.in000066400000000000000000000003211264354352000171510ustar00rootroot00000000000000include CONTRIBUTORS.txt include LICENSE.txt include MANIFEST.in include README.rst recursive-include crispy_forms/static * recursive-include crispy_forms/templates * recursive-include crispy_forms/tests *.py django-crispy-forms-1.6.0/Makefile000066400000000000000000000003151264354352000170560ustar00rootroot00000000000000.PHONY: develop test develop: pip install -q -r requirements.txt pip install -q -e . test: develop DJANGO_SETTINGS_MODULE=crispy_forms.tests.test_settings py.test crispy_forms/tests --cov=crispy_formsdjango-crispy-forms-1.6.0/README.rst000066400000000000000000000066361264354352000171210ustar00rootroot00000000000000=================== django-crispy-forms =================== .. image:: https://travis-ci.org/maraujop/django-crispy-forms.png?branch=dev :alt: Build Status :target: https://travis-ci.org/maraujop/django-crispy-forms .. image:: http://codecov.io/github/maraujop/django-crispy-forms/coverage.svg?branch=master :target: http://codecov.io/github/maraujop/django-crispy-forms?branch=master 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. The application mainly provides: * 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. * 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. 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. .. _`Uni-form`: http://sprawsm.com/uni-form .. _`Bootstrap`: http://twitter.github.com/bootstrap/index.html .. _`see the docs`: http://django-crispy-forms.rtfd.org Authors ======= django-crispy-forms is the new django-uni-form. django-uni-form was an application created by `Daniel Greenfeld`_ that I leaded since version 0.8.0. The name change tries to better explain the purpose of the application, which changed in a significant way since its birth. If you are upgrading from django-uni-form, we have `instructions`_ for helping you. * Lead developer: `Miguel Araujo`_ .. _`Daniel Greenfeld`: https://github.com/pydanny .. _`Miguel Araujo`: https://github.com/maraujop .. _`instructions`: http://django-crispy-forms.readthedocs.org/en/1.1.1/migration.html Example ======= This is a teaser of what you can do with latest django-crispy-forms. `Find here the gist`_ for generating this form: .. image:: http://i.imgur.com/LSREg.png .. _`Find here the gist`: https://gist.github.com/1838193 Documentation ============= For extensive documentation see the ``docs`` folder or `read it on readthedocs`_ .. _`read it on readthedocs`: http://django-crispy-forms.readthedocs.org/en/latest/index.html Special thanks ============== * To Daniel Greenfeld (`@pydanny`_) for his support, time and the opportunity given to me to do this. * The name of the project was suggested by the fantastic Audrey Roy (`@audreyr`_) * To Kenneth Love (`@kennethlove`_) for creating django-uni-form-contrib from which bootstrap template pack was started. .. _`@audreyr`: https://github.com/audreyr .. _`@pydanny`: https://github.com/pydanny .. _`@kennethlove`: https://github.com/kennethlove Note ---- 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. .. _Django: http://djangoproject.com django-crispy-forms-1.6.0/__init__.py000066400000000000000000000000001264354352000175160ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/000077500000000000000000000000001264354352000201365ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/LICENSE000066400000000000000000000020651264354352000211460ustar00rootroot00000000000000Copyright (c) 2009 Daniel Greenfeld and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.django-crispy-forms-1.6.0/crispy_forms/__init__.py000066400000000000000000000000571264354352000222510ustar00rootroot00000000000000# -*- coding: utf-8 -*- __version__ = '1.6.0' django-crispy-forms-1.6.0/crispy_forms/base.py000066400000000000000000000013611264354352000214230ustar00rootroot00000000000000 class KeepContext(object): """ Context manager that receives a `django.template.Context` instance and a list of keys Once the context manager is exited, it removes `keys` from the context, to avoid side effects in later layout objects that may use the same context variables. Layout objects should use `extra_context` to introduce context variables, never touch context object themselves, that could introduce side effects. """ def __init__(self, context, keys): self.context = context self.keys = keys def __enter__(self): pass def __exit__(self, type, value, traceback): for key in list(self.keys): if key in self.context: del self.context[key] django-crispy-forms-1.6.0/crispy_forms/bootstrap.py000066400000000000000000000317001264354352000225260ustar00rootroot00000000000000from __future__ import unicode_literals from random import randint from django.template import Template from django.template.loader import render_to_string from django.template.defaultfilters import slugify from .compatibility import text_type from .layout import LayoutObject, Field, Div from .utils import render_field, flatatt, TEMPLATE_PACK class PrependedAppendedText(Field): template = "%s/layout/prepended_appended_text.html" def __init__(self, field, prepended_text=None, appended_text=None, *args, **kwargs): self.field = field self.appended_text = appended_text self.prepended_text = prepended_text if 'active' in kwargs: self.active = kwargs.pop('active') self.input_size = None css_class = kwargs.get('css_class', '') if 'input-lg' in css_class: self.input_size = 'input-lg' if 'input-sm' in css_class: self.input_size = 'input-sm' super(PrependedAppendedText, self).__init__(field, *args, **kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs): extra_context = { 'crispy_appended_text': self.appended_text, 'crispy_prepended_text': self.prepended_text, 'input_size': self.input_size, 'active': getattr(self, "active", False) } template = self.get_template_name(template_pack) return render_field( self.field, form, form_style, context, template=template, attrs=self.attrs, template_pack=template_pack, extra_context=extra_context, **kwargs ) class AppendedText(PrependedAppendedText): def __init__(self, field, text, *args, **kwargs): kwargs.pop('appended_text', None) kwargs.pop('prepended_text', None) self.text = text super(AppendedText, self).__init__(field, appended_text=text, **kwargs) class PrependedText(PrependedAppendedText): def __init__(self, field, text, *args, **kwargs): kwargs.pop('appended_text', None) kwargs.pop('prepended_text', None) self.text = text super(PrependedText, self).__init__(field, prepended_text=text, **kwargs) class FormActions(LayoutObject): """ Bootstrap layout object. It wraps fields in a
Example:: FormActions( HTML(Information Saved), Submit('Save', 'Save', css_class='btn-primary') ) """ template = "%s/layout/formactions.html" def __init__(self, *fields, **kwargs): self.fields = list(fields) self.template = kwargs.pop('template', self.template) self.attrs = kwargs if 'css_class' in self.attrs: self.attrs['class'] = self.attrs.pop('css_class') def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): html = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) template = self.get_template_name(template_pack) context.update({ 'formactions': self, 'fields_output': html }) return render_to_string(template, context.flatten()) def flat_attrs(self): return flatatt(self.attrs) class InlineCheckboxes(Field): """ Layout object for rendering checkboxes inline:: InlineCheckboxes('field_name') """ template = "%s/layout/checkboxselectmultiple_inline.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return super(InlineCheckboxes, self).render( form, form_style, context, template_pack=template_pack, extra_context={'inline_class': 'inline'} ) class InlineRadios(Field): """ Layout object for rendering radiobuttons inline:: InlineRadios('field_name') """ template = "%s/layout/radioselect_inline.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return super(InlineRadios, self).render( form, form_style, context, template_pack=template_pack, extra_context={'inline_class': 'inline'} ) class FieldWithButtons(Div): template = '%s/layout/field_with_buttons.html' field_template = '%s/layout/field.html' def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs): # We first render the buttons field_template = self.field_template % template_pack buttons = ''.join( render_field( field, form, form_style, context, field_template, layout_object=self, template_pack=template_pack, **kwargs ) for field in self.fields[1:] ) extra_context = {'div': self, 'buttons': buttons} template = self.get_template_name(template_pack) if isinstance(self.fields[0], Field): # FieldWithButtons(Field('field_name'), StrictButton("go")) # We render the field passing its name and attributes return render_field( self.fields[0][0], form, form_style, context, template, attrs=self.fields[0].attrs, template_pack=template_pack, extra_context=extra_context, **kwargs ) else: return render_field( self.fields[0], form, form_style, context, template, extra_context=extra_context, **kwargs ) class StrictButton(object): """ Layout object for rendering an HTML button:: Button("button content", css_class="extra") """ template = '%s/layout/button.html' field_classes = 'btn' def __init__(self, content, **kwargs): self.content = content self.template = kwargs.pop('template', self.template) kwargs.setdefault('type', 'button') # We turn css_id and css_class into id and class if 'css_id' in kwargs: kwargs['id'] = kwargs.pop('css_id') kwargs['class'] = self.field_classes if 'css_class' in kwargs: kwargs['class'] += " %s" % kwargs.pop('css_class') self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): self.content = Template(text_type(self.content)).render(context) template = self.template % template_pack context.update({'button': self}) return render_to_string(template, context.flatten()) class Container(Div): """ Base class used for `Tab` and `AccordionGroup`, represents a basic container concept """ css_class = "" def __init__(self, name, *fields, **kwargs): super(Container, self).__init__(*fields, **kwargs) self.template = kwargs.pop('template', self.template) self.name = name self._active_originally_included = "active" in kwargs self.active = kwargs.pop("active", False) if not self.css_id: self.css_id = slugify(self.name) def __contains__(self, field_name): """ check if field_name is contained within tab. """ return field_name in map(lambda pointer: pointer[1], self.get_field_names()) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): if self.active: if not 'active' in self.css_class: self.css_class += ' active' else: self.css_class = self.css_class.replace('active', '') return super(Container, self).render(form, form_style, context, template_pack) class ContainerHolder(Div): """ Base class used for `TabHolder` and `Accordion`, groups containers """ def first_container_with_errors(self, errors): """ Returns the first container with errors, otherwise returns None. """ for tab in self.fields: errors_here = any(error in tab for error in errors) if errors_here: return tab return None def open_target_group_for_form(self, form): """ Makes sure that the first group that should be open is open. This is either the first group with errors or the first group in the container, unless that first group was originally set to active=False. """ target = self.first_container_with_errors(form.errors.keys()) if target is None: target = self.fields[0] if not target._active_originally_included: target.active = True return target target.active = True return target class Tab(Container): """ Tab object. It wraps fields in a div whose default class is "tab-pane" and takes a name as first argument. Example:: Tab('tab_name', 'form_field_1', 'form_field_2', 'form_field_3') """ css_class = 'tab-pane' link_template = '%s/layout/tab-link.html' def render_link(self, template_pack=TEMPLATE_PACK, **kwargs): """ Render the link for the tab-pane. It must be called after render so css_class is updated with active if needed. """ link_template = self.link_template % template_pack return render_to_string(link_template, {'link': self}) class TabHolder(ContainerHolder): """ TabHolder object. It wraps Tab objects in a container. Requires bootstrap-tab.js:: TabHolder( Tab('form_field_1', 'form_field_2'), Tab('form_field_3') ) """ template = '%s/layout/tab.html' def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): for tab in self.fields: tab.active = False # Open the group that should be open. self.open_target_group_for_form(form) content = self.get_rendered_fields(form, form_style, context, template_pack) links = ''.join(tab.render_link(template_pack) for tab in self.fields) context.update({ 'tabs': self, 'links': links, 'content': content }) template = self.get_template_name(template_pack) return render_to_string(template, context.flatten()) class AccordionGroup(Container): """ Accordion Group (pane) object. It wraps given fields inside an accordion tab. It takes accordion tab name as first argument:: AccordionGroup("group name", "form_field_1", "form_field_2") """ template = "%s/accordion-group.html" data_parent = "" # accordion parent div id. class Accordion(ContainerHolder): """ Accordion menu object. It wraps `AccordionGroup` objects in a container:: Accordion( AccordionGroup("group name", "form_field_1", "form_field_2"), AccordionGroup("another group name", "form_field") ) """ template = "%s/accordion.html" def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): content = '' # accordion group needs the parent div id to set `data-parent` (I don't # know why). This needs to be a unique id if not self.css_id: self.css_id = "-".join(["accordion", text_type(randint(1000, 9999))]) # Open the group that should be open. self.open_target_group_for_form(form) for group in self.fields: group.data_parent = self.css_id content += render_field( group, form, form_style, context, template_pack=template_pack, **kwargs ) template = self.get_template_name(template_pack) context.update({'accordion': self, 'content': content}) return render_to_string(template, context.flatten()) class Alert(Div): """ `Alert` generates markup in the form of an alert dialog Alert(content='Warning! Best check yo self, you're not looking too good.') """ template = "%s/layout/alert.html" css_class = "alert" def __init__(self, content, dismiss=True, block=False, **kwargs): fields = [] if block: self.css_class += ' alert-block' Div.__init__(self, *fields, **kwargs) self.template = kwargs.pop('template', self.template) self.content = content self.dismiss = dismiss def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): template = self.get_template_name(template_pack) context.update({'alert': self, 'content': self.content, 'dismiss': self.dismiss}) return render_to_string(template, context.flatten()) class UneditableField(Field): """ Layout object for rendering fields as uneditable in bootstrap Example:: UneditableField('field_name', css_class="input-xlarge") """ template = "%s/layout/uneditable_input.html" def __init__(self, field, *args, **kwargs): self.attrs = {'class': 'uneditable-input'} super(UneditableField, self).__init__(field, *args, **kwargs) class InlineField(Field): template = "%s/layout/inline_field.html" django-crispy-forms-1.6.0/crispy_forms/compatibility.py000066400000000000000000000014321264354352000233610ustar00rootroot00000000000000import sys from django.utils.functional import SimpleLazyObject try: basestring except: basestring = str # Python3 PY2 = sys.version_info[0] == 2 if not PY2: text_type = str binary_type = bytes string_types = (str,) integer_types = (int,) else: text_type = unicode binary_type = str string_types = basestring integer_types = (int, long) try: # avoid RemovedInDjango19Warning by using lru_cache where available from django.utils.lru_cache import lru_cache except ImportError: from django.utils.functional import memoize def lru_cache(): def decorator(function, cache_dict=None): if cache_dict is None: cache_dict = {} return memoize(function, cache_dict, 1) return decorator django-crispy-forms-1.6.0/crispy_forms/exceptions.py000066400000000000000000000005111264354352000226660ustar00rootroot00000000000000class CrispyError(Exception): pass class FormHelpersException(CrispyError): """ This is raised when building a form via helpers throws an error. We want to catch form helper errors as soon as possible because debugging templatetags is never fun. """ pass class DynamicError(CrispyError): pass django-crispy-forms-1.6.0/crispy_forms/helper.py000066400000000000000000000340011264354352000217650ustar00rootroot00000000000000# -*- coding: utf-8 -*- import re from django.core.urlresolvers import reverse, NoReverseMatch from django.utils.safestring import mark_safe from crispy_forms.compatibility import string_types from crispy_forms.layout import Layout from crispy_forms.layout_slice import LayoutSlice from crispy_forms.utils import render_field, flatatt, TEMPLATE_PACK, list_intersection, list_difference from crispy_forms.exceptions import FormHelpersException class DynamicLayoutHandler(object): def _check_layout(self): if self.layout is None: raise FormHelpersException("You need to set a layout in your FormHelper") def _check_layout_and_form(self): self._check_layout() if self.form is None: raise FormHelpersException("You need to pass a form instance to your FormHelper") def all(self): """ Returns all layout objects of first level of depth """ self._check_layout() return LayoutSlice(self.layout, slice(0, len(self.layout.fields), 1)) def filter(self, *LayoutClasses, **kwargs): """ Returns a LayoutSlice pointing to layout objects of type `LayoutClass` """ self._check_layout() max_level = kwargs.pop('max_level', 0) greedy = kwargs.pop('greedy', False) filtered_layout_objects = self.layout.get_layout_objects(LayoutClasses, max_level=max_level, greedy=greedy) return LayoutSlice(self.layout, filtered_layout_objects) def filter_by_widget(self, widget_type): """ Returns a LayoutSlice pointing to fields with widgets of `widget_type` """ self._check_layout_and_form() layout_field_names = self.layout.get_field_names() # Let's filter all fields with widgets like widget_type filtered_fields = [] for pointer in layout_field_names: if isinstance(self.form.fields[pointer[1]].widget, widget_type): filtered_fields.append(pointer) return LayoutSlice(self.layout, filtered_fields) def exclude_by_widget(self, widget_type): """ Returns a LayoutSlice pointing to fields with widgets NOT matching `widget_type` """ self._check_layout_and_form() layout_field_names = self.layout.get_field_names() # Let's exclude all fields with widgets like widget_type filtered_fields = [] for pointer in layout_field_names: if not isinstance(self.form.fields[pointer[1]].widget, widget_type): filtered_fields.append(pointer) return LayoutSlice(self.layout, filtered_fields) def __getitem__(self, key): """ Return a LayoutSlice that makes changes affect the current instance of the layout and not a copy. """ # when key is a string containing the field name if isinstance(key, string_types): # Django templates access FormHelper attributes using dictionary [] operator # This could be a helper['form_id'] access, not looking for a field if hasattr(self, key): return getattr(self, key) self._check_layout() layout_field_names = self.layout.get_field_names() filtered_field = [] for pointer in layout_field_names: # There can be an empty pointer if len(pointer) == 2 and pointer[1] == key: filtered_field.append(pointer) return LayoutSlice(self.layout, filtered_field) return LayoutSlice(self.layout, key) def __setitem__(self, key, value): self.layout[key] = value def __delitem__(self, key): del self.layout.fields[key] def __len__(self): if self.layout is not None: return len(self.layout.fields) else: return 0 class FormHelper(DynamicLayoutHandler): """ This class controls the form rendering behavior of the form passed to the `{% crispy %}` tag. For doing so you will need to set its attributes and pass the corresponding helper object to the tag:: {% crispy form form.helper %} Let's see what attributes you can set and what form behaviors they apply to: **form_method**: Specifies form method attribute. You can see it to 'POST' or 'GET'. Defaults to 'POST' **form_action**: 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:: url(r'^show/profile/$', 'show_my_profile_view', name = 'show_my_profile') - It can simply point to a URL '/whatever/blabla/'. **form_id**: Generates a form id for dom identification. If no id provided then no id attribute is created on the form. **form_class**: String containing separated CSS clases to be applied to form class attribute. The form will always have by default 'uniForm' class. **form_tag**: 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. **form_error_title**: If a form has `non_field_errors` to display, they are rendered in a div. You can set title's div with this attribute. Example: "Oooops!" or "Form Errors" **formset_error_title**: If a formset has `non_form_errors` to display, they are rendered in a div. You can set title's div with this attribute. **form_style**: Uni-form has two built in different form styles. You can choose your favorite. This can be set to "default" or "inline". Defaults to "default". **include_media**: Whether to automatically include form media. Set to False if you want to manually include form media outside the form. Defaults to True. Public Methods: **add_input(input)**: You can add input buttons using this method. Inputs added using this method will be rendered at the end of the form/formset. **add_layout(layout)**: You can add a `Layout` object to `FormHelper`. The Layout specifies in a simple, clean and DRY way how the form fields should be rendered. You can wrap fields, order them, customize pretty much anything in the form. Best way to add a helper to a form is adding a property named helper to the form that returns customized `FormHelper` object:: from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit class MyForm(forms.Form): title = forms.CharField(_("Title")) @property def helper(self): helper = FormHelper() helper.form_id = 'this-form-rocks' helper.form_class = 'search' helper.add_input(Submit('save', 'save')) [...] return helper You can use it in a template doing:: {% load crispy_forms_tags %} {% crispy form %} """ _form_method = 'post' _form_action = '' _form_style = 'default' form = None form_id = '' form_class = '' layout = None form_tag = True form_error_title = None formset_error_title = None form_show_errors = True render_unmentioned_fields = False render_hidden_fields = False render_required_fields = False _help_text_inline = False _error_text_inline = True html5_required = False form_show_labels = True template = None field_template = None disable_csrf = False label_class = '' field_class = '' include_media = True def __init__(self, form=None): self.attrs = {} self.inputs = [] if form is not None: self.form = form self.layout = self.build_default_layout(form) def build_default_layout(self, form): return Layout(*form.fields.keys()) @property def form_method(self): return self._form_method @form_method.setter def form_method(self, method): if method.lower() not in ('get', 'post'): raise FormHelpersException('Only GET and POST are valid in the \ form_method helper attribute') self._form_method = method.lower() @property def form_action(self): try: return reverse(self._form_action) except NoReverseMatch: return self._form_action @form_action.setter def form_action(self, action): self._form_action = action @property def form_style(self): if self._form_style == "default": return '' if self._form_style == "inline": return 'inlineLabels' @form_style.setter def form_style(self, style): if style.lower() not in ('default', 'inline'): raise FormHelpersException('Only default and inline are valid in the \ form_style helper attribute') self._form_style = style.lower() @property def help_text_inline(self): return self._help_text_inline @help_text_inline.setter def help_text_inline(self, flag): self._help_text_inline = flag self._error_text_inline = not flag @property def error_text_inline(self): return self._error_text_inline @error_text_inline.setter def error_text_inline(self, flag): self._error_text_inline = flag self._help_text_inline = not flag def add_input(self, input_object): self.inputs.append(input_object) def add_layout(self, layout): self.layout = layout def render_layout(self, form, context, template_pack=TEMPLATE_PACK): """ Returns safe html of the rendering of the layout """ form.rendered_fields = set() form.crispy_field_template = self.field_template # This renders the specified Layout strictly html = self.layout.render( form, self.form_style, context, template_pack=template_pack ) # Rendering some extra fields if specified if self.render_unmentioned_fields or self.render_hidden_fields or self.render_required_fields: fields = set(form.fields.keys()) left_fields_to_render = fields - form.rendered_fields for field in left_fields_to_render: if ( self.render_unmentioned_fields or self.render_hidden_fields and form.fields[field].widget.is_hidden or self.render_required_fields and form.fields[field].widget.is_required ): html += render_field( field, form, self.form_style, context, template_pack=template_pack ) # If the user has Meta.fields defined, not included in the layout, # we suppose they need to be rendered if hasattr(form, 'Meta'): if hasattr(form.Meta, 'fields'): current_fields = tuple(getattr(form, 'fields', {}).keys()) meta_fields = getattr(form.Meta, 'fields') fields_to_render = list_intersection(current_fields, meta_fields) left_fields_to_render = list_difference(fields_to_render, form.rendered_fields) for field in left_fields_to_render: html += render_field(field, form, self.form_style, context) return mark_safe(html) def get_attributes(self, template_pack=TEMPLATE_PACK): """ Used by crispy_forms_tags to get helper attributes """ items = { 'form_method': self.form_method.strip(), 'form_tag': self.form_tag, 'form_style': self.form_style.strip(), 'form_show_errors': self.form_show_errors, 'help_text_inline': self.help_text_inline, 'error_text_inline': self.error_text_inline, 'html5_required': self.html5_required, 'form_show_labels': self.form_show_labels, 'disable_csrf': self.disable_csrf, 'label_class': self.label_class, 'field_class': self.field_class, 'include_media': self.include_media } # col-[lg|md|sm|xs]- label_size_match = re.search('(\d+)', self.label_class) device_type_match = re.search('(lg|md|sm|xs)', self.label_class) if label_size_match and device_type_match: try: items['label_size'] = int(label_size_match.groups()[0]) items['bootstrap_device_type'] = device_type_match.groups()[0] except: pass items['attrs'] = {} if self.attrs: items['attrs'] = self.attrs.copy() if self.form_action: items['attrs']['action'] = self.form_action.strip() if self.form_id: items['attrs']['id'] = self.form_id.strip() if self.form_class: # uni_form TEMPLATE PACK has a uniForm class by default if template_pack == 'uni_form': items['attrs']['class'] = "uniForm %s" % self.form_class.strip() else: items['attrs']['class'] = self.form_class.strip() else: if template_pack == 'uni_form': items['attrs']['class'] = self.attrs.get('class', '') + " uniForm" items['flat_attrs'] = flatatt(items['attrs']) if self.inputs: items['inputs'] = self.inputs if self.form_error_title: items['form_error_title'] = self.form_error_title.strip() if self.formset_error_title: items['formset_error_title'] = self.formset_error_title.strip() for attribute_name, value in self.__dict__.items(): if attribute_name not in items and attribute_name not in ['layout', 'inputs'] and not attribute_name.startswith('_'): items[attribute_name] = value return items django-crispy-forms-1.6.0/crispy_forms/layout.py000066400000000000000000000371131264354352000220320ustar00rootroot00000000000000from __future__ import unicode_literals from django.template import Template from django.template.loader import render_to_string from django.utils.html import conditional_escape from crispy_forms.compatibility import string_types, text_type from crispy_forms.utils import render_field, flatatt, TEMPLATE_PACK, get_template_pack class TemplateNameMixin(object): def get_template_name(self, template_pack): if '%s' in self.template: template = self.template % template_pack else: template = self.template return template class LayoutObject(TemplateNameMixin): def __getitem__(self, slice): return self.fields[slice] def __setitem__(self, slice, value): self.fields[slice] = value def __delitem__(self, slice): del self.fields[slice] def __len__(self): return len(self.fields) def __getattr__(self, name): """ This allows us to access self.fields list methods like append or insert, without having to declare them one by one """ # Check necessary for unpickling, see #107 if 'fields' in self.__dict__ and hasattr(self.fields, name): return getattr(self.fields, name) else: return object.__getattribute__(self, name) def get_field_names(self, index=None): """ Returns a list of lists, those lists are named pointers. First parameter is the location of the field, second one the name of the field. Example:: [ [[0,1,2], 'field_name1'], [[0,3], 'field_name2'] ] """ return self.get_layout_objects(string_types, greedy=True) def get_layout_objects(self, *LayoutClasses, **kwargs): """ Returns a list of lists pointing to layout objects of any type matching `LayoutClasses`:: [ [[0,1,2], 'div'], [[0,3], 'field_name'] ] :param max_level: An integer that indicates max level depth to reach when traversing a layout. :param greedy: Boolean that indicates whether to be greedy. If set, max_level is skipped. """ index = kwargs.pop('index', None) max_level = kwargs.pop('max_level', 0) greedy = kwargs.pop('greedy', False) pointers = [] if index is not None and not isinstance(index, list): index = [index] elif index is None: index = [] for i, layout_object in enumerate(self.fields): if isinstance(layout_object, LayoutClasses): if len(LayoutClasses) == 1 and LayoutClasses[0] == string_types: pointers.append([index + [i], layout_object]) else: pointers.append([index + [i], layout_object.__class__.__name__.lower()]) # If it's a layout object and we haven't reached the max depth limit or greedy # we recursive call if hasattr(layout_object, 'get_field_names') and (len(index) < max_level or greedy): new_kwargs = {'index': index + [i], 'max_level': max_level, 'greedy': greedy} pointers = pointers + layout_object.get_layout_objects(*LayoutClasses, **new_kwargs) return pointers def get_rendered_fields(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return ''.join( render_field(field, form, form_style, context, template_pack=template_pack, **kwargs) for field in self.fields ) class Layout(LayoutObject): """ Form Layout. It is conformed by Layout objects: `Fieldset`, `Row`, `Column`, `MultiField`, `HTML`, `ButtonHolder`, `Button`, `Hidden`, `Reset`, `Submit` and fields. Form fields have to be strings. Layout objects `Fieldset`, `Row`, `Column`, `MultiField` and `ButtonHolder` can hold other Layout objects within. Though `ButtonHolder` should only hold `HTML` and BaseInput inherited classes: `Button`, `Hidden`, `Reset` and `Submit`. Example:: helper.layout = Layout( Fieldset('Company data', 'is_company' ), Fieldset(_('Contact details'), 'email', Row('password1', 'password2'), 'first_name', 'last_name', HTML(''), 'company' ), ButtonHolder( Submit('Save', 'Save', css_class='button white'), ), ) """ def __init__(self, *fields): self.fields = list(fields) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) class ButtonHolder(LayoutObject): """ Layout object. It wraps fields in a
This is where you should put Layout objects that render to form buttons like Submit. It should only hold `HTML` and `BaseInput` inherited objects. Example:: ButtonHolder( HTML(Information Saved), Submit('Save', 'Save') ) """ template = "%s/layout/buttonholder.html" def __init__(self, *fields, **kwargs): self.fields = list(fields) self.css_class = kwargs.get('css_class', None) self.css_id = kwargs.get('css_id', None) self.template = kwargs.get('template', self.template) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): html = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) template = self.get_template_name(template_pack) context.update({'buttonholder': self, 'fields_output': html}) return render_to_string(template, context.flatten()) class BaseInput(TemplateNameMixin): """ A base class to reduce the amount of code in the Input classes. """ template = "%s/layout/baseinput.html" def __init__(self, name, value, **kwargs): self.name = name self.value = value self.id = kwargs.pop('css_id', '') self.attrs = {} if 'css_class' in kwargs: self.field_classes += ' %s' % kwargs.pop('css_class') self.template = kwargs.pop('template', self.template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): """ Renders an `` if container is used as a Layout object. Input button value can be a variable in context. """ self.value = Template(text_type(self.value)).render(context) template = self.get_template_name(template_pack) context.update({'input': self}) return render_to_string(template, context.flatten()) class Submit(BaseInput): """ Used to create a Submit button descriptor for the {% crispy %} template tag:: submit = Submit('Search the Site', 'search this site') .. note:: The first argument is also slugified and turned into the id for the submit button. """ input_type = 'submit' def __init__(self, *args, **kwargs): self.field_classes = 'submit submitButton' if get_template_pack() == 'uni_form' else 'btn btn-primary' super(Submit, self).__init__(*args, **kwargs) class Button(BaseInput): """ Used to create a Submit input descriptor for the {% crispy %} template tag:: button = Button('Button 1', 'Press Me!') .. note:: The first argument is also slugified and turned into the id for the button. """ input_type = 'button' def __init__(self, *args, **kwargs): self.field_classes = 'button' if get_template_pack() == 'uni_form' else 'btn' super(Button, self).__init__(*args, **kwargs) class Hidden(BaseInput): """ Used to create a Hidden input descriptor for the {% crispy %} template tag. """ input_type = 'hidden' field_classes = 'hidden' class Reset(BaseInput): """ Used to create a Reset button input descriptor for the {% crispy %} template tag:: reset = Reset('Reset This Form', 'Revert Me!') .. note:: The first argument is also slugified and turned into the id for the reset. """ input_type = 'reset' def __init__(self, *args, **kwargs): self.field_classes = 'reset resetButton' if get_template_pack() == 'uni_form' else 'btn btn-inverse' super(Reset, self).__init__(*args, **kwargs) class Fieldset(LayoutObject): """ Layout object. It wraps fields in a
Example:: Fieldset("Text for the legend", 'form_field_1', 'form_field_2' ) The first parameter is the text for the fieldset legend. This text is context aware, so you can do things like:: Fieldset("Data for {{ user.username }}", 'form_field_1', 'form_field_2' ) """ template = "%s/layout/fieldset.html" def __init__(self, legend, *fields, **kwargs): self.fields = list(fields) self.legend = legend self.css_class = kwargs.pop('css_class', '') self.css_id = kwargs.pop('css_id', None) self.template = kwargs.pop('template', self.template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): fields = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) legend = '' if self.legend: legend = '%s' % Template(text_type(self.legend)).render(context) template = self.get_template_name(template_pack) return render_to_string( template, {'fieldset': self, 'legend': legend, 'fields': fields, 'form_style': form_style} ) class MultiField(LayoutObject): """ MultiField container. Renders to a MultiField
""" template = "%s/layout/multifield.html" field_template = "%s/multifield.html" def __init__(self, label, *fields, **kwargs): self.fields = list(fields) self.label_html = label self.label_class = kwargs.pop('label_class', 'blockLabel') self.css_class = kwargs.pop('css_class', 'ctrlHolder') self.css_id = kwargs.pop('css_id', None) self.template = kwargs.pop('template', self.template) self.field_template = kwargs.pop('field_template', self.field_template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): # If a field within MultiField contains errors if context['form_show_errors']: for field in map(lambda pointer: pointer[1], self.get_field_names()): if field in form.errors: self.css_class += " error" field_template = self.field_template % template_pack fields_output = self.get_rendered_fields( form, form_style, context, template_pack, template=field_template, labelclass=self.label_class, layout_object=self, **kwargs ) template = self.get_template_name(template_pack) context.update({ 'multifield': self, 'fields_output': fields_output }) return render_to_string(template, context.flatten()) class Div(LayoutObject): """ Layout object. It wraps fields in a
You can set `css_id` for a DOM id and `css_class` for a DOM class. Example:: Div('form_field_1', 'form_field_2', css_id='div-example', css_class='divs') """ template = "%s/layout/div.html" def __init__(self, *fields, **kwargs): self.fields = list(fields) if hasattr(self, 'css_class') and 'css_class' in kwargs: self.css_class += ' %s' % kwargs.pop('css_class') if not hasattr(self, 'css_class'): self.css_class = kwargs.pop('css_class', None) self.css_id = kwargs.pop('css_id', '') self.template = kwargs.pop('template', self.template) self.flat_attrs = flatatt(kwargs) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): fields = self.get_rendered_fields(form, form_style, context, template_pack, **kwargs) template = self.get_template_name(template_pack) return render_to_string(template, {'div': self, 'fields': fields}) class Row(Div): """ Layout object. It wraps fields in a div whose default class is "formRow". Example:: Row('form_field_1', 'form_field_2', 'form_field_3') """ def __init__(self, *args, **kwargs): self.css_class = 'formRow' if get_template_pack() == 'uni_form' else 'row' super(Row, self).__init__(*args, **kwargs) class Column(Div): """ Layout object. It wraps fields in a div whose default class is "formColumn". Example:: Column('form_field_1', 'form_field_2') """ css_class = 'formColumn' class HTML(object): """ Layout object. It can contain pure HTML and it has access to the whole context of the page where the form is being rendered. Examples:: HTML("{% if saved %}Data saved{% endif %}") HTML('') """ def __init__(self, html): self.html = html def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs): return Template(text_type(self.html)).render(context) class Field(LayoutObject): """ Layout object, It contains one field name, and you can add attributes to it easily. For setting class attributes, you need to use `css_class`, as `class` is a Python keyword. Example:: Field('field_name', style="color: #333;", css_class="whatever", id="field_name") """ template = "%s/field.html" def __init__(self, *args, **kwargs): self.fields = list(args) if not hasattr(self, 'attrs'): self.attrs = {} if 'css_class' in kwargs: if 'class' in self.attrs: self.attrs['class'] += " %s" % kwargs.pop('css_class') else: self.attrs['class'] = kwargs.pop('css_class') self.wrapper_class = kwargs.pop('wrapper_class', None) self.template = kwargs.pop('template', self.template) # We use kwargs as HTML attributes, turning data_id='test' into data-id='test' self.attrs.update(dict([(k.replace('_', '-'), conditional_escape(v)) for k, v in kwargs.items()])) def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, extra_context=None, **kwargs): if extra_context is None: extra_context = {} if hasattr(self, 'wrapper_class'): extra_context['wrapper_class'] = self.wrapper_class template = self.get_template_name(template_pack) return self.get_rendered_fields( form, form_style, context, template_pack, template=template, attrs=self.attrs, extra_context=extra_context, **kwargs ) class MultiWidgetField(Field): """ Layout object. For fields with :class:`~django.forms.MultiWidget` as `widget`, you can pass additional attributes to each widget. Example:: MultiWidgetField( 'multiwidget_field_name', attrs=( {'style': 'width: 30px;'}, {'class': 'second_widget_class'} ), ) .. note:: To override widget's css class use ``class`` not ``css_class``. """ def __init__(self, *args, **kwargs): self.fields = list(args) self.attrs = kwargs.pop('attrs', {}) self.template = kwargs.pop('template', self.template) django-crispy-forms-1.6.0/crispy_forms/layout_slice.py000066400000000000000000000140401264354352000232030ustar00rootroot00000000000000# -*- coding: utf-8 -*- from crispy_forms.compatibility import integer_types, string_types from crispy_forms.exceptions import DynamicError from crispy_forms.layout import Fieldset, MultiField from crispy_forms.bootstrap import Container class LayoutSlice(object): # List of layout objects that need args passed first before fields args_first = (Fieldset, MultiField, Container) def __init__(self, layout, key): self.layout = layout if isinstance(key, integer_types): self.slice = slice(key, key + 1, 1) else: self.slice = key def wrapped_object(self, LayoutClass, fields, *args, **kwargs): """ Returns a layout object of type `LayoutClass` with `args` and `kwargs` that wraps `fields` inside. """ if args: if isinstance(fields, list): fields = tuple(fields) else: fields = (fields,) if LayoutClass in self.args_first: arguments = args + fields else: arguments = fields + args return LayoutClass(*arguments, **kwargs) else: if isinstance(fields, list): return LayoutClass(*fields, **kwargs) else: return LayoutClass(fields, **kwargs) def pre_map(self, function): """ Iterates over layout objects pointed in `self.slice` executing `function` on them. It passes `function` penultimate layout object and the position where to find last one """ if isinstance(self.slice, slice): for i in range(*self.slice.indices(len(self.layout.fields))): function(self.layout, i) elif isinstance(self.slice, list): # A list of pointers Ex: [[[0, 0], 'div'], [[0, 2, 3], 'field_name']] for pointer in self.slice: position = pointer[0] # If it's pointing first level if len(position) == 1: function(self.layout, position[-1]) else: layout_object = self.layout.fields[position[0]] for i in position[1:-1]: layout_object = layout_object.fields[i] try: function(layout_object, position[-1]) except IndexError: # We could avoid this exception, recalculating pointers. # However this case is most of the time an undesired behavior raise DynamicError("Trying to wrap a field within an already wrapped field, \ recheck your filter or layout") def wrap(self, LayoutClass, *args, **kwargs): """ Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with `args` and `kwargs` passed. """ def wrap_object(layout_object, j): layout_object.fields[j] = self.wrapped_object( LayoutClass, layout_object.fields[j], *args, **kwargs ) self.pre_map(wrap_object) def wrap_once(self, LayoutClass, *args, **kwargs): """ Wraps every layout object pointed in `self.slice` under a `LayoutClass` instance with `args` and `kwargs` passed, unless layout object's parent is already a subclass of `LayoutClass`. """ def wrap_object_once(layout_object, j): if not isinstance(layout_object, LayoutClass): layout_object.fields[j] = self.wrapped_object( LayoutClass, layout_object.fields[j], *args, **kwargs ) self.pre_map(wrap_object_once) def wrap_together(self, LayoutClass, *args, **kwargs): """ Wraps all layout objects pointed in `self.slice` together under a `LayoutClass` instance with `args` and `kwargs` passed. """ if isinstance(self.slice, slice): # The start of the slice is replaced start = self.slice.start if self.slice.start is not None else 0 self.layout.fields[start] = self.wrapped_object( LayoutClass, self.layout.fields[self.slice], *args, **kwargs ) # The rest of places of the slice are removed, as they are included in the previous for i in reversed(range(*self.slice.indices(len(self.layout.fields)))): if i != start: del self.layout.fields[i] elif isinstance(self.slice, list): raise DynamicError("wrap_together doesn't work with filter, only with [] operator") def map(self, function): """ Iterates over layout objects pointed in `self.slice` executing `function` on them It passes `function` last layout object """ if isinstance(self.slice, slice): for i in range(*self.slice.indices(len(self.layout.fields))): function(self.layout.fields[i]) elif isinstance(self.slice, list): # A list of pointers Ex: [[[0, 0], 'div'], [[0, 2, 3], 'field_name']] for pointer in self.slice: position = pointer[0] layout_object = self.layout.fields[position[0]] for i in position[1:]: previous_layout_object = layout_object layout_object = layout_object.fields[i] # If update_attrs is applied to a string, we call to its wrapping layout object if ( function.__name__ == 'update_attrs' and isinstance(layout_object, string_types) ): function(previous_layout_object) else: function(layout_object) def update_attributes(self, **kwargs): """ Updates attributes of every layout object pointed in `self.slice` using kwargs """ def update_attrs(layout_object): if hasattr(layout_object, 'attrs'): layout_object.attrs.update(kwargs) self.map(update_attrs) django-crispy-forms-1.6.0/crispy_forms/models.py000066400000000000000000000000001264354352000217610ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/000077500000000000000000000000001264354352000221345ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/000077500000000000000000000000001264354352000241515ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/accordion-group.html000066400000000000000000000006401264354352000301320ustar00rootroot00000000000000
{{ fields|safe }}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/accordion.html000066400000000000000000000001221264354352000267730ustar00rootroot00000000000000
{{ content|safe }}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/betterform.html000066400000000000000000000012331264354352000272070ustar00rootroot00000000000000{% for fieldset in form.fieldsets %}
{% if fieldset.legend %} {{ fieldset.legend }} {% endif %} {% if fieldset.description %}

{{ fieldset.description }}

{% endif %} {% for field in fieldset %} {% if field.is_hidden %} {{ field }} {% else %} {% include "bootstrap/field.html" %} {% endif %} {% endfor %} {% if not forloop.last or not fieldset_open %}
{% endif %} {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/display_form.html000066400000000000000000000004061264354352000275270ustar00rootroot00000000000000{% if form.form_html %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap/errors.html" %} {% endif %} {{ form.form_html }} {% else %} {% include "bootstrap/uni_form.html" %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/errors.html000066400000000000000000000004321264354352000263520ustar00rootroot00000000000000{% if form.non_field_errors %}
{% if form_error_title %}

{{ form_error_title }}

{% endif %}
    {{ form.non_field_errors|unordered_list }}
{% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/errors_formset.html000066400000000000000000000004451264354352000301150ustar00rootroot00000000000000{% if formset.non_form_errors %}
{% if formset_error_title %}

{{ formset_error_title }}

{% endif %}
    {{ formset.non_form_errors|unordered_list }}
{% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/field.html000066400000000000000000000034071264354352000261260ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} <{% 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 %}"> {% if field.label and not field|is_checkbox and form_show_labels %} {% endif %} {% if field|is_checkboxselectmultiple %} {% include 'bootstrap/layout/checkboxselectmultiple.html' %} {% endif %} {% if field|is_radioselect %} {% include 'bootstrap/layout/radioselect.html' %} {% endif %} {% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
{% if field|is_checkbox and form_show_labels %} {% else %} {% crispy_field field %} {% include 'bootstrap/layout/help_text_and_errors.html' %} {% endif %}
{% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/000077500000000000000000000000001264354352000254665ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/alert.html000066400000000000000000000004051264354352000274620ustar00rootroot00000000000000 {% if dismiss %}{% endif %} {{ content|safe }}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/baseinput.html000066400000000000000000000006541264354352000303530ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/button.html000066400000000000000000000001101264354352000276570ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/checkboxselectmultiple.html000066400000000000000000000014251264354352000331200ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
{% include 'bootstrap/layout/field_errors_block.html' %} {% for choice in field.field.choices %} {% endfor %} {% include 'bootstrap/layout/help_text.html' %}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/checkboxselectmultiple_inline.html000066400000000000000000000011731264354352000344560ustar00rootroot00000000000000{% if field.is_hidden %} {{ field }} {% else %}
{% if field.label %} {% endif %} {% include 'bootstrap/layout/checkboxselectmultiple.html' %}
{% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/div.html000066400000000000000000000002671264354352000271430ustar00rootroot00000000000000
{{ fields|safe }}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/field_errors.html000066400000000000000000000003471264354352000310370ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %} {{ error }} {% endfor %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/field_errors_block.html000066400000000000000000000003401264354352000322020ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %}

{{ error }}

{% endfor %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/field_with_buttons.html000066400000000000000000000015261264354352000322540ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.label and form_show_labels %} {% endif %}
{% crispy_field field %} {{ buttons|safe }}
{% include 'bootstrap/layout/help_text_and_errors.html' %}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/fieldset.html000066400000000000000000000004731264354352000301570ustar00rootroot00000000000000
{% if legend %}{{ legend|safe }}{% endif %} {{ fields|safe }}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/formactions.html000066400000000000000000000002061264354352000306760ustar00rootroot00000000000000 {{ fields_output|safe }}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/help_text.html000066400000000000000000000004371264354352000303540ustar00rootroot00000000000000{% if field.help_text %} {% if help_text_inline %} {{ field.help_text|safe }} {% else %}

{{ field.help_text|safe }}

{% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/help_text_and_errors.html000066400000000000000000000005721264354352000325720ustar00rootroot00000000000000{% if help_text_inline and not error_text_inline %} {% include 'bootstrap/layout/help_text.html' %} {% endif %} {% if error_text_inline %} {% include 'bootstrap/layout/field_errors.html' %} {% else %} {% include 'bootstrap/layout/field_errors_block.html' %} {% endif %} {% if not help_text_inline %} {% include 'bootstrap/layout/help_text.html' %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/multifield.html000066400000000000000000000010251264354352000305100ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field.label %} {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/prepended_appended_text.html000066400000000000000000000024401264354352000332260ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
{% if field.label and form_show_labels %} {% endif %}
{% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} {% crispy_field field %} {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %}
{% include 'bootstrap/layout/help_text_and_errors.html' %}
{% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/radioselect.html000066400000000000000000000013101264354352000306450ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
{% include 'bootstrap/layout/field_errors_block.html' %} {% for choice in field.field.choices %} {% endfor %} {% include 'bootstrap/layout/help_text.html' %}
django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/radioselect_inline.html000066400000000000000000000011601264354352000322060ustar00rootroot00000000000000{% if field.is_hidden %} {{ field }} {% else %}
{% if field.label %} {% endif %} {% include 'bootstrap/layout/radioselect.html' %}
{% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/tab-link.html000066400000000000000000000002721264354352000300560ustar00rootroot00000000000000
  • {{ link.name|capfirst }}{% if tab.errors %}!{% endif %}
  • django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/tab.html000066400000000000000000000002431264354352000271210ustar00rootroot00000000000000 {{ links|safe }}
    {{ content|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/layout/uneditable_input.html000066400000000000000000000010601264354352000317040ustar00rootroot00000000000000
    {{ field.label|safe }}{% if field.field.required %}*{% endif %}
    {% if field.value %}{{ field.value }}{% endif %} {% include 'bootstrap/layout/help_text.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/table_inline_formset.html000066400000000000000000000040021264354352000312170ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% load crispy_forms_field %} {% specialspaceless %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% if formset.readonly and not formset.queryset.exists %} {% else %} {% for field in formset.forms.0 %} {% if field.label and not field.is_hidden %} {{ field.label|safe }}{% if field.field.required and not field|is_checkbox %}*{% endif %} {% endif %} {% endfor %} {% endif %} {% for form in formset %} {% if form_show_errors and not form.is_extra %} {% include "bootstrap/errors.html" %} {% endif %} {% for field in form %} {% include 'bootstrap/field.html' with tag="th" form_show_labels=False %} {% endfor %} {% endfor %} {% if inputs %}
    {% for input in inputs %} {% include "bootstrap/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if formset_tag %}{% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/uni_form.html000066400000000000000000000004741264354352000266620ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap/errors.html" %} {% endif %} {% for field in form %} {% include "bootstrap/field.html" %} {% endfor %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/uni_formset.html000066400000000000000000000003441264354352000273720ustar00rootroot00000000000000{% with formset.management_form as form %} {% include 'bootstrap/uni_form.html' %} {% endwith %} {% for form in formset %}
    {% include 'bootstrap/uni_form.html' %}
    {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/whole_uni_form.html000066400000000000000000000011721264354352000300540ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if form_tag %}
    {% endif %} {% if form_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %} {% include "bootstrap/display_form.html" %} {% if inputs %}
    {% for input in inputs %} {% include "bootstrap/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if form_tag %}
    {% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap/whole_uni_formset.html000066400000000000000000000015161264354352000305720ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% specialspaceless %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% include "bootstrap/errors_formset.html" %} {% for form in formset %} {% include "bootstrap/display_form.html" %} {% endfor %} {% if inputs %}
    {% for input in inputs %} {% include "bootstrap/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if formset_tag %}
    {% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/000077500000000000000000000000001264354352000242345ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/accordion-group.html000066400000000000000000000007161264354352000302210ustar00rootroot00000000000000
    {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/accordion.html000066400000000000000000000001241264354352000270600ustar00rootroot00000000000000
    {{ content|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/betterform.html000066400000000000000000000012341264354352000272730ustar00rootroot00000000000000{% for fieldset in form.fieldsets %}
    {% if fieldset.legend %} {{ fieldset.legend }} {% endif %} {% if fieldset.description %}

    {{ fieldset.description }}

    {% endif %} {% for field in fieldset %} {% if field.is_hidden %} {{ field }} {% else %} {% include "bootstrap3/field.html" %} {% endif %} {% endfor %} {% if not forloop.last or not fieldset_open %}
    {% endif %} {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/display_form.html000066400000000000000000000004101264354352000276050ustar00rootroot00000000000000{% if form.form_html %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap3/errors.html" %} {% endif %} {{ form.form_html }} {% else %} {% include "bootstrap3/uni_form.html" %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/errors.html000066400000000000000000000004331264354352000264360ustar00rootroot00000000000000{% if form.non_field_errors %}
    {% if form_error_title %}

    {{ form_error_title }}

    {% endif %}
      {{ form.non_field_errors|unordered_list }}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/errors_formset.html000066400000000000000000000004461264354352000302010ustar00rootroot00000000000000{% if formset.non_form_errors %}
    {% if formset_error_title %}

    {{ formset_error_title }}

    {% endif %}
      {{ formset.non_form_errors|unordered_list }}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/field.html000066400000000000000000000043021264354352000262040ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox %}
    {% if label_class %}
    {% endif %} {% endif %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-error{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> {% if field.label and not field|is_checkbox and form_show_labels %} {% endif %} {% if field|is_checkboxselectmultiple %} {% include 'bootstrap3/layout/checkboxselectmultiple.html' %} {% endif %} {% if field|is_radioselect %} {% include 'bootstrap3/layout/radioselect.html' %} {% endif %} {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} {% if field|is_checkbox and form_show_labels %} {% else %}
    {% crispy_field field %} {% include 'bootstrap3/layout/help_text_and_errors.html' %}
    {% endif %} {% endif %} {% if field|is_checkbox %} {% if label_class %}
    {% endif %}
    {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/inputs.html000066400000000000000000000005651264354352000264520ustar00rootroot00000000000000{% if inputs %}
    {% if label_class %}
    {% endif %}
    {% for input in inputs %} {% include "bootstrap3/layout/baseinput.html" %} {% endfor %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/000077500000000000000000000000001264354352000255515ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/alert.html000066400000000000000000000004051264354352000275450ustar00rootroot00000000000000 {% if dismiss %}{% endif %} {{ content|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/baseinput.html000066400000000000000000000006541264354352000304360ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/button.html000066400000000000000000000001101264354352000277420ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/buttonholder.html000066400000000000000000000003241264354352000311470ustar00rootroot00000000000000
    {{ fields_output|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/checkboxselectmultiple.html000066400000000000000000000016341264354352000332050ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
    {% include 'bootstrap3/layout/field_errors_block.html' %} {% for choice in field.field.choices %} {% if not inline_class %}
    {% endif %} {% if not inline_class %}
    {% endif %} {% endfor %} {% include 'bootstrap3/layout/help_text.html' %}
    checkboxselectmultiple_inline.html000066400000000000000000000012171264354352000344610ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout{% if field.is_hidden %} {{ field }} {% else %}
    {% if field.label %} {% endif %} {% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/div.html000066400000000000000000000002671264354352000272260ustar00rootroot00000000000000
    {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/field_errors.html000066400000000000000000000003461264354352000311210ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %} {{ error }} {% endfor %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/field_errors_block.html000066400000000000000000000003401264354352000322650ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %}

    {{ error }}

    {% endfor %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/field_with_buttons.html000066400000000000000000000017601264354352000323370ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.label and form_show_labels %} {% endif %}
    {% crispy_field field %} {{ buttons|safe }}
    {% include 'bootstrap3/layout/help_text_and_errors.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/fieldset.html000066400000000000000000000004731264354352000302420ustar00rootroot00000000000000
    {% if legend %}{{ legend|safe }}{% endif %} {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/formactions.html000066400000000000000000000004451264354352000307660ustar00rootroot00000000000000 {% if label_class %}
    {% endif %}
    {{ fields_output|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/help_text.html000066400000000000000000000004361264354352000304360ustar00rootroot00000000000000{% if field.help_text %} {% if help_text_inline %} {{ field.help_text|safe }} {% else %}

    {{ field.help_text|safe }}

    {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/help_text_and_errors.html000066400000000000000000000005761264354352000326610ustar00rootroot00000000000000{% if help_text_inline and not error_text_inline %} {% include 'bootstrap3/layout/help_text.html' %} {% endif %} {% if error_text_inline %} {% include 'bootstrap3/layout/field_errors.html' %} {% else %} {% include 'bootstrap3/layout/field_errors_block.html' %} {% endif %} {% if not help_text_inline %} {% include 'bootstrap3/layout/help_text.html' %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/inline_field.html000066400000000000000000000014231264354352000310600ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox %}
    {% else %}
    {% crispy_field field 'placeholder' field.label %}
    {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/multifield.html000066400000000000000000000010251264354352000305730ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field.label %} {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/prepended_appended_text.html000066400000000000000000000034571264354352000333220ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
    {% if field.label and form_show_labels %} {% endif %}
    {% if field|is_select %} {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} {% crispy_field field %} {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %} {% else %}
    {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} {% crispy_field field %} {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %}
    {% endif %} {% include 'bootstrap3/layout/help_text_and_errors.html' %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/radioselect.html000066400000000000000000000015131264354352000307350ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
    {% include 'bootstrap3/layout/field_errors_block.html' %} {% for choice in field.field.choices %} {% if not inline_class %}
    {% endif %} {% if not inline_class %}
    {% endif %} {% endfor %} {% include 'bootstrap3/layout/help_text.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/radioselect_inline.html000066400000000000000000000012711264354352000322740ustar00rootroot00000000000000{% if field.is_hidden %} {{ field }} {% else %}
    {% if field.label %} {% endif %} {% include 'bootstrap3/layout/radioselect.html' %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/tab-link.html000066400000000000000000000002721264354352000301410ustar00rootroot00000000000000
  • {{ link.name|capfirst }}{% if tab.errors %}!{% endif %}
  • django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/tab.html000066400000000000000000000002561264354352000272100ustar00rootroot00000000000000 {{ links|safe }}
    {{ content|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/layout/uneditable_input.html000066400000000000000000000011211264354352000317650ustar00rootroot00000000000000{% load crispy_forms_field %}
    {% crispy_field field 'disabled' 'disabled' %} {% include 'bootstrap3/layout/help_text.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/table_inline_formset.html000066400000000000000000000035011264354352000313050ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% load crispy_forms_field %} {% specialspaceless %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% if formset.readonly and not formset.queryset.exists %} {% else %} {% for field in formset.forms.0 %} {% if field.label and not field|is_checkbox and not field.is_hidden %} {{ field.label|safe }}{% if field.field.required %}*{% endif %} {% endif %} {% endfor %} {% endif %} {% for form in formset %} {% if form_show_errors and not form.is_extra %} {% include "bootstrap3/errors.html" %} {% endif %} {% for field in form %} {% include 'bootstrap3/field.html' with tag="th" form_show_labels=False %} {% endfor %} {% endfor %} {% include "bootstrap3/inputs.html" %} {% if formset_tag %}{% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/uni_form.html000066400000000000000000000004761264354352000267470ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap3/errors.html" %} {% endif %} {% for field in form %} {% include "bootstrap3/field.html" %} {% endfor %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/uni_formset.html000066400000000000000000000003461264354352000274570ustar00rootroot00000000000000{% with formset.management_form as form %} {% include 'bootstrap3/uni_form.html' %} {% endwith %} {% for form in formset %}
    {% include 'bootstrap3/uni_form.html' %}
    {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/whole_uni_form.html000066400000000000000000000007221264354352000301370ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if form_tag %}
    {% endif %} {% if form_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %} {% include "bootstrap3/display_form.html" %} {% include "bootstrap3/inputs.html" %} {% if form_tag %}
    {% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap3/whole_uni_formset.html000066400000000000000000000015211264354352000306510ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% specialspaceless %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% include "bootstrap3/errors_formset.html" %} {% for form in formset %} {% include "bootstrap3/display_form.html" %} {% endfor %} {% if inputs %}
    {% for input in inputs %} {% include "bootstrap3/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if formset_tag %}
    {% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/000077500000000000000000000000001264354352000242355ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/accordion-group.html000066400000000000000000000007161264354352000302220ustar00rootroot00000000000000
    {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/accordion.html000066400000000000000000000001241264354352000270610ustar00rootroot00000000000000
    {{ content|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/betterform.html000066400000000000000000000012341264354352000272740ustar00rootroot00000000000000{% for fieldset in form.fieldsets %}
    {% if fieldset.legend %} {{ fieldset.legend }} {% endif %} {% if fieldset.description %}

    {{ fieldset.description }}

    {% endif %} {% for field in fieldset %} {% if field.is_hidden %} {{ field }} {% else %} {% include "bootstrap4/field.html" %} {% endif %} {% endfor %} {% if not forloop.last or not fieldset_open %}
    {% endif %} {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/display_form.html000066400000000000000000000004101264354352000276060ustar00rootroot00000000000000{% if form.form_html %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap4/errors.html" %} {% endif %} {{ form.form_html }} {% else %} {% include "bootstrap4/uni_form.html" %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/errors.html000066400000000000000000000004331264354352000264370ustar00rootroot00000000000000{% if form.non_field_errors %}
    {% if form_error_title %}

    {{ form_error_title }}

    {% endif %}
      {{ form.non_field_errors|unordered_list }}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/errors_formset.html000066400000000000000000000004461264354352000302020ustar00rootroot00000000000000{% if formset.non_form_errors %}
    {% if formset_error_title %}

    {{ formset_error_title }}

    {% endif %}
      {{ formset.non_form_errors|unordered_list }}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/field.html000066400000000000000000000043121264354352000262060ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox %}
    {% if label_class %}
    {% endif %} {% endif %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group row{% 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 %}"> {% if field.label and not field|is_checkbox and form_show_labels %} {% endif %} {% if field|is_checkboxselectmultiple %} {% include 'bootstrap4/layout/checkboxselectmultiple.html' %} {% endif %} {% if field|is_radioselect %} {% include 'bootstrap4/layout/radioselect.html' %} {% endif %} {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} {% if field|is_checkbox and form_show_labels %} {% else %}
    {% crispy_field field %} {% include 'bootstrap4/layout/help_text_and_errors.html' %}
    {% endif %} {% endif %} {% if field|is_checkbox %} {% if label_class %}
    {% endif %}
    {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/inputs.html000066400000000000000000000005711264354352000264500ustar00rootroot00000000000000{% if inputs %}
    {% if label_class %}
    {% endif %}
    {% for input in inputs %} {% include "bootstrap4/layout/baseinput.html" %} {% endfor %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/000077500000000000000000000000001264354352000255525ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/alert.html000066400000000000000000000004051264354352000275460ustar00rootroot00000000000000 {% if dismiss %}{% endif %} {{ content|safe }} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/baseinput.html000066400000000000000000000006541264354352000304370ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/button.html000066400000000000000000000001101264354352000277430ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/buttonholder.html000066400000000000000000000003241264354352000311500ustar00rootroot00000000000000
    {{ fields_output|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/checkboxselectmultiple.html000066400000000000000000000016511264354352000332050ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
    {% include 'bootstrap4/layout/field_errors_block.html' %} {% for choice in field.field.choices %} {% if not inline_class %}
    {% endif %} {% if not inline_class %}
    {% endif %} {% endfor %} {% include 'bootstrap4/layout/help_text.html' %}
    checkboxselectmultiple_inline.html000066400000000000000000000012231264354352000344570ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout{% if field.is_hidden %} {{ field }} {% else %}
    {% if field.label %} {% endif %} {% include 'bootstrap4/layout/checkboxselectmultiple.html' %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/div.html000066400000000000000000000002671264354352000272270ustar00rootroot00000000000000
    {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/field_errors.html000066400000000000000000000003461264354352000311220ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %} {{ error }} {% endfor %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/field_errors_block.html000066400000000000000000000003401264354352000322660ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %}

    {{ error }}

    {% endfor %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/field_with_buttons.html000066400000000000000000000017641264354352000323440ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.label and form_show_labels %} {% endif %}
    {% crispy_field field %} {{ buttons|safe }}
    {% include 'bootstrap4/layout/help_text_and_errors.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/fieldset.html000066400000000000000000000004731264354352000302430ustar00rootroot00000000000000
    {% if legend %}{{ legend|safe }}{% endif %} {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/formactions.html000066400000000000000000000004511264354352000307640ustar00rootroot00000000000000 {% if label_class %}
    {% endif %}
    {{ fields_output|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/help_text.html000066400000000000000000000004361264354352000304370ustar00rootroot00000000000000{% if field.help_text %} {% if help_text_inline %} {{ field.help_text|safe }} {% else %}

    {{ field.help_text|safe }}

    {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/help_text_and_errors.html000066400000000000000000000005761264354352000326620ustar00rootroot00000000000000{% if help_text_inline and not error_text_inline %} {% include 'bootstrap4/layout/help_text.html' %} {% endif %} {% if error_text_inline %} {% include 'bootstrap4/layout/field_errors.html' %} {% else %} {% include 'bootstrap4/layout/field_errors_block.html' %} {% endif %} {% if not help_text_inline %} {% include 'bootstrap4/layout/help_text.html' %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/inline_field.html000066400000000000000000000014231264354352000310610ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox %}
    {% else %}
    {% crispy_field field 'placeholder' field.label %}
    {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/multifield.html000066400000000000000000000010251264354352000305740ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field.label %} {% endif %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/prepended_appended_text.html000066400000000000000000000034631264354352000333200ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
    {% if field.label and form_show_labels %} {% endif %}
    {% if field|is_select %} {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} {% crispy_field field %} {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %} {% else %}
    {% if crispy_prepended_text %}{{ crispy_prepended_text|safe }}{% endif %} {% crispy_field field %} {% if crispy_appended_text %}{{ crispy_appended_text|safe }}{% endif %}
    {% endif %} {% include 'bootstrap4/layout/help_text_and_errors.html' %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/radioselect.html000066400000000000000000000015301264354352000307350ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
    {% include 'bootstrap4/layout/field_errors_block.html' %} {% for choice in field.field.choices %} {% if not inline_class %}
    {% endif %} {% if not inline_class %}
    {% endif %} {% endfor %} {% include 'bootstrap4/layout/help_text.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/radioselect_inline.html000066400000000000000000000012751264354352000323010ustar00rootroot00000000000000{% if field.is_hidden %} {{ field }} {% else %}
    {% if field.label %} {% endif %} {% include 'bootstrap4/layout/radioselect.html' %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/tab-link.html000066400000000000000000000002721264354352000301420ustar00rootroot00000000000000
  • {{ link.name|capfirst }}{% if tab.errors %}!{% endif %}
  • django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/tab.html000066400000000000000000000002561264354352000272110ustar00rootroot00000000000000 {{ links|safe }}
    {{ content|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/layout/uneditable_input.html000066400000000000000000000011251264354352000317720ustar00rootroot00000000000000{% load crispy_forms_field %}
    {% crispy_field field 'disabled' 'disabled' %} {% include 'bootstrap4/layout/help_text.html' %}
    django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/table_inline_formset.html000066400000000000000000000034721264354352000313150ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% load crispy_forms_field %} {% specialspaceless %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% if formset.readonly and not formset.queryset.exists %} {% else %} {% for field in formset.forms.0 %} {% if field.label and not field|is_checkbox and not field.is_hidden %} {{ field.label|safe }}{% if field.field.required %}*{% endif %} {% endif %} {% endfor %} {% endif %} {% for form in formset %} {% if form_show_errors and not form.is_extra %} {% include "bootstrap4/errors.html" %} {% endif %} {% for field in form %} {% include 'bootstrap4/field.html' with tag="th" form_show_labels=False %} {% endfor %} {% endfor %} {% include "bootstrap4/inputs.html" %} {% if formset_tag %}{% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/uni_form.html000066400000000000000000000004761264354352000267500ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap4/errors.html" %} {% endif %} {% for field in form %} {% include "bootstrap4/field.html" %} {% endfor %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/uni_formset.html000066400000000000000000000003461264354352000274600ustar00rootroot00000000000000{% with formset.management_form as form %} {% include 'bootstrap4/uni_form.html' %} {% endwith %} {% for form in formset %}
    {% include 'bootstrap4/uni_form.html' %}
    {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/whole_uni_form.html000066400000000000000000000007221264354352000301400ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if form_tag %}
    {% endif %} {% if form_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %} {% include "bootstrap4/display_form.html" %} {% include "bootstrap4/inputs.html" %} {% if form_tag %}
    {% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/bootstrap4/whole_uni_formset.html000066400000000000000000000015211264354352000306520ustar00rootroot00000000000000{% load crispy_forms_tags %} {% load crispy_forms_utils %} {% specialspaceless %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% include "bootstrap4/errors_formset.html" %} {% for form in formset %} {% include "bootstrap4/display_form.html" %} {% endfor %} {% if inputs %}
    {% for input in inputs %} {% include "bootstrap4/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if formset_tag %}
    {% endif %} {% endspecialspaceless %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/000077500000000000000000000000001264354352000237525ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/betterform.html000066400000000000000000000012321264354352000270070ustar00rootroot00000000000000{% for fieldset in form.fieldsets %}
    {% if fieldset.legend %} {{ fieldset.legend }} {% endif %} {% if fieldset.description %}

    {{ fieldset.description }}

    {% endif %} {% for field in fieldset %} {% if field.is_hidden %} {{ field }} {% else %} {% include "uni_form/field.html" %} {% endif %} {% endfor %} {% if not forloop.last or not fieldset_open %}
    {% endif %} {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/display_form.html000066400000000000000000000004041264354352000273260ustar00rootroot00000000000000{% if form.form_html %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "uni_form/errors.html" %} {% endif %} {{ form.form_html }} {% else %} {% include "uni_form/uni_form.html" %} {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/errors.html000066400000000000000000000003741264354352000261600ustar00rootroot00000000000000{% if form.errors and form.non_field_errors %}
    {% if form_error_title %}

    {{ form_error_title }}

    {% endif %}
      {{ form.non_field_errors|unordered_list }}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/errors_formset.html000066400000000000000000000003671264354352000277210ustar00rootroot00000000000000{% if formset.non_form_errors %}
    {% if formset_error_title %}

    {{ formset_error_title }}

    {% endif %}
      {{ formset.non_form_errors|unordered_list }}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/field.html000066400000000000000000000024271264354352000257300ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
    {% if form_show_errors %} {% for error in field.errors %}

    {{ error }}

    {% endfor %} {% endif %} {% if field.label %} {% if field|is_checkbox %} {% crispy_field field %} {% endif %} {% endif %} {% if not field|is_checkbox %} {% crispy_field field %} {% endif %} {% if field.help_text %}
    {{ field.help_text|safe }}
    {% endif %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/field.strict.html000066400000000000000000000022061264354352000272320ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
    {% for error in field.errors %}

    {{ error }}

    {% endfor %} {% if field|is_checkbox %} {% crispy_field field %} {% endif %} {% if field.label %} {% endif %} {% if not field|is_checkbox %} {% crispy_field field %} {% endif %} {% if field.help_text %}

    {{ field.help_text|safe }}

    {% endif %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/layout/000077500000000000000000000000001264354352000252675ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/layout/baseinput.html000066400000000000000000000007001264354352000301440ustar00rootroot00000000000000 django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/layout/buttonholder.html000066400000000000000000000003241264354352000306650ustar00rootroot00000000000000
    {{ fields_output|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/layout/div.html000066400000000000000000000002671264354352000267440ustar00rootroot00000000000000
    {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/layout/fieldset.html000066400000000000000000000004731264354352000277600ustar00rootroot00000000000000
    {% if legend %}{{ legend|safe }}{% endif %} {{ fields|safe }}
    django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/layout/multifield.html000066400000000000000000000020121264354352000303060ustar00rootroot00000000000000
    {% if form_show_errors %} {% for field in multifield.bound_fields %} {% if field.errors %} {% for error in field.errors %}

    {{ error }}

    {% endfor %} {% endif %} {% endfor %} {% endif %} {% if multifield.label_html %}

    {{ multifield.label_html|safe }}

    {% endif %}
    {{ fields_output|safe }}
    {% for field in multifield.bound_fields %} {% if field.help_text %}

    {{ field.help_text|safe }}

    {% endif %} {% endfor %}
    django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/multifield.html000066400000000000000000000013231264354352000267750ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
    {% if form_show_labels and field.label %} {% endif %} {% crispy_field field %} {% if field.help_text %}
    {{ field.help_text|safe }}
    {% endif %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/uni_form.html000066400000000000000000000006131264354352000264560ustar00rootroot00000000000000{% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "uni_form/errors.html" %} {% endif %} {% if form_style == "" or form_style %}
    {% endif %} {% for field in form %} {% include "uni_form/field.html" %} {% endfor %} {% if form_style == "" or form_style %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/uni_formset.html000066400000000000000000000003421264354352000271710ustar00rootroot00000000000000{% with formset.management_form as form %} {% include 'uni_form/uni_form.html' %} {% endwith %} {% for form in formset %}
    {% include 'uni_form/uni_form.html' %}
    {% endfor %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/whole_uni_form.html000066400000000000000000000010501264354352000276500ustar00rootroot00000000000000{% if form_tag %}
    {% endif %} {% if form_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %} {% include "uni_form/display_form.html" %} {% if inputs %}
    {% for input in inputs %} {% include "uni_form/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if form_tag %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templates/uni_form/whole_uni_formset.html000066400000000000000000000013771264354352000304000ustar00rootroot00000000000000{% load crispy_forms_tags %} {% if formset_tag %}
    {% endif %} {% if formset_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %}
    {{ formset.management_form|crispy }}
    {% include "uni_form/errors_formset.html" %} {% for form in formset %} {% include "uni_form/display_form.html" %} {% endfor %} {% if inputs %}
    {% for input in inputs %} {% include "uni_form/layout/baseinput.html" %} {% endfor %}
    {% endif %} {% if formset_tag %}
    {% endif %} django-crispy-forms-1.6.0/crispy_forms/templatetags/000077500000000000000000000000001264354352000226305ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templatetags/__init__.py000066400000000000000000000000001264354352000247270ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/templatetags/crispy_forms_field.py000066400000000000000000000127411264354352000270710ustar00rootroot00000000000000try: from itertools import izip except ImportError: izip = zip import django from django import forms from django import template from django.template import loader, Context from django.conf import settings from crispy_forms.utils import TEMPLATE_PACK, get_template_pack register = template.Library() @register.filter def is_checkbox(field): return isinstance(field.field.widget, forms.CheckboxInput) @register.filter def is_password(field): return isinstance(field.field.widget, forms.PasswordInput) @register.filter def is_radioselect(field): return isinstance(field.field.widget, forms.RadioSelect) @register.filter def is_select(field): return isinstance(field.field.widget, forms.Select) @register.filter def is_checkboxselectmultiple(field): return isinstance(field.field.widget, forms.CheckboxSelectMultiple) @register.filter def is_file(field): return isinstance(field.field.widget, forms.ClearableFileInput) @register.filter def classes(field): """ Returns CSS classes of a field """ return field.widget.attrs.get('class', None) @register.filter def css_class(field): """ Returns widgets class name in lowercase """ return field.field.widget.__class__.__name__.lower() def pairwise(iterable): """s -> (s0,s1), (s2,s3), (s4, s5), ...""" a = iter(iterable) return izip(a, a) class CrispyFieldNode(template.Node): def __init__(self, field, attrs): self.field = field self.attrs = attrs self.html5_required = 'html5_required' def render(self, context): # Nodes are not threadsafe so we must store and look up our instance # variables in the current rendering context first if self not in context.render_context: context.render_context[self] = ( template.Variable(self.field), self.attrs, template.Variable(self.html5_required) ) field, attrs, html5_required = context.render_context[self] field = field.resolve(context) try: html5_required = html5_required.resolve(context) except template.VariableDoesNotExist: html5_required = False # If template pack has been overridden in FormHelper we can pick it from context template_pack = context.get('template_pack', TEMPLATE_PACK) widgets = getattr(field.field.widget, 'widgets', [field.field.widget]) if isinstance(attrs, dict): attrs = [attrs] * len(widgets) converters = { 'textinput': 'textinput textInput', 'fileinput': 'fileinput fileUpload', 'passwordinput': 'textinput textInput', } converters.update(getattr(settings, 'CRISPY_CLASS_CONVERTERS', {})) for widget, attr in zip(widgets, attrs): class_name = widget.__class__.__name__.lower() class_name = converters.get(class_name, class_name) css_class = widget.attrs.get('class', '') if css_class: if css_class.find(class_name) == -1: css_class += " %s" % class_name else: css_class = class_name if ( template_pack in ['bootstrap3', 'bootstrap4'] and not is_checkbox(field) and not is_file(field) ): css_class += ' form-control' widget.attrs['class'] = css_class # HTML5 required attribute if html5_required and field.field.required and 'required' not in widget.attrs: if field.field.widget.__class__.__name__ is not 'RadioSelect': widget.attrs['required'] = 'required' for attribute_name, attribute in attr.items(): attribute_name = template.Variable(attribute_name).resolve(context) if attribute_name in widget.attrs: widget.attrs[attribute_name] += " " + template.Variable(attribute).resolve(context) else: widget.attrs[attribute_name] = template.Variable(attribute).resolve(context) return field @register.tag(name="crispy_field") def crispy_field(parser, token): """ {% crispy_field field attrs %} """ token = token.split_contents() field = token.pop(1) attrs = {} # We need to pop tag name, or pairwise would fail token.pop(0) for attribute_name, value in pairwise(token): attrs[attribute_name] = value return CrispyFieldNode(field, attrs) @register.simple_tag() def crispy_addon(field, append="", prepend="", form_show_labels=True): """ Renders a form field using bootstrap's prepended or appended text:: {% crispy_addon form.my_field prepend="$" append=".00" %} You can also just prepend or append like so {% crispy_addon form.my_field prepend="$" %} {% crispy_addon form.my_field append=".00" %} """ if field: context = Context({ 'field': field, 'form_show_errors': True, 'form_show_labels': form_show_labels, }) template = loader.get_template('%s/layout/prepended_appended_text.html' % get_template_pack()) context['crispy_prepended_text'] = prepend context['crispy_appended_text'] = append if not prepend and not append: raise TypeError("Expected a prepend and/or append argument") if django.VERSION >= (1, 8): context = context.flatten() return template.render(context) django-crispy-forms-1.6.0/crispy_forms/templatetags/crispy_forms_filters.py000066400000000000000000000067121264354352000274570ustar00rootroot00000000000000# -*- coding: utf-8 -*- import django from django.conf import settings from django.forms import forms from django.forms.formsets import BaseFormSet from django.template import Context from django.template.loader import get_template from django.utils.safestring import mark_safe from django import template from crispy_forms.compatibility import lru_cache from crispy_forms.exceptions import CrispyError from crispy_forms.utils import flatatt, TEMPLATE_PACK @lru_cache() def uni_formset_template(template_pack=TEMPLATE_PACK): return get_template('%s/uni_formset.html' % template_pack) @lru_cache() def uni_form_template(template_pack=TEMPLATE_PACK): return get_template('%s/uni_form.html' % template_pack) register = template.Library() @register.filter(name='crispy') def as_crispy_form(form, template_pack=TEMPLATE_PACK, label_class="", field_class=""): """ The original and still very useful way to generate a div elegant form/formset:: {% load crispy_forms_tags %}
    {% csrf_token %} {{ myform|crispy }}
    or, if you want to explicitly set the template pack:: {{ myform|crispy:"bootstrap" }} In ``bootstrap3`` or ``bootstrap4`` for horizontal forms you can do:: {{ myform|label_class:"col-lg-2",field_class:"col-lg-8" }} """ if isinstance(form, BaseFormSet): template = uni_formset_template(template_pack) c = Context({ 'formset': form, 'form_show_errors': True, 'form_show_labels': True, 'label_class': label_class, 'field_class': field_class, }) else: template = uni_form_template(template_pack) c = Context({ 'form': form, 'form_show_errors': True, 'form_show_labels': True, 'label_class': label_class, 'field_class': field_class, }) if django.VERSION >= (1, 8): c = c.flatten() return template.render(c) @register.filter(name='as_crispy_errors') def as_crispy_errors(form, template_pack=TEMPLATE_PACK): """ Renders only form errors the same way as django-crispy-forms:: {% load crispy_forms_tags %} {{ form|as_crispy_errors }} or:: {{ form|as_crispy_errors:"bootstrap" }} """ if isinstance(form, BaseFormSet): template = get_template('%s/errors_formset.html' % template_pack) c = Context({'formset': form}) else: template = get_template('%s/errors.html' % template_pack) c = Context({'form': form}) if django.VERSION >= (1, 8): c = c.flatten() return template.render(c) @register.filter(name='as_crispy_field') def as_crispy_field(field, template_pack=TEMPLATE_PACK): """ Renders a form field like a django-crispy-forms field:: {% load crispy_forms_tags %} {{ form.field|as_crispy_field }} or:: {{ form.field|as_crispy_field:"bootstrap" }} """ if not isinstance(field, forms.BoundField) and settings.DEBUG: raise CrispyError('|as_crispy_field got passed an invalid or inexistent field') template = get_template('%s/field.html' % template_pack) c = Context({'field': field, 'form_show_errors': True, 'form_show_labels': True}) if django.VERSION >= (1, 8): c = c.flatten() return template.render(c) @register.filter(name='flatatt') def flatatt_filter(attrs): return mark_safe(flatatt(attrs)) django-crispy-forms-1.6.0/crispy_forms/templatetags/crispy_forms_tags.py000066400000000000000000000250051264354352000267410ustar00rootroot00000000000000# -*- coding: utf-8 -*- from copy import copy import django from django.conf import settings from django.forms.formsets import BaseFormSet from django.template import Context from django.template.loader import get_template from django import template from crispy_forms.helper import FormHelper from crispy_forms.compatibility import lru_cache, string_types register = template.Library() # We import the filters, so they are available when doing load crispy_forms_tags from crispy_forms.templatetags.crispy_forms_filters import * from crispy_forms.utils import TEMPLATE_PACK, get_template_pack class ForLoopSimulator(object): """ Simulates a forloop tag, precisely:: {% for form in formset.forms %} If `{% crispy %}` is rendering a formset with a helper, We inject a `ForLoopSimulator` object in the context as `forloop` so that formset forms can do things like:: Fieldset("Item {{ forloop.counter }}", [...]) HTML("{% if forloop.first %}First form text{% endif %}" """ def __init__(self, formset): self.len_values = len(formset.forms) # Shortcuts for current loop iteration number. self.counter = 1 self.counter0 = 0 # Reverse counter iteration numbers. self.revcounter = self.len_values self.revcounter0 = self.len_values - 1 # Boolean values designating first and last times through loop. self.first = True self.last = (0 == self.len_values - 1) def iterate(self): """ Updates values as if we had iterated over the for """ self.counter += 1 self.counter0 += 1 self.revcounter -= 1 self.revcounter0 -= 1 self.first = False self.last = (self.revcounter0 == self.len_values - 1) def copy_context(context): """ Copies a `Context` variable. It uses `Context.__copy__` if available (introduced in Django 1.3) or copy otherwise. """ if hasattr(context, "__copy__"): return context.__copy__() duplicate = copy(context) duplicate.dicts = context.dicts[:] return duplicate class BasicNode(template.Node): """ Basic Node object that we can rely on for Node objects in normal template tags. I created this because most of the tags we'll be using will need both the form object and the helper string. This handles both the form object and parses out the helper string into attributes that templates can easily handle. """ def __init__(self, form, helper, template_pack=None): self.form = form if helper is not None: self.helper = helper else: self.helper = None self.template_pack = template_pack or get_template_pack() def get_render(self, context): """ Returns a `Context` object with all the necessary stuff for rendering the form :param context: `django.template.Context` variable holding the context for the node `self.form` and `self.helper` are resolved into real Python objects resolving them from the `context`. The `actual_form` can be a form or a formset. If it's a formset `is_formset` is set to True. If the helper has a layout we use it, for rendering the form or the formset's forms. """ # Nodes are not thread safe in multithreaded environments # https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#thread-safety-considerations if self not in context.render_context: context.render_context[self] = ( template.Variable(self.form), template.Variable(self.helper) if self.helper else None ) form, helper = context.render_context[self] actual_form = form.resolve(context) if self.helper is not None: helper = helper.resolve(context) else: # If the user names the helper within the form `helper` (standard), we use it # This allows us to have simplified tag syntax: {% crispy form %} helper = FormHelper() if not hasattr(actual_form, 'helper') else actual_form.helper # use template_pack from helper, if defined try: if helper.template_pack: self.template_pack = helper.template_pack except AttributeError: pass self.actual_helper = helper # We get the response dictionary is_formset = isinstance(actual_form, BaseFormSet) response_dict = self.get_response_dict(helper, context, is_formset) node_context = copy_context(context) node_context.update(response_dict) # If we have a helper's layout we use it, for the form or the formset's forms if helper and helper.layout: if not is_formset: actual_form.form_html = helper.render_layout(actual_form, node_context, template_pack=self.template_pack) else: forloop = ForLoopSimulator(actual_form) helper.render_hidden_fields = True for form in actual_form: node_context.update({'forloop': forloop}) form.form_html = helper.render_layout(form, node_context, template_pack=self.template_pack) forloop.iterate() if is_formset: response_dict.update({'formset': actual_form}) else: response_dict.update({'form': actual_form}) return Context(response_dict) def get_response_dict(self, helper, context, is_formset): """ Returns a dictionary with all the parameters necessary to render the form/formset in a template. :param context: `django.template.Context` for the node :param is_formset: Boolean value. If set to True, indicates we are working with a formset. """ if not isinstance(helper, FormHelper): raise TypeError('helper object provided to {% crispy %} tag must be a crispy.helper.FormHelper object.') attrs = helper.get_attributes(template_pack=self.template_pack) form_type = "form" if is_formset: form_type = "formset" # We take form/formset parameters from attrs if they are set, otherwise we use defaults response_dict = { 'template_pack': self.template_pack, '%s_action' % form_type: attrs['attrs'].get("action", ''), '%s_method' % form_type: attrs.get("form_method", 'post'), '%s_tag' % form_type: attrs.get("form_tag", True), '%s_class' % form_type: attrs['attrs'].get("class", ''), '%s_id' % form_type: attrs['attrs'].get("id", ""), '%s_style' % form_type: attrs.get("form_style", None), 'form_error_title': attrs.get("form_error_title", None), 'formset_error_title': attrs.get("formset_error_title", None), 'form_show_errors': attrs.get("form_show_errors", True), 'help_text_inline': attrs.get("help_text_inline", False), 'html5_required': attrs.get("html5_required", False), 'form_show_labels': attrs.get("form_show_labels", True), 'disable_csrf': attrs.get("disable_csrf", False), 'inputs': attrs.get('inputs', []), 'is_formset': is_formset, '%s_attrs' % form_type: attrs.get('attrs', ''), 'flat_attrs': attrs.get('flat_attrs', ''), 'error_text_inline': attrs.get('error_text_inline', True), 'label_class': attrs.get('label_class', ''), 'label_size': attrs.get('label_size', 0), 'field_class': attrs.get('field_class', ''), 'include_media': attrs.get('include_media', True), } # Handles custom attributes added to helpers for attribute_name, value in attrs.items(): if attribute_name not in response_dict: response_dict[attribute_name] = value if 'csrf_token' in context: response_dict['csrf_token'] = context['csrf_token'] return response_dict @lru_cache() def whole_uni_formset_template(template_pack=TEMPLATE_PACK): return get_template('%s/whole_uni_formset.html' % template_pack) @lru_cache() def whole_uni_form_template(template_pack=TEMPLATE_PACK): return get_template('%s/whole_uni_form.html' % template_pack) class CrispyFormNode(BasicNode): def render(self, context): c = self.get_render(context) if self.actual_helper is not None and getattr(self.actual_helper, 'template', False): template = get_template(self.actual_helper.template) else: if c['is_formset']: template = whole_uni_formset_template(self.template_pack) else: template = whole_uni_form_template(self.template_pack) if django.VERSION >= (1, 8): c = c.flatten() return template.render(c) # {% crispy %} tag @register.tag(name="crispy") def do_uni_form(parser, token): """ You need to pass in at least the form/formset object, and can also pass in the optional `crispy_forms.helpers.FormHelper` object. helper (optional): A `crispy_forms.helper.FormHelper` object. Usage:: {% load crispy_tags %} {% crispy form form.helper %} You can also provide the template pack as the third argument:: {% crispy form form.helper 'bootstrap' %} If the `FormHelper` attribute is named `helper` you can simply do:: {% crispy form %} {% crispy form 'bootstrap' %} """ token = token.split_contents() form = token.pop(1) helper = None template_pack = "'%s'" % get_template_pack() # {% crispy form helper %} try: helper = token.pop(1) except IndexError: pass # {% crispy form helper 'bootstrap' %} try: template_pack = token.pop(1) except IndexError: pass # {% crispy form 'bootstrap' %} if ( helper is not None and isinstance(helper, string_types) and ("'" in helper or '"' in helper) ): template_pack = helper helper = None if template_pack is not None: template_pack = template_pack[1:-1] ALLOWED_TEMPLATE_PACKS = getattr( settings, 'CRISPY_ALLOWED_TEMPLATE_PACKS', ('bootstrap', 'uni_form', 'bootstrap3', 'bootstrap4') ) if template_pack not in ALLOWED_TEMPLATE_PACKS: raise template.TemplateSyntaxError( "crispy tag's template_pack argument should be in %s" % str(ALLOWED_TEMPLATE_PACKS) ) return CrispyFormNode(form, helper, template_pack=template_pack) django-crispy-forms-1.6.0/crispy_forms/templatetags/crispy_forms_utils.py000066400000000000000000000017561264354352000271520ustar00rootroot00000000000000# -*- coding: utf-8 -*- import re from django import template from django.utils.encoding import force_text from django.utils.functional import allow_lazy from crispy_forms.compatibility import text_type register = template.Library() def remove_spaces(value): html = re.sub(r'>\s{3,}<', '> <', force_text(value)) return re.sub(r'/><', r'/> <', force_text(html)) remove_spaces = allow_lazy(remove_spaces, text_type) class SpecialSpacelessNode(template.Node): def __init__(self, nodelist): self.nodelist = nodelist def render(self, context): return remove_spaces(self.nodelist.render(context).strip()) @register.tag def specialspaceless(parser, token): """ Removes whitespace between HTML tags, and introduces a whitespace after buttons an inputs, necessary for Bootstrap to place them correctly in the layout. """ nodelist = parser.parse(('endspecialspaceless',)) parser.delete_first_token() return SpecialSpacelessNode(nodelist) django-crispy-forms-1.6.0/crispy_forms/tests/000077500000000000000000000000001264354352000213005ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/tests/__init__.py000066400000000000000000000000001264354352000233770ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/tests/compatibility.py000066400000000000000000000003231264354352000245210ustar00rootroot00000000000000# coding: utf-8 try: from django.template.loader import get_template_from_string except ImportError: from django.template import Engine get_template_from_string = Engine.get_default().from_string django-crispy-forms-1.6.0/crispy_forms/tests/conftest.py000066400000000000000000000023501264354352000234770ustar00rootroot00000000000000# coding: utf-8 import pytest from crispy_forms.layout import Layout, Div, Field, Submit, Fieldset, HTML only_uni_form = pytest.mark.only('uni_form') only_bootstrap = pytest.mark.only('bootstrap', 'bootstrap3', 'bootstrap4') only_bootstrap3 = pytest.mark.only('bootstrap3') only_bootstrap4 = pytest.mark.only('bootstrap4') @pytest.fixture def advanced_layout(): return Layout( Div( Div(Div('email')), Div(Field('password1')), Submit("save", "save"), Fieldset( "legend", 'first_name', HTML("extra text"), ), Layout( "password2", ), ), 'last_name', ) @pytest.fixture(autouse=True, params=('uni_form', 'bootstrap', 'bootstrap3', 'bootstrap4')) def template_packs(request, settings): check_template_pack(request._pyfuncitem._obj, request.param) settings.CRISPY_TEMPLATE_PACK = request.param def check_template_pack(function, template_pack): if hasattr(function, 'only'): mark = function.only if template_pack not in mark.args: pytest.skip('Requires %s template pack' % ' or '.join(mark.args)) django-crispy-forms-1.6.0/crispy_forms/tests/forms.py000066400000000000000000000074621264354352000230110ustar00rootroot00000000000000import django from django import forms from django.db import models from crispy_forms.helper import FormHelper class TestForm(forms.Form): is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput()) email = forms.EmailField(label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email") password1 = forms.CharField(label="password", max_length=30, required=True, widget=forms.PasswordInput()) password2 = forms.CharField(label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput()) first_name = forms.CharField(label="first name", max_length=5, required=True, widget=forms.TextInput()) last_name = forms.CharField(label="last name", max_length=5, required=True, widget=forms.TextInput()) datetime_field = forms.DateTimeField(label="date time", widget=forms.SplitDateTimeWidget()) def clean(self): super(TestForm, self).clean() password1 = self.cleaned_data.get('password1', None) password2 = self.cleaned_data.get('password2', None) if not password1 and not password2 or password1 != password2: raise forms.ValidationError("Passwords dont match") return self.cleaned_data class TestForm2(TestForm): def __init__(self, *args, **kwargs): super(TestForm2, self).__init__(*args, **kwargs) self.helper = FormHelper(self) class CheckboxesTestForm(forms.Form): checkboxes = forms.MultipleChoiceField( choices=( (1, "Option one"), (2, "Option two"), (3, "Option three") ), initial=(1,), widget=forms.CheckboxSelectMultiple, ) alphacheckboxes = forms.MultipleChoiceField( choices=( ('option_one', "Option one"), ('option_two', "Option two"), ('option_three', "Option three") ), initial=('option_two', 'option_three'), widget=forms.CheckboxSelectMultiple, ) numeric_multiple_checkboxes = forms.MultipleChoiceField( choices=( (1, "Option one"), (2, "Option two"), (3, "Option three") ), initial=(1, 2), widget=forms.CheckboxSelectMultiple, ) inline_radios = forms.ChoiceField( choices=( ('option_one', "Option one"), ('option_two', "Option two"), ), widget=forms.RadioSelect, initial='option_two', ) class CrispyTestModel(models.Model): email = models.CharField(max_length=20) password = models.CharField(max_length=20) class TestForm3(forms.ModelForm): class Meta: model = CrispyTestModel fields = ['email', 'password'] exclude = ['password'] def __init__(self, *args, **kwargs): super(TestForm3, self).__init__(*args, **kwargs) self.helper = FormHelper(self) class TestForm4(forms.ModelForm): class Meta: """ before Django1.6, one cannot use __all__ shortcut for fields without getting the following error: django.core.exceptions.FieldError: Unknown field(s) (a, l, _) specified for CrispyTestModel because obviously it casts the string to a set """ model = CrispyTestModel if django.VERSION >= (1, 6): fields = '__all__' # eliminate RemovedInDjango18Warning class TestForm5(forms.Form): choices = [ (1, 1), (2, 2), (1000, 1000), ] checkbox_select_multiple = forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple, choices=choices ) radio_select = forms.ChoiceField( widget=forms.RadioSelect, choices=choices ) pk = forms.IntegerField() class TestFormWithMedia(forms.Form): class Media: css = {'all': ('test.css',)} js = ('test.js',) django-crispy-forms-1.6.0/crispy_forms/tests/templates/000077500000000000000000000000001264354352000232765ustar00rootroot00000000000000django-crispy-forms-1.6.0/crispy_forms/tests/templates/crispy_render_template.html000066400000000000000000000000601264354352000307230ustar00rootroot00000000000000{% load crispy_forms_tags %} {% crispy form %} django-crispy-forms-1.6.0/crispy_forms/tests/templates/custom_field_template.html000066400000000000000000000001031264354352000305260ustar00rootroot00000000000000

    Special custom field

    {% include 'bootstrap/field.html' %} django-crispy-forms-1.6.0/crispy_forms/tests/templates/custom_form_template.html000066400000000000000000000001131264354352000304070ustar00rootroot00000000000000

    Special custom form

    {% include "bootstrap/whole_uni_form.html" %} django-crispy-forms-1.6.0/crispy_forms/tests/test_dynamic_api.py000066400000000000000000000326741264354352000252020ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django import forms import pytest from .conftest import only_uni_form from crispy_forms.compatibility import string_types from crispy_forms.exceptions import DynamicError from crispy_forms.helper import FormHelper, FormHelpersException from crispy_forms.layout import ( Layout, Fieldset, MultiField, HTML, Div, Field ) from crispy_forms.bootstrap import AppendedText from crispy_forms.tests.forms import TestForm def test_wrap_all_fields(): helper = FormHelper() layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper.all().wrap(Field, css_class="test-class") for field in layout.fields: assert isinstance(field, Field) assert field.attrs['class'] == "test-class" assert layout[0][0] == 'email' assert layout[1][0] == 'password1' assert layout[2][0] == 'password2' def test_wrap_selected_fields(): helper = FormHelper() layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper[1:3].wrap(Field, css_class="test-class") assert not isinstance(layout.fields[0], Field) assert isinstance(layout.fields[1], Field) assert isinstance(layout.fields[2], Field) helper[0].wrap(Fieldset, 'legend', css_class="test-class") assert isinstance(layout[0], Fieldset) assert layout[0].legend == 'legend' assert layout[0][0] == 'email' def test_wrap_together_with_slices(): helper = FormHelper() layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper[1:3].wrap_together(Field, css_class="test-class") assert layout.fields[0] == 'email' assert isinstance(layout.fields[1], Field) assert layout.fields[1][0] == 'password1' assert layout.fields[1][1] == 'password2' layout = Layout( Div('email'), 'password1', 'password2', ) helper.layout = layout helper[0:3].wrap_together(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) assert isinstance(layout.fields[0][0], Div) assert layout.fields[0][0][0] == 'email' assert layout.fields[0][1] == 'password1' assert layout.fields[0][2] == 'password2' layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper[0].wrap_together(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) assert layout.fields[1] == 'password1' assert layout.fields[2] == 'password2' layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper[0].wrap_together(Fieldset, "legend", css_class="test-class") assert isinstance(layout.fields[0], Fieldset) assert layout.fields[0].legend == 'legend' assert layout.fields[1] == 'password1' assert layout.fields[2] == 'password2' def test_wrap_together_partial_slices(): helper = FormHelper() layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper[:2].wrap_together(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) assert layout.fields[1] == 'password2' assert layout.fields[0][0] == 'email' assert layout.fields[0][1] == 'password1' helper = FormHelper() layout = Layout( 'email', 'password1', 'password2', ) helper.layout = layout helper[1:].wrap_together(Field, css_class="test-class") assert layout.fields[0] == 'email' assert isinstance(layout.fields[1], Field) assert layout.fields[1][0] == 'password1' assert layout.fields[1][1] == 'password2' def test_update_attributes(): helper = FormHelper() helper.layout = Layout( 'email', Field('password1'), 'password2', ) helper['password1'].update_attributes(readonly=True) assert 'readonly' in helper.layout[1].attrs def test_update_attributes_and_wrap_once(): helper = FormHelper() layout = Layout( 'email', Field('password1'), 'password2', ) helper.layout = layout helper.filter(Field).update_attributes(readonly=True) assert isinstance(layout[1], Field) assert layout[1].attrs == {'readonly': True} layout = Layout( 'email', Div(Field('password1')), 'password2', ) helper.layout = layout helper.filter(Field, max_level=2).update_attributes(readonly=True) assert isinstance(layout[1][0], Field) assert layout[1][0].attrs == {'readonly': True} layout = Layout( 'email', Div(Field('password1')), 'password2', ) helper.layout = layout helper.filter(string_types, greedy=True).wrap_once(Field) helper.filter(Field, greedy=True).update_attributes(readonly=True) assert isinstance(layout[0], Field) assert isinstance(layout[1][0], Field) assert isinstance(layout[1][0][0], string_types) assert isinstance(layout[2], Field) assert layout[1][0].attrs == {'readonly': True} assert layout[0].attrs == {'readonly': True} assert layout[2].attrs == {'readonly': True} def test_get_layout_objects(): layout_1 = Layout( Div() ) assert layout_1.get_layout_objects(Div) == [[[0], 'div']] layout_2 = Layout( Div( Div( Div('email') ), Div('password1'), 'password2' ) ) assert layout_2.get_layout_objects(Div) == [[[0], 'div']] assert layout_2.get_layout_objects(Div, max_level=1) == [ [[0], 'div'], [[0, 0], 'div'], [[0, 1], 'div'] ] assert layout_2.get_layout_objects(Div, max_level=2) == [ [[0], 'div'], [[0, 0], 'div'], [[0, 0, 0], 'div'], [[0, 1], 'div'] ] layout_3 = Layout( 'email', Div('password1'), 'password2', ) assert layout_3.get_layout_objects(string_types, max_level=2) == [ [[0], 'email'], [[1, 0], 'password1'], [[2], 'password2'] ] layout_4 = Layout( Div( Div('field_name'), 'field_name2', ), Div('password'), 'extra_field' ) assert layout_4.get_layout_objects(Div) == [ [[0], 'div'], [[1], 'div'] ] assert layout_4.get_layout_objects(Div, max_level=1) == [ [[0], 'div'], [[0, 0], 'div'], [[1], 'div'] ] def test_filter_and_wrap(): helper = FormHelper() layout = Layout( 'email', Div('password1'), 'password2', ) helper.layout = layout helper.filter(string_types).wrap(Field, css_class="test-class") assert isinstance(layout.fields[0], Field) assert isinstance(layout.fields[1], Div) assert isinstance(layout.fields[2], Field) assert layout[2][0] == 'password2' # Wrapping a div in a div helper.filter(Div).wrap(Div, css_class="test-class") assert isinstance(layout.fields[1], Div) assert isinstance(layout.fields[1].fields[0], Div) assert layout[1][0][0] == 'password1' def test_filter_and_wrap_side_effects(): helper = FormHelper() layout = Layout( Div( 'extra_field', Div('password1'), ), ) helper.layout = layout with pytest.raises(DynamicError): helper.filter(Div, max_level=2).wrap(Div, css_class="test-class") def test_get_field_names(): layout_1 = Div( 'field_name' ) assert layout_1.get_field_names() == [ [[0], 'field_name'] ] layout_2 = Div( Div('field_name') ) assert layout_2.get_field_names() == [ [[0, 0], 'field_name'] ] layout_3 = Div( Div('field_name'), 'password' ) assert layout_3.get_field_names() == [ [[0, 0], 'field_name'], [[1], 'password'] ] layout_4 = Div( Div( Div('field_name'), 'field_name2', ), Div('password'), 'extra_field' ) assert layout_4.get_field_names() == [ [[0, 0, 0], 'field_name'], [[0, 1], 'field_name2'], [[1, 0], 'password'], [[2], 'extra_field'] ] layout_5 = Div( Div( 'field_name', 'field_name2', ), 'extra_field' ) assert layout_5.get_field_names() == [ [[0, 0], 'field_name'], [[0, 1], 'field_name2'], [[1], 'extra_field'], ] def test_layout_get_field_names(): layout_1 = Layout( Div('field_name'), 'password' ) assert layout_1.get_field_names() == [ [[0, 0], 'field_name'], [[1], 'password'], ] layout_2 = Layout( Div('field_name'), 'password', Fieldset('legend', 'extra_field') ) assert layout_2.get_field_names() == [ [[0, 0], 'field_name'], [[1], 'password'], [[2, 0], 'extra_field'], ] layout_3 = Layout( Div( Div( Div('email') ), Div('password1'), 'password2' ) ) assert layout_3.get_field_names() == [ [[0, 0, 0, 0], 'email'], [[0, 1, 0], 'password1'], [[0, 2], 'password2'], ] def test_filter_by_widget(advanced_layout): form = TestForm() form.helper = FormHelper(form) form.helper.layout = advanced_layout assert form.helper.filter_by_widget(forms.PasswordInput).slice == [ [[0, 1, 0, 0], 'password1'], [[0, 4, 0], 'password2'], ] def test_exclude_by_widget(advanced_layout): form = TestForm() form.helper = FormHelper(form) form.helper.layout = advanced_layout assert form.helper.exclude_by_widget(forms.PasswordInput).slice == [ [[0, 0, 0, 0], 'email'], [[0, 3, 0], 'first_name'], [[1], 'last_name'], ] def test_exclude_by_widget_and_wrap(advanced_layout): form = TestForm() form.helper = FormHelper(form) form.helper.layout = advanced_layout form.helper.exclude_by_widget(forms.PasswordInput).wrap(Field, css_class='hero') # Check wrapped fields assert isinstance(form.helper.layout[0][0][0][0], Field) assert isinstance(form.helper.layout[0][3][0], Field) assert isinstance(form.helper.layout[1], Field) # Check others stay the same assert isinstance(form.helper.layout[0][3][1], HTML) assert isinstance(form.helper.layout[0][1][0][0], string_types) assert isinstance(form.helper.layout[0][4][0], string_types) def test_all_without_layout(): form = TestForm() form.helper = FormHelper() with pytest.raises(FormHelpersException): form.helper.all().wrap(Div) def test_filter_by_widget_without_form(advanced_layout): form = TestForm() form.helper = FormHelper() form.helper.layout = advanced_layout with pytest.raises(FormHelpersException): form.helper.filter_by_widget(forms.PasswordInput) def test_formhelper__getitem__(): helper = FormHelper() layout = Layout( Div('email'), 'password1', ) helper.layout = layout helper['email'].wrap(Field, css_class='hero') assert isinstance(layout[0][0], Field) assert layout[0][0][0] == 'email' helper = FormHelper() helper.layout = Layout('password1') helper['password1'].wrap(AppendedText, "extra") assert isinstance(helper.layout[0], AppendedText) assert helper.layout[0][0] == 'password1' assert helper.layout[0].text == 'extra' def test_formhelper__setitem__(): helper = FormHelper() layout = Layout( 'first_field', Div('email') ) helper.layout = layout helper[0] = 'replaced' assert layout[0] == 'replaced' def test_formhelper__delitem__and__len__(): helper = FormHelper() layout = Layout( 'first_field', Div('email') ) helper.layout = layout del helper[0] assert len(helper) == 1 def test__delitem__and__len__layout_object(): layout = Layout( 'first_field', Div('email') ) del layout[0] assert len(layout) == 1 def test__getitem__layout_object(): layout = Layout( Div( Div( Div('email') ), Div('password1'), 'password2' ) ) assert isinstance(layout[0], Div) assert isinstance(layout[0][0], Div) assert isinstance(layout[0][0][0], Div) assert isinstance(layout[0][1], Div) assert isinstance(layout[0][1][0], string_types) assert isinstance(layout[0][2], string_types) def test__getattr__append_layout_object(): layout = Layout( Div('email') ) layout.append('password1') assert isinstance(layout[0], Div) assert isinstance(layout[0][0], string_types) assert isinstance(layout[1], string_types) def test__setitem__layout_object(): layout = Layout( Div('email') ) layout[0][0] = 'password1' assert isinstance(layout[0], Div) assert layout[0][0] == 'password1' @only_uni_form def test_filter(): helper = FormHelper() helper.layout = Layout( Div( MultiField('field_name'), 'field_name2', ), Div('password'), 'extra_field' ) assert helper.filter(Div, MultiField).slice == [ [[0], 'div'], [[1], 'div'] ] assert helper.filter(Div, MultiField, max_level=1).slice == [ [[0], 'div'], [[0, 0], 'multifield'], [[1], 'div'] ] assert helper.filter(MultiField, max_level=1).slice == [ [[0, 0], 'multifield'] ] django-crispy-forms-1.6.0/crispy_forms/tests/test_form_helper.py000066400000000000000000000534711264354352000252250ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import re import django from django import forms from django.core.urlresolvers import reverse from django.forms.models import formset_factory from django.middleware.csrf import _get_new_csrf_key from django.template import ( TemplateSyntaxError, Context ) import pytest from django.utils.translation import ugettext_lazy as _ from .compatibility import get_template_from_string from .conftest import only_uni_form, only_bootstrap3, only_bootstrap4, only_bootstrap from .forms import TestForm, TestFormWithMedia from crispy_forms.bootstrap import ( FieldWithButtons, PrependedAppendedText, AppendedText, PrependedText, StrictButton ) from crispy_forms.compatibility import text_type from crispy_forms.helper import FormHelper, FormHelpersException from crispy_forms.layout import ( Layout, Submit, Reset, Hidden, Button, MultiField, Field ) from crispy_forms.utils import render_crispy_form from crispy_forms.templatetags.crispy_forms_tags import CrispyFormNode def test_inputs(settings): form_helper = FormHelper() form_helper.add_input(Submit('my-submit', 'Submit', css_class="button white")) form_helper.add_input(Reset('my-reset', 'Reset')) form_helper.add_input(Hidden('my-hidden', 'Hidden')) form_helper.add_input(Button('my-button', 'Button')) template = get_template_from_string(""" {% load crispy_forms_tags %} {% crispy form form_helper %} """) c = Context({'form': TestForm(), 'form_helper': form_helper}) html = template.render(c) assert 'button white' in html assert 'id="submit-id-my-submit"' in html assert 'id="reset-id-my-reset"' in html assert 'name="my-hidden"' in html assert 'id="button-id-my-button"' in html if settings.CRISPY_TEMPLATE_PACK == 'uni_form': assert 'submit submitButton' in html assert 'reset resetButton' in html assert 'class="button"' in html else: assert 'class="btn"' in html assert 'btn btn-primary' in html assert 'btn btn-inverse' in html assert len(re.findall(r']+> <', html)) == 8 def test_invalid_form_method(): form_helper = FormHelper() with pytest.raises(FormHelpersException): form_helper.form_method = "superPost" def test_form_with_helper_without_layout(settings): form_helper = FormHelper() form_helper.form_id = 'this-form-rocks' form_helper.form_class = 'forms-that-rock' form_helper.form_method = 'GET' form_helper.form_action = 'simpleAction' form_helper.form_error_title = 'ERRORS' template = get_template_from_string(""" {% load crispy_forms_tags %} {% crispy testForm form_helper %} """) # now we render it, with errors form = TestForm({'password1': 'wargame', 'password2': 'god'}) form.is_valid() c = Context({'testForm': form, 'form_helper': form_helper}) html = template.render(c) # Lets make sure everything loads right assert html.count('Passwords dont match" in html # now lets remove the form tag and render it again. All the True items above # should now be false because the form tag is removed. form_helper.form_tag = False html = template.render(c) assert 'Passwords dont match' in html assert text_type(_('This field is required.')) in html assert 'error' in html # Now we render without errors form.helper.form_show_errors = False c = Context({'testForm': form}) html = template.render(c) # Ensure errors were not rendered assert '
  • Passwords dont match
  • ' not in html assert text_type(_('This field is required.')) not in html assert 'error' not in html def test_html5_required(): form = TestForm() form.helper = FormHelper() form.helper.html5_required = True html = render_crispy_form(form) # 6 out of 7 fields are required and an extra one for the SplitDateTimeWidget makes 7. assert html.count('required="required"') == 7 form = TestForm() form.helper = FormHelper() form.helper.html5_required = False html = render_crispy_form(form) def test_media_is_included_by_default_with_uniform(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'uni_form' html = render_crispy_form(form) assert 'test.css' in html assert 'test.js' in html def test_media_is_included_by_default_with_bootstrap(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'bootstrap' html = render_crispy_form(form) assert 'test.css' in html assert 'test.js' in html def test_media_is_included_by_default_with_bootstrap3(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'bootstrap3' html = render_crispy_form(form) assert 'test.css' in html assert 'test.js' in html def test_media_is_included_by_default_with_bootstrap4(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'bootstrap4' html = render_crispy_form(form) assert 'test.css' in html assert 'test.js' in html def test_media_removed_when_include_media_is_false_with_uniform(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'uni_form' form.helper.include_media = False html = render_crispy_form(form) assert 'test.css' not in html assert 'test.js' not in html def test_media_removed_when_include_media_is_false_with_bootstrap(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'bootstrap' form.helper.include_media = False html = render_crispy_form(form) assert 'test.css' not in html assert 'test.js' not in html def test_media_removed_when_include_media_is_false_with_bootstrap3(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'bootstrap3' form.helper.include_media = False html = render_crispy_form(form) assert 'test.css' not in html assert 'test.js' not in html def test_media_removed_when_include_media_is_false_with_bootstrap4(): form = TestFormWithMedia() form.helper = FormHelper() form.helper.template_pack = 'bootstrap4' form.helper.include_media = False html = render_crispy_form(form) assert 'test.css' not in html assert 'test.js' not in html def test_attrs(): form = TestForm() form.helper = FormHelper() form.helper.attrs = {'id': 'TestIdForm', 'autocomplete': "off"} html = render_crispy_form(form) assert 'autocomplete="off"' in html assert 'id="TestIdForm"' in html def test_template_context(): helper = FormHelper() helper.attrs = { 'id': 'test-form', 'class': 'test-forms', 'action': 'submit/test/form', 'autocomplete': 'off', } node = CrispyFormNode('form', 'helper') context = node.get_response_dict(helper, {}, False) assert context['form_id'] == "test-form" assert context['form_attrs']['id'] == "test-form" assert "test-forms" in context['form_class'] assert "test-forms" in context['form_attrs']['class'] assert context['form_action'] == "submit/test/form" assert context['form_attrs']['action'] == "submit/test/form" assert context['form_attrs']['autocomplete'] == "off" def test_template_context_using_form_attrs(): helper = FormHelper() helper.form_id = 'test-form' helper.form_class = 'test-forms' helper.form_action = 'submit/test/form' node = CrispyFormNode('form', 'helper') context = node.get_response_dict(helper, {}, False) assert context['form_id'] == "test-form" assert context['form_attrs']['id'] == "test-form" assert "test-forms" in context['form_class'] assert "test-forms" in context['form_attrs']['class'] assert context['form_action'] == "submit/test/form" assert context['form_attrs']['action'] == "submit/test/form" def test_template_helper_access(): helper = FormHelper() helper.form_id = 'test-form' assert helper['form_id'] == 'test-form' def test_without_helper(settings): template = get_template_from_string(""" {% load crispy_forms_tags %} {% crispy form %} """) c = Context({'form': TestForm()}) html = template.render(c) # Lets make sure everything loads right assert 'Special custom form" in html def test_helper_custom_field_template(): form = TestForm() form.helper = FormHelper() form.helper.layout = Layout( 'password1', 'password2', ) form.helper.field_template = 'custom_field_template.html' html = render_crispy_form(form) assert html.count("

    Special custom field

    ") == 2 @only_uni_form def test_form_show_errors(): form = TestForm({ 'email': 'invalidemail', 'first_name': 'first_name_too_long', 'last_name': 'last_name_too_long', 'password1': 'yes', 'password2': 'yes', }) form.helper = FormHelper() form.helper.layout = Layout( Field('email'), Field('first_name'), Field('last_name'), Field('password1'), Field('password2'), ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) assert html.count('error') == 9 form.helper.form_show_errors = False html = render_crispy_form(form) assert html.count('error') == 0 @only_uni_form def test_multifield_errors(): form = TestForm({ 'email': 'invalidemail', 'password1': 'yes', 'password2': 'yes', }) form.helper = FormHelper() form.helper.layout = Layout( MultiField('legend', 'email') ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) assert html.count('error') == 3 # Reset layout for avoiding side effects form.helper.layout = Layout( MultiField('legend', 'email') ) form.helper.form_show_errors = False html = render_crispy_form(form) assert html.count('error') == 0 @only_bootstrap def test_bootstrap_form_show_errors(): form = TestForm({ 'email': 'invalidemail', 'first_name': 'first_name_too_long', 'last_name': 'last_name_too_long', 'password1': 'yes', 'password2': 'yes', }) form.helper = FormHelper() form.helper.layout = Layout( AppendedText('email', 'whatever'), PrependedText('first_name', 'blabla'), PrependedAppendedText('last_name', 'foo', 'bar'), AppendedText('password1', 'whatever'), PrependedText('password2', 'blabla'), ) form.is_valid() form.helper.form_show_errors = True html = render_crispy_form(form) assert html.count('error') == 6 form.helper.form_show_errors = False html = render_crispy_form(form) assert html.count('error') == 0 @only_bootstrap def test_error_text_inline(settings): form = TestForm({'email': 'invalidemail'}) form.helper = FormHelper() layout = Layout( AppendedText('first_name', 'wat'), PrependedText('email', '@'), PrependedAppendedText('last_name', '@', 'wat'), ) form.helper.layout = layout form.is_valid() html = render_crispy_form(form) help_class = 'help-inline' if settings.CRISPY_TEMPLATE_PACK in ['bootstrap3', 'bootstrap4']: help_class = 'help-block' matches = re.findall( '') error_position = html.find('

    ') assert help_position < error_position # Viceversa form = TestForm({'email': 'invalidemail'}) form.helper = FormHelper() form.helper.error_text_inline = True form.helper.help_text_inline = False form.helper.layout = Layout('email') form.is_valid() html = render_crispy_form(form) # Check that error goes before help, otherwise CSS won't work error_position = html.find('') help_position = html.find('

    ') assert error_position < help_position @only_bootstrap def test_form_show_labels(): form = TestForm() form.helper = FormHelper() form.helper.layout = Layout( 'password1', FieldWithButtons( 'password2', StrictButton("Confirm") ), PrependedText( 'first_name', 'Mr.' ), AppendedText( 'last_name', '@' ), PrependedAppendedText( 'datetime_field', 'on', 'secs' ) ) form.helper.form_show_labels = False html = render_crispy_form(form) assert html.count("