pax_global_header00006660000000000000000000000064145201133250014506gustar00rootroot0000000000000052 comment=c7be48c07fdb316ab2cb6dd1cda4715ef6e9425a crispy-bootstrap5-2023.10/000077500000000000000000000000001452011332500152665ustar00rootroot00000000000000crispy-bootstrap5-2023.10/.github/000077500000000000000000000000001452011332500166265ustar00rootroot00000000000000crispy-bootstrap5-2023.10/.github/workflows/000077500000000000000000000000001452011332500206635ustar00rootroot00000000000000crispy-bootstrap5-2023.10/.github/workflows/publish.yml000066400000000000000000000030361452011332500230560ustar00rootroot00000000000000name: Publish Python Package on: release: types: [created] jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel setuptools python -m pip install --upgrade tox - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) deploy: runs-on: ubuntu-latest needs: [tests] steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: "3.10" - uses: actions/cache@v2 name: Configure pip caching with: path: ~/.cache/pip key: ${{ runner.os }}-publish-pip-${{ hashFiles('**/setup.py') }} restore-keys: | ${{ runner.os }}-publish-pip- - name: Install dependencies run: | pip install build twine - name: Publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: | python -m build twine upload dist/* crispy-bootstrap5-2023.10/.github/workflows/test.yml000066400000000000000000000022341452011332500223660ustar00rootroot00000000000000name: Test on: push: branches: - main pull_request: jobs: tests: name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip wheel setuptools python -m pip install --upgrade tox - name: Run tox targets for ${{ matrix.python-version }} run: tox run -f py$(echo ${{ matrix.python-version }} | tr -d .) lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.11" - name: Install dependencies run: | python -m pip install --upgrade pip tox - name: Run lint run: tox -e lint crispy-bootstrap5-2023.10/.gitignore000066400000000000000000000001571452011332500172610ustar00rootroot00000000000000.venv __pycache__/ *.py[cod] *$py.class venv .eggs .pytest_cache *.egg-info .DS_Store .vscode *python-version crispy-bootstrap5-2023.10/.pre-commit-config.yaml000066400000000000000000000004231452011332500215460ustar00rootroot00000000000000repos: - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black - repo: https://github.com/pycqa/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/pycqa/flake8 rev: 6.0.0 hooks: - id: flake8crispy-bootstrap5-2023.10/CHANGELOG.md000066400000000000000000000043711452011332500171040ustar00rootroot00000000000000# CHANGELOG FOR CRISPY-BOOTSTRAP5 ## 2023.10 (2023-10-2023) * Added Django 5.0 and 4.2 support * Added Python 3.11 and 3.12 support * Dropped Python 3.7 support * Dropped Django 3.2, 4.0 and 4.1 support * Switched to CalVer versioning See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/8?closed=1) for full change log. ## 0.7 (2022-09-28) * Added Django 4.1 support See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestones?state=closed) for for change list. ## 0.6 (2021-09-28) * Added Django 4.0 support ## 0.5 (2021-08-20) * Added support for [Accordion Flush](https://getbootstrap.com/docs/5.0/components/accordion/#flush) and [Always Open](https://getbootstrap.com/docs/5.0/components/accordion/#always-open) (#63) * Added support for grouped inputs (#64) * Added support for clearable file field (#53) * Removed various `|safe` filters in templates See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/6?closed=1) for full changelog. ## 0.4 (2021-05-27) * Added support for Bootstrap 5 Floating Labels (#42) * Dropped support for Django 3.0 * Added support for Django 3.2 See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/5?closed=1) for full changelog. ## 0.3.1(2021-03-03) * Fixed classes for `row` layout object (#36) See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/4?closed=1) for full changelog. ## 0.3 (2021-02-21) * Fixed rendering of select widgets (#31) See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/3?closed=1) for full changelog. ## 0.2 (2021-01-31) * Tested for compatibility with Bootstrap5 Beta 1 * Fixed InlineField (#28) * Implemented new Bootstrap5 accordion (#24) * Improved tests and fixed rendering of blank attributes (#23) See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/2) for full changelog. ## 0.1 (2020-11-19) * Initial release, compatibility with Bootstrap5 Alpha 3 * Converted templates from Bootstrap 4, and initial set of fixes * Brought forward Bootstrap 4 test suite and updated for Bootstrap5 See [Milestones](https://github.com/django-crispy-forms/crispy-bootstrap5/milestone/1) for full changelog. crispy-bootstrap5-2023.10/LICENSE000066400000000000000000000020601452011332500162710ustar00rootroot00000000000000Copyright (c) 2020 David Smith 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.crispy-bootstrap5-2023.10/MANIFEST.in000066400000000000000000000001451452011332500170240ustar00rootroot00000000000000include LICENSE include MANIFEST.in include README.md recursive-include crispy_bootstrap5/templates *crispy-bootstrap5-2023.10/README.md000066400000000000000000000037571452011332500165610ustar00rootroot00000000000000# crispy-bootstrap5 [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/smithdc1/crispy-bootstrap5/blob/main/LICENSE) Bootstrap5 template pack for django-crispy-forms ## Installation Install this plugin using `pip`: ```bash $ pip install crispy-bootstrap5 ``` ## Usage You will need to update your project's settings file to add ``crispy_forms`` and ``crispy_bootstrap5`` to your projects ``INSTALLED_APPS``. Also set ``bootstrap5`` as and allowed template pack and as the default template pack for your project ```python INSTALLED_APPS = ( ... "crispy_forms", "crispy_bootstrap5", ... ) CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" ``` ## What's new? Bootstrap 5 introduces [floating labels](https://getbootstrap.com/docs/5.0/forms/floating-labels/). This template pack include a layout object to use this input type ```python from crispy_bootstrap5.bootstrap5 import FloatingField # then in your Layout ... Layout( FloatingField("first_name"), ) ``` Accordions also have new features, such as [Accordion flush](https://getbootstrap.com/docs/5.0/components/accordion/#flush) and [Always open](https://getbootstrap.com/docs/5.0/components/accordion/#always-open). There is a new layout object to use them ```python from crispy_bootstrap5.bootstrap5 import BS5Accordion # then in your Layout # if not informed, flush and always_open default to False ... Layout( BS5Accordion( AccordionGroup("group name", "form_field_1", "form_field_2"), AccordionGroup("another group name", "form_field"), flush=True, always_open=True ) ) ``` ## Development To contribute to this library, first checkout the code. Then create a new virtual environment: ```bash cd crispy-bootstrap5 python -mvenv venv source venv/bin/activate ``` Or if you are using `pipenv`: ```bash pipenv shell ``` Now install the dependencies and tests: ```bash pip install -e '.[test]' ``` To run the tests: ```bash pytest ``` crispy-bootstrap5-2023.10/crispy_bootstrap5/000077500000000000000000000000001452011332500207615ustar00rootroot00000000000000crispy-bootstrap5-2023.10/crispy_bootstrap5/__init__.py000066400000000000000000000000301452011332500230630ustar00rootroot00000000000000__version__ = "2023.10" crispy-bootstrap5-2023.10/crispy_bootstrap5/bootstrap5.py000066400000000000000000000016361452011332500234430ustar00rootroot00000000000000from crispy_forms.bootstrap import Accordion from crispy_forms.layout import Field class FloatingField(Field): template = "bootstrap5/layout/floating_field.html" class BS5Accordion(Accordion): """ Bootstrap5 Accordion menu object. It wraps `AccordionGroup` objects in a container. It also allows the usage of accordion-flush, introduced in bootstrap5:: BS5Accordion( AccordionGroup("group name", "form_field_1", "form_field_2"), AccordionGroup("another group name", "form_field"), flush=True, always_open=True ) """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.flush = kwargs.pop("flush", False) self.always_open = kwargs.pop("always_open", False) if self.always_open: for accordion_group in self.fields: accordion_group.always_open = True crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/000077500000000000000000000000001452011332500227575ustar00rootroot00000000000000crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/000077500000000000000000000000001452011332500250615ustar00rootroot00000000000000crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/accordion-group.html000066400000000000000000000012441452011332500310430ustar00rootroot00000000000000

{{ fields|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/accordion.html000066400000000000000000000002051452011332500277050ustar00rootroot00000000000000
{{ content|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/betterform.html000066400000000000000000000012341452011332500301200ustar00rootroot00000000000000{% 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 "bootstrap5/field.html" %} {% endif %} {% endfor %} {% if not forloop.last or not fieldset_open %}
{% endif %} {% endfor %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/display_form.html000066400000000000000000000004101452011332500304320ustar00rootroot00000000000000{% if form.form_html %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap5/errors.html" %} {% endif %} {{ form.form_html }} {% else %} {% include "bootstrap5/uni_form.html" %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/errors.html000066400000000000000000000004471452011332500272700ustar00rootroot00000000000000{% if form.non_field_errors %}
{% if form_error_title %}

{{ form_error_title }}

{% endif %}
{% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/errors_formset.html000066400000000000000000000004621452011332500310240ustar00rootroot00000000000000{% if formset.non_form_errors %}
{% if formset_error_title %}

{{ formset_error_title }}

{% endif %}
{% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/field.html000066400000000000000000000076261452011332500270450ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox and tag != "td" %}
{% if label_class %}
{% endif %} {% endif %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="mb-3{% if field|is_checkbox and form_show_labels %} form-check{% else %}{% if 'form-horizontal' in form_class %} row{% endif %}{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> {% if field.label and not field|is_checkbox and form_show_labels %} {# not field|is_radioselect in row below can be removed once Django 3.2 is no longer supported #} {% endif %} {% if field|is_checkboxselectmultiple or field|is_radioselect %} {% include 'bootstrap5/layout/radio_checkbox_select.html' %} {% endif %} {% if not field|is_checkboxselectmultiple and not field|is_radioselect %} {% if field|is_checkbox and form_show_labels %} {% if field.errors %} {% crispy_field field 'class' 'form-check-input is-invalid' %} {% else %} {% crispy_field field 'class' 'form-check-input' %} {% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% else %} {% if field_class %}
{% endif %} {% if field|is_file %} {% include 'bootstrap5/layout/field_file.html' %} {% elif field|is_select %} {% if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' %} {% else %} {% crispy_field field 'class' 'form-select' %} {% endif %} {% elif field|is_checkbox %} {% if field.errors %} {% crispy_field field 'class' 'form-check-input is-invalid' %} {% else %} {% crispy_field field 'class' 'form-check-input' %} {% endif %} {% elif field.errors %} {% crispy_field field 'class' 'form-control is-invalid' %} {% else %} {% crispy_field field 'class' 'form-control' %} {% endif %} {% if not field|is_file %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% endif %} {% if field_class %}
{% endif %} {% endif %} {% endif %} {% if field|is_checkbox and tag != "td" %} {% if label_class %}
{% endif %}
{% endif %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/inputs.html000066400000000000000000000006241452011332500272730ustar00rootroot00000000000000{% if inputs %}
{% if label_class %}
{% endif %}
{% for input in inputs %} {% include "bootstrap5/layout/baseinput.html" %} {% endfor %}
{% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/000077500000000000000000000000001452011332500263765ustar00rootroot00000000000000crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/alert.html000066400000000000000000000004711452011332500303750ustar00rootroot00000000000000crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/attrs.html000066400000000000000000000002541452011332500304220ustar00rootroot00000000000000{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/baseinput.html000066400000000000000000000006471452011332500312650ustar00rootroot00000000000000 crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/button.html000066400000000000000000000001031452011332500305710ustar00rootroot00000000000000 crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/buttonholder.html000066400000000000000000000003241452011332500317740ustar00rootroot00000000000000
{{ fields_output|safe }}
checkboxselectmultiple_inline.html000066400000000000000000000013571452011332500353130ustar00rootroot00000000000000crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout{% if field.is_hidden %} {{ field }} {% else %}
{% if field.label %} {% endif %} {% include 'bootstrap5/layout/radio_checkbox_select.html' %}
{% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/column.html000066400000000000000000000003671452011332500305670ustar00rootroot00000000000000
{{ fields|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/div.html000066400000000000000000000002621452011332500300460ustar00rootroot00000000000000
{{ fields|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/field_errors.html000066400000000000000000000003541452011332500317450ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %} {{ error }} {% endfor %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/field_errors_block.html000066400000000000000000000003461452011332500331200ustar00rootroot00000000000000{% if form_show_errors and field.errors %} {% for error in field.errors %}

{{ error }}

{% endfor %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/field_file.html000066400000000000000000000027471452011332500313600ustar00rootroot00000000000000{% load crispy_forms_field %} {% for widget in field.subwidgets %} {% if widget.data.is_initial %}
{{ widget.data.initial_text }}
{{ field.value.name }} {% if not widget.data.required %} {% endif %}
{% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% endfor %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/field_with_buttons.html000066400000000000000000000026341452011332500331650ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.label and form_show_labels %} {% endif %}
{% if field.errors %} {% crispy_field field 'class' 'form-control is-invalid' %} {% else %} {% crispy_field field 'class' 'form-control' %} {% endif %} {{ buttons|safe }}
{% for error in field.errors %}

{{ error }}

{% endfor %} {% include 'bootstrap5/layout/help_text.html' %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/fieldset.html000066400000000000000000000004661452011332500310710ustar00rootroot00000000000000
{% if legend %}{{ legend|safe }}{% endif %} {{ fields|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/floating_field.html000066400000000000000000000023521452011332500322340ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="form-floating mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> {% if field|is_select %} {%if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' 'placeholder' field.name %} {% else %} {% crispy_field field 'class' 'form-select' 'placeholder' field.name %} {% endif %} {% else %} {% if field.errors %} {% crispy_field field 'class' 'form-control is-invalid' 'placeholder' field.name %} {% else %} {% crispy_field field 'class' 'form-control' 'placeholder' field.name %} {% endif %} {% endif %} {% include 'bootstrap5/layout/help_text_and_errors.html' %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/formactions.html000066400000000000000000000004101452011332500316030ustar00rootroot00000000000000 {% if label_class %}
{% endif %}
{{ fields_output|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/help_text.html000066400000000000000000000005271452011332500312640ustar00rootroot00000000000000{% if field.help_text %} {% if help_text_inline %} {{ field.help_text|safe}} {% else %}
{{ field.help_text|safe }}
{% endif %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/help_text_and_errors.html000066400000000000000000000005761452011332500335060ustar00rootroot00000000000000{% if help_text_inline and not error_text_inline %} {% include 'bootstrap5/layout/help_text.html' %} {% endif %} {% if error_text_inline %} {% include 'bootstrap5/layout/field_errors.html' %} {% else %} {% include 'bootstrap5/layout/field_errors_block.html' %} {% endif %} {% if not help_text_inline %} {% include 'bootstrap5/layout/help_text.html' %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/inline_field.html000066400000000000000000000020121452011332500317000ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field|is_checkbox %}
{% crispy_field field 'class' 'form-check-input' %}
{% else %}
{% if field.errors %} {% crispy_field field 'class' 'form-control is-invalid' 'placeholder' field.label %} {% else %} {% crispy_field field 'class' 'form-control' 'placeholder' field.label %} {% endif %}
{% endif %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/multifield.html000066400000000000000000000010251452011332500314200ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %} {% if field.label %} {% endif %} {% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/prepended_appended_text.html000066400000000000000000000045521452011332500341440ustar00rootroot00000000000000{% load crispy_forms_field %} {% if field.is_hidden %} {{ field }} {% else %}
{% if field.label and form_show_labels %} {% endif %}
{# prepend #} {% if crispy_prepended_text %} {{ crispy_prepended_text }} {% endif %} {# input #} {% if field|is_select %} {% if field.errors %} {% crispy_field field 'class' 'form-select is-invalid' %} {% else %} {% crispy_field field 'class' 'form-select' %} {% endif %} {% elif field.errors %} {% crispy_field field 'class' 'form-control is-invalid' %} {% else %} {% crispy_field field 'class' 'form-control' %} {% endif %} {# append #} {% if crispy_appended_text %} {{ crispy_appended_text }} {% endif %} {% if error_text_inline %} {% include 'bootstrap5/layout/field_errors.html' %} {% else %} {% include 'bootstrap5/layout/field_errors_block.html' %} {% endif %}
{% if not help_text_inline %} {% include 'bootstrap5/layout/help_text.html' %} {% endif %}
{% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/radio_checkbox_select.html000066400000000000000000000024031452011332500335660ustar00rootroot00000000000000{% load crispy_forms_filters %} {% load l10n %}
{% for group, options, index in field|optgroups %} {% if group %}{{ group }}{% endif %} {% for option in options %}
{% if field.errors and forloop.last and not inline_class and forloop.parentloop.last %} {% include 'bootstrap5/layout/field_errors_block.html' %} {% endif %}
{% endfor %} {% endfor %}
{% if field.errors and inline_class %} {% for error in field.errors %}

{{ error }}

{% endfor %} {% endif %} {% include 'bootstrap5/layout/help_text.html' %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/radioselect_inline.html000066400000000000000000000013571452011332500331260ustar00rootroot00000000000000{% if field.is_hidden %} {{ field }} {% else %}
{% if field.label %} {% endif %} {% include 'bootstrap5/layout/radio_checkbox_select.html' %}
{% endif %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/row.html000066400000000000000000000002251452011332500300720ustar00rootroot00000000000000
{{ fields|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/tab-link.html000066400000000000000000000003161452011332500307650ustar00rootroot00000000000000 crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/tab.html000066400000000000000000000002551452011332500300340ustar00rootroot00000000000000 {{ links|safe }}
{{ content|safe }}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/layout/uneditable_input.html000066400000000000000000000012501452011332500326150ustar00rootroot00000000000000{% load crispy_forms_field %}
{% crispy_field field 'disabled' 'disabled' %} {% include 'bootstrap5/layout/help_text.html' %}
crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/table_inline_formset.html000066400000000000000000000040221452011332500321310ustar00rootroot00000000000000{% 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 }}{% if field.field.required and not field|is_checkbox %}*{% endif %} {% endif %} {% endfor %} {% endif %} {% for field in formset.empty_form %} {% include 'bootstrap5/field.html' with tag="td" form_show_labels=False %} {% endfor %} {% for form in formset %} {% if form_show_errors and not form.is_extra %} {% include "bootstrap5/errors.html" %} {% endif %} {% for field in form %} {% include 'bootstrap5/field.html' with tag="td" form_show_labels=False %} {% endfor %} {% endfor %} {% include "bootstrap5/inputs.html" %} {% if formset_tag %}{% endif %} {% endspecialspaceless %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/uni_form.html000066400000000000000000000004651452011332500275720ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if include_media %}{{ form.media }}{% endif %} {% if form_show_errors %} {% include "bootstrap5/errors.html" %} {% endif %} {% for field in form %} {% include field_template %} {% endfor %} {% endspecialspaceless %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/uni_formset.html000066400000000000000000000003461452011332500303040ustar00rootroot00000000000000{% with formset.management_form as form %} {% include 'bootstrap5/uni_form.html' %} {% endwith %} {% for form in formset %}
{% include 'bootstrap5/uni_form.html' %}
{% endfor %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/whole_uni_form.html000066400000000000000000000007151452011332500307660ustar00rootroot00000000000000{% load crispy_forms_utils %} {% specialspaceless %} {% if form_tag %}
{% endif %} {% if form_method|lower == 'post' and not disable_csrf %} {% csrf_token %} {% endif %} {% include "bootstrap5/display_form.html" %} {% include "bootstrap5/inputs.html" %} {% if form_tag %}
{% endif %} {% endspecialspaceless %} crispy-bootstrap5-2023.10/crispy_bootstrap5/templates/bootstrap5/whole_uni_formset.html000066400000000000000000000015141452011332500315000ustar00rootroot00000000000000{% 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 "bootstrap5/errors_formset.html" %} {% for form in formset %} {% include "bootstrap5/display_form.html" %} {% endfor %} {% if inputs %}
{% for input in inputs %} {% include "bootstrap5/layout/baseinput.html" %} {% endfor %}
{% endif %} {% if formset_tag %}
{% endif %} {% endspecialspaceless %} crispy-bootstrap5-2023.10/pyproject.toml000066400000000000000000000031131452011332500202000ustar00rootroot00000000000000[build-system] build-backend = "setuptools.build_meta" requires = [ "setuptools>=61", ] [project] name="crispy-bootstrap5" description="Bootstrap5 template pack for django-crispy-forms" readme = "README.md" license = {text = "MIT"} authors = [{name = "David Smith"}] requires-python = ">=3.8" classifiers=[ "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", ] dynamic = [ "version", ] dependencies = [ "django>=4.2", "django-crispy-forms>=2", ] [project.optional-dependencies] test = [ "pytest", "pytest-django", ] [project.urls] "CI" = "https://github.com/django-crispy-forms/crispy-bootstrap5/actions" "Changelog" = "https://github.com/django-crispy-forms/crispy-bootstrap5/releases" "Homepage" = "https://github.com/django-crispy-forms/crispy-bootstrap5" "Issues" = "https://github.com/django-crispy-forms/crispy-bootstrap5/issues" [tool.setuptools.dynamic] version = {attr = "crispy_bootstrap5.__version__"} [tool.isort] profile = "black" [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE= "tests.test_settings" crispy-bootstrap5-2023.10/test.html000066400000000000000000000167371452011332500171510ustar00rootroot00000000000000 Document
  • Passwords dont match
company email* password* re-enter password* first name* last name* date time*
Insert your email
This field is required.Insert your email This field is required. This field is required. This field is required. This field is required. This field is required.
crispy-bootstrap5-2023.10/tests/000077500000000000000000000000001452011332500164305ustar00rootroot00000000000000crispy-bootstrap5-2023.10/tests/__init__.py000066400000000000000000000000001452011332500205270ustar00rootroot00000000000000crispy-bootstrap5-2023.10/tests/forms.py000066400000000000000000000177061452011332500201430ustar00rootroot00000000000000from crispy_forms.helper import FormHelper from django import forms from django.db import models class SampleForm(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.SplitDateTimeField( label="date time", widget=forms.SplitDateTimeWidget() ) def clean(self): super().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 SampleForm2(SampleForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) class CheckboxesSampleForm(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 SampleForm3(forms.ModelForm): class Meta: model = CrispyTestModel fields = ["email", "password"] exclude = ["password"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) class SampleForm4(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 fields = "__all__" # eliminate RemovedInDjango18Warning class SampleForm5(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 SampleFormWithMedia(forms.Form): class Media: css = {"all": ("test.css",)} js = ("test.js",) class SampleFormWithMultiValueField(forms.Form): multi = forms.SplitDateTimeField() class CrispyEmptyChoiceTestModel(models.Model): fruit = models.CharField( choices=[("apple", "Apple"), ("pear", "Pear")], null=True, blank=True, max_length=20, ) class SampleForm6(forms.ModelForm): class Meta: """ When allowing null=True in a model field, the corresponding field will have a choice for the empty value. When the form is initialized by an instance with initial value None, this choice should be selected. """ model = CrispyEmptyChoiceTestModel fields = ["fruit"] widgets = {"fruit": forms.RadioSelect()} class SampleForm7(forms.ModelForm): is_company = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) password2 = forms.CharField( label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput(), ) class Meta: model = CrispyTestModel fields = ("email", "password", "password2") class SampleForm8(forms.ModelForm): is_company = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) password2 = forms.CharField( label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput(), ) class Meta: model = CrispyTestModel fields = ("email", "password2", "password") class FakeFieldFile: """ Quacks like a FieldFile (has a .url and string representation), but doesn't require us to care about storages etc. """ url = "something" def __str__(self): return self.url class FileForm(forms.Form): file_field = forms.FileField(widget=forms.FileInput) clearable_file = forms.FileField( widget=forms.ClearableFileInput, required=False, initial=FakeFieldFile() ) class FileFormRequired(forms.Form): file_field = forms.FileField(required=True, widget=forms.FileInput) clearable_file = forms.FileField(required=True, widget=forms.ClearableFileInput) class InputsForm(forms.Form): choices = ((1, "Option one"), (2, "Option two"), (3, "Option three")) text_input = forms.CharField() text_area = forms.CharField(widget=forms.Textarea()) checkboxes = forms.MultipleChoiceField( choices=choices, initial=(1,), widget=forms.CheckboxSelectMultiple, ) radio = forms.ChoiceField(widget=forms.RadioSelect, choices=choices) single_checkbox = forms.CharField( label="company", required=False, widget=forms.CheckboxInput() ) select_input = forms.ChoiceField(choices=choices) class LabelForm(forms.Form): text_input = forms.CharField(label="Test html escape <>&") class CustomRadioSelect(forms.RadioSelect): pass class CustomCheckboxSelectMultiple(forms.CheckboxSelectMultiple): pass class SampleFormCustomWidgets(forms.Form): inline_radios = forms.ChoiceField( choices=( ("option_one", "Option one"), ("option_two", "Option two"), ), widget=CustomRadioSelect, initial="option_two", ) checkboxes = forms.MultipleChoiceField( choices=((1, "Option one"), (2, "Option two"), (3, "Option three")), initial=(1,), widget=CustomCheckboxSelectMultiple, ) class GroupedChoiceForm(forms.Form): choices = [ ( "Audio", [ ("vinyl", "Vinyl"), ("cd", "CD"), ], ), ( "Video", [ ("vhs", "VHS Tape"), ("dvd", "DVD"), ], ), ("unknown", "Unknown"), ] checkbox_select_multiple = forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple, choices=choices, help_text="help", ) radio = forms.MultipleChoiceField(widget=forms.RadioSelect, choices=choices) class HelpTextForm(forms.Form): email = forms.EmailField( label="email", required=True, widget=forms.TextInput(), help_text="Insert your email", ) crispy-bootstrap5-2023.10/tests/results/000077500000000000000000000000001452011332500201315ustar00rootroot00000000000000crispy-bootstrap5-2023.10/tests/results/accordion.html000066400000000000000000000037471452011332500227730ustar00rootroot00000000000000

crispy-bootstrap5-2023.10/tests/results/accordion_always_open.html000066400000000000000000000036451452011332500253710ustar00rootroot00000000000000

crispy-bootstrap5-2023.10/tests/results/accordion_flush.html000066400000000000000000000037671452011332500241760ustar00rootroot00000000000000

crispy-bootstrap5-2023.10/tests/results/alert.html000066400000000000000000000004071452011332500221270ustar00rootroot00000000000000 crispy-bootstrap5-2023.10/tests/results/checkboxes.html000066400000000000000000000020051452011332500231320ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/field_with_buttons.html000066400000000000000000000011201452011332500247050ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2023.10/tests/results/field_with_buttons_failing.html000066400000000000000000000011371452011332500264060ustar00rootroot00000000000000

This field is required.

crispy-bootstrap5-2023.10/tests/results/field_with_buttons_failing_lt50.html000066400000000000000000000011131452011332500272440ustar00rootroot00000000000000

This field is required.

crispy-bootstrap5-2023.10/tests/results/field_with_buttons_lt50.html000066400000000000000000000010531452011332500255560ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2023.10/tests/results/flat_attrs.html000066400000000000000000000000541452011332500231610ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/help_text_escape.html000066400000000000000000000006321452011332500243340ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2023.10/tests/results/help_text_escape_lt50.html000066400000000000000000000005651452011332500252050ustar00rootroot00000000000000
Insert your email
crispy-bootstrap5-2023.10/tests/results/radio.html000066400000000000000000000021721452011332500221170ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/row.html000066400000000000000000000015531452011332500216320ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/single_checkbox.html000066400000000000000000000005761452011332500241560ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_bootstrap5_form_inline.html000066400000000000000000000017241452011332500265450ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_bootstrap5_form_inline_lt50.html000066400000000000000000000016571452011332500274160ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_clearable_file_field.html000066400000000000000000000020331452011332500261300ustar00rootroot00000000000000
Currently
crispy-bootstrap5-2023.10/tests/results/test_clearable_file_field_failing.html000066400000000000000000000013641452011332500276270ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2023.10/tests/results/test_clearable_file_field_failing_lt50.html000066400000000000000000000011331452011332500304650ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2023.10/tests/results/test_file_field.html000066400000000000000000000006211452011332500241370ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_file_field_failing.html000066400000000000000000000013341452011332500256320ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2023.10/tests/results/test_file_field_failing_lt50.html000066400000000000000000000011031452011332500264700ustar00rootroot00000000000000
This field is required.
crispy-bootstrap5-2023.10/tests/results/test_floating_field.html000066400000000000000000000005421452011332500250250ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_floating_field_failing.html000066400000000000000000000022261452011332500265170ustar00rootroot00000000000000
This field is required.
This field is required.
crispy-bootstrap5-2023.10/tests/results/test_floating_field_failing_lt50.html000066400000000000000000000021561452011332500273650ustar00rootroot00000000000000
This field is required.
This field is required.
crispy-bootstrap5-2023.10/tests/results/test_grouped_checkboxes.html000066400000000000000000000034511452011332500257240ustar00rootroot00000000000000
Audio
Video
help
crispy-bootstrap5-2023.10/tests/results/test_grouped_checkboxes_failing.html000066400000000000000000000041531452011332500274150ustar00rootroot00000000000000
Audio
Video

This field is required.

help
crispy-bootstrap5-2023.10/tests/results/test_grouped_checkboxes_failing_lt50.html000066400000000000000000000040071452011332500302570ustar00rootroot00000000000000
Audio
Video

This field is required.

help
crispy-bootstrap5-2023.10/tests/results/test_grouped_radios.html000066400000000000000000000025431452011332500250700ustar00rootroot00000000000000
Audio
Video
crispy-bootstrap5-2023.10/tests/results/test_grouped_radios_failing.html000066400000000000000000000041261452011332500265600ustar00rootroot00000000000000
Audio
Video

This field is required.

crispy-bootstrap5-2023.10/tests/results/test_grouped_radios_failing_lt50.html000066400000000000000000000037621452011332500274310ustar00rootroot00000000000000
Audio
Video

This field is required.

crispy-bootstrap5-2023.10/tests/results/test_inline_field.html000066400000000000000000000012711452011332500245000ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_prepended_appended_select.html000066400000000000000000000035421452011332500272270ustar00rootroot00000000000000
bar
bar
£ .00
crispy-bootstrap5-2023.10/tests/results/test_prepended_appended_text.html000066400000000000000000000030451452011332500267320ustar00rootroot00000000000000
@<>&gmail.com
Insert your email
#
$
crispy-bootstrap5-2023.10/tests/results/test_prepended_appended_text_lt50.html000066400000000000000000000030001452011332500275650ustar00rootroot00000000000000
@<>&gmail.com
Insert your email
#
$
crispy-bootstrap5-2023.10/tests/results/test_select.html000066400000000000000000000007331452011332500233400ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/test_tabular_formset_layout.html000066400000000000000000000202701452011332500266450ustar00rootroot00000000000000
company email* password* re-enter password* first name* last name* date time*
Insert your email
Insert your email
Insert your email
Insert your email
crispy-bootstrap5-2023.10/tests/results/test_tabular_formset_layout_failing.html000066400000000000000000000154351452011332500303450ustar00rootroot00000000000000
  • Passwords dont match
company email* password* re-enter password* first name* last name* date time*
Insert your email
This field is required.
Insert your email
This field is required. This field is required. This field is required. This field is required. This field is required.
crispy-bootstrap5-2023.10/tests/results/test_tabular_formset_layout_failing_lt50.html000066400000000000000000000150571452011332500312110ustar00rootroot00000000000000
  • Passwords dont match
company email* password* re-enter password* first name* last name* date time*
Insert your email
This field is required.
Insert your email
This field is required. This field is required. This field is required. This field is required. This field is required.
crispy-bootstrap5-2023.10/tests/results/test_tabular_formset_layout_lt50.html000066400000000000000000000177141452011332500275220ustar00rootroot00000000000000
company email* password* re-enter password* first name* last name* date time*
Insert your email
Insert your email
Insert your email
Insert your email
crispy-bootstrap5-2023.10/tests/results/text_area.html000066400000000000000000000006031452011332500227720ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/results/text_input.html000066400000000000000000000006151452011332500232240ustar00rootroot00000000000000
crispy-bootstrap5-2023.10/tests/templates/000077500000000000000000000000001452011332500204265ustar00rootroot00000000000000crispy-bootstrap5-2023.10/tests/templates/crispy_render_template.html000066400000000000000000000000601452011332500260530ustar00rootroot00000000000000{% load crispy_forms_tags %} {% crispy form %} crispy-bootstrap5-2023.10/tests/templates/custom_field_template.html000066400000000000000000000001041452011332500256570ustar00rootroot00000000000000

Special custom field

{% include 'bootstrap5/field.html' %} crispy-bootstrap5-2023.10/tests/templates/custom_form_template.html000066400000000000000000000001141452011332500255400ustar00rootroot00000000000000

Special custom form

{% include "bootstrap5/whole_uni_form.html" %} crispy-bootstrap5-2023.10/tests/templates/custom_form_template_with_context.html000066400000000000000000000002311452011332500303370ustar00rootroot00000000000000

Special custom form with context passthrough

Got prefix: {{ prefix }}. {% include "bootstrap5/whole_uni_form.html" %} Got suffix: {{ suffix }}. crispy-bootstrap5-2023.10/tests/test_form_helper.py000066400000000000000000000467051452011332500223570ustar00rootroot00000000000000import re import pytest from crispy_forms.bootstrap import ( AppendedText, FieldWithButtons, PrependedAppendedText, PrependedText, StrictButton, ) from crispy_forms.helper import FormHelper, FormHelpersException from crispy_forms.layout import Button, Hidden, Layout, Reset, Submit from crispy_forms.templatetags.crispy_forms_tags import CrispyFormNode from crispy_forms.utils import render_crispy_form from django import forms from django.forms.models import formset_factory from django.middleware.csrf import _get_new_csrf_string from django.template import Context, Template from django.urls import reverse from django.utils.translation import gettext_lazy as _ from .forms import SampleForm, SampleForm7, SampleForm8, SampleFormWithMedia def test_inputs(): 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 = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "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 assert 'class="btn"' in html assert "btn btn-primary" in html assert "btn btn-inverse" in html assert len(re.findall(r"]+> <", html)) == 9 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 = Template( """ {% load crispy_forms_tags %} {% crispy testForm form_helper %} """ ) # now we render it, with errors form = SampleForm({"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 str(_("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 str(_("This field is required.")) not in html assert "error" not in html def test_html5_required(): form = SampleForm() 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 len(re.findall(r"\brequired\b", html)) == 7 form = SampleForm() form.helper = FormHelper() form.helper.html5_required = False html = render_crispy_form(form) def test_media_is_included_by_default_with_bootstrap5(): form = SampleFormWithMedia() form.helper = FormHelper() form.helper.template_pack = "bootstrap5" 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_bootstrap5(): form = SampleFormWithMedia() form.helper = FormHelper() form.helper.template_pack = "bootstrap5" 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 = SampleForm() 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(): template = Template( """ {% load crispy_forms_tags %} {% crispy form %} """ ) c = Context({"form": SampleForm()}) html = template.render(c) # Lets make sure everything loads right assert "') assert contains_partial(html, '') def test_render_required_fields(): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout("email") test_form.helper.render_required_fields = True html = render_crispy_form(test_form) assert html.count("Special custom form" in html def test_helper_custom_field_template(): form = SampleForm() 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 def test_helper_custom_field_template_no_layout(): form = SampleForm() form.helper = FormHelper() form.helper.field_template = "custom_field_template.html" html = render_crispy_form(form) for field in form.fields: assert html.count('id="div_id_%s"' % field) == 1 assert html.count("

    Special custom field

    ") == len(form.fields) def test_helper_std_field_template_no_layout(): form = SampleForm() form.helper = FormHelper() html = render_crispy_form(form) for field in form.fields: assert html.count('id="div_id_%s"' % field) == 1 def test_bootstrap_form_show_errors_bs5(): form = SampleForm( { "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") == 3 form.helper.form_show_errors = False html = render_crispy_form(form) assert html.count("error") == 0 def test_error_text_inline(): form = SampleForm({"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 = "invalid-feedback" help_tag_name = "div" matches = re.findall( r'') error_position = html.find('

    ') assert help_position < error_position # Viceversa form = SampleForm({"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 def test_form_show_labels(): form = SampleForm() 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("' in html assert '
    ' in html assert html.count("col-lg-8") == 7 # TODO FIX THIS TEST # assert "offset" not in html form.helper.label_class = "col-sm-3 col-md-4" form.helper.field_class = "col-sm-8 col-md-6" html = render_crispy_form(form) assert '
    ' in html assert '
    ' in html assert html.count("col-sm-8") == 7 # TODO FIX THIS TEST # assert "offset" not in html def test_label_class_and_field_class_bs5_offset_when_horizontal(): # Test col-XX-YY pattern form = SampleForm() form.helper = FormHelper() form.helper.label_class = "col-lg-2" form.helper.field_class = "col-lg-8" form.helper.form_class = "form-horizontal" html = render_crispy_form(form) assert '
    ' in html assert '
    ' in html assert html.count("col-lg-8") == 7 # Test multi col-XX-YY pattern and col-X pattern form.helper.label_class = "col-sm-3 col-md-4 col-5 col-lg-4" form.helper.field_class = "col-sm-8 col-md-6 col-7 col-lg-8" html = render_crispy_form(form) assert '
    ' in html assert ( '
    ' in html ) assert html.count("col-sm-8") == 7 assert html.count("col-md-6") == 7 assert html.count("col-7") == 7 assert html.count("col-lg-8") == 7 def test_form_group_with_form_inline_bs5(): form = SampleForm() form.helper = FormHelper() html = render_crispy_form(form) assert '
    ' in html # .row class shouldn't be together with .form-group in inline forms form = SampleForm() form.helper = FormHelper() form.helper.form_class = "form-inline" form.helper.field_template = "bootstrap5/layout/inline_field.html" html = render_crispy_form(form) assert '
    ' not in html def test_passthrough_context(): """ Test to ensure that context is passed through implicitly from outside of the crispy form into the crispy form templates. """ form = SampleForm() form.helper = FormHelper() form.helper.template = "custom_form_template_with_context.html" c = {"prefix": "foo", "suffix": "bar"} html = render_crispy_form(form, helper=form.helper, context=c) assert "Got prefix: foo" in html assert "Got suffix: bar" in html crispy-bootstrap5-2023.10/tests/test_layout.py000066400000000000000000000461251452011332500213660ustar00rootroot00000000000000import django import pytest from crispy_forms.bootstrap import ( AppendedText, Field, FieldWithButtons, InlineCheckboxes, PrependedAppendedText, PrependedText, StrictButton, ) from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Column, Fieldset, Layout, Row from crispy_forms.utils import render_crispy_form from django import forms from django.forms.models import formset_factory, modelformset_factory from django.middleware.csrf import _get_new_csrf_string from django.shortcuts import render from django.template import Context, Template from django.test import override_settings from django.urls import reverse from django.utils.translation import gettext_lazy as _ from .forms import ( CheckboxesSampleForm, CrispyEmptyChoiceTestModel, CrispyTestModel, FileForm, FileFormRequired, HelpTextForm, InputsForm, LabelForm, SampleForm, SampleForm2, SampleForm3, SampleForm4, SampleForm6, ) from .utils import contains_partial, parse_expected, parse_form CONVERTERS = { "textinput": "inputtext textinput textInput", "fileinput": "fileinput fileUpload", "passwordinput": "textinput textInput", } def test_invalid_unicode_characters(settings): # Adds a BooleanField that uses non valid unicode characters "ñ" form_helper = FormHelper() form_helper.add_layout(Layout("españa")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) settings.CRISPY_FAIL_SILENTLY = False with pytest.raises(Exception): template.render(c) def test_unicode_form_field(): class UnicodeForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["contraseña"] = forms.CharField() helper = FormHelper() helper.layout = Layout("contraseña") html = render_crispy_form(UnicodeForm()) assert 'id="id_contraseña"' in html def test_meta_extra_fields_with_missing_fields(): class FormWithMeta(SampleForm): class Meta: fields = ("email", "first_name", "last_name") form = FormWithMeta() # We remove email field on the go del form.fields["email"] form_helper = FormHelper() form_helper.layout = Layout("first_name") template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": form, "form_helper": form_helper}) html = template.render(c) assert "email" not in html def test_layout_unresolved_field(settings): form_helper = FormHelper() form_helper.add_layout(Layout("typo")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) settings.CRISPY_FAIL_SILENTLY = False with pytest.raises(Exception): template.render(c) def test_double_rendered_field(settings): form_helper = FormHelper() form_helper.add_layout(Layout("is_company", "is_company")) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context({"form": SampleForm(), "form_helper": form_helper}) settings.CRISPY_FAIL_SILENTLY = False with pytest.raises(Exception): template.render(c) def test_context_pollution(): class ExampleForm(forms.Form): comment = forms.CharField() form = ExampleForm() form2 = SampleForm() template = Template( """ {% load crispy_forms_tags %} {{ form.as_ul }} {% crispy form2 %} {{ form.as_ul }} """ ) c = Context({"form": form, "form2": form2}) html = template.render(c) assert html.count('name="comment"') == 2 assert html.count('name="is_company"') == 1 def test_layout_fieldset_row_html_with_unicode_fieldnames(): form_helper = FormHelper() form_helper.add_layout( Layout( Fieldset( "Company Data", "is_company", css_id="fieldset_company_data", css_class="fieldsets", title="fieldset_title", test_fieldset="123", ), Fieldset( "User Data", "email", Row( "password1", "password2", css_id="row_passwords", css_class="rows", ), HTML('test link'), HTML( """ {% if flag %}{{ message }}{% endif %} """ ), "first_name", "last_name", ), ) ) template = Template( """ {% load crispy_forms_tags %} {% crispy form form_helper %} """ ) c = Context( { "form": SampleForm(), "form_helper": form_helper, "flag": True, "message": "Hello!", } ) html = template.render(c) assert 'id="fieldset_company_data"' in html assert 'class="fieldsets' in html assert 'title="fieldset_title"' in html assert 'test-fieldset="123"' in html assert 'id="row_passwords"' in html assert html.count("click me"""), ) ), ) ) if django.VERSION < (5, 0): expected = "field_with_buttons_lt50.html" else: expected = "field_with_buttons.html" assert parse_form(form) == parse_expected(expected) form = SampleForm3({}) form.helper = FormHelper() form.helper.add_layout( Layout( Column( FieldWithButtons( "email", StrictButton("Go!", css_class="btn-outline-secondary") ) ), ) ) if django.VERSION < (5, 0): expected = "field_with_buttons_failing_lt50.html" else: expected = "field_with_buttons_failing.html" assert parse_form(form) == parse_expected(expected) @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_formset_layout(): SampleFormSet = formset_factory(SampleForm, extra=3) formset = SampleFormSet() helper = FormHelper() helper.form_id = "thisFormsetRocks" helper.form_class = "formsets-that-rock" helper.form_method = "POST" helper.form_action = "simpleAction" helper.layout = Layout( Fieldset( "Item {{ forloop.counter }}", "is_company", "email", ), HTML("{% if forloop.first %}Note for first form only{% endif %}"), Row("password1", "password2"), Fieldset("", "first_name", "last_name"), ) html = render_crispy_form( form=formset, helper=helper, context={"csrf_token": _get_new_csrf_string()} ) # Check formset fields assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert html.count("hidden") == 5 # Check form structure assert html.count("', ) assert contains_partial( html, '', ) assert contains_partial( html, '', ) assert html.count('name="form-0-email"') == 1 assert html.count('name="form-1-email"') == 1 assert html.count('name="form-2-email"') == 1 assert html.count('name="form-3-email"') == 0 assert html.count("password") == 0 def test_i18n(): template = Template( """ {% load crispy_forms_tags %} {% crispy form form.helper %} """ ) form = SampleForm() form_helper = FormHelper() form_helper.layout = Layout( HTML(_("i18n text")), Fieldset( _("i18n legend"), "first_name", "last_name", ), ) form.helper = form_helper html = template.render(Context({"form": form})) assert html.count("i18n legend") == 1 def test_default_layout(): test_form = SampleForm2() assert test_form.helper.layout.fields == [ "is_company", "email", "password1", "password2", "first_name", "last_name", "datetime_field", ] def test_default_layout_two(): test_form = SampleForm3() assert test_form.helper.layout.fields == ["email"] def test_modelform_layout_without_meta(): test_form = SampleForm4() test_form.helper = FormHelper() test_form.helper.layout = Layout("email") html = render_crispy_form(test_form) assert "email" in html assert "password" not in html def test_specialspaceless_not_screwing_intended_spaces(): # see issue #250 test_form = SampleForm() test_form.fields["email"].widget = forms.Textarea() test_form.helper = FormHelper() test_form.helper.layout = Layout( "email", HTML("first span second span") ) html = render_crispy_form(test_form) assert "first span second span" in html def test_choice_with_none_is_selected(): # see issue #701 model_instance = CrispyEmptyChoiceTestModel() model_instance.fruit = None test_form = SampleForm6(instance=model_instance) html = render_crispy_form(test_form) assert "checked" in html def test_keepcontext_context_manager(): # Test case for issue #180 # Apparently it only manifest when using render_to_response this exact way form = CheckboxesSampleForm() form.helper = FormHelper() # We use here InlineCheckboxes as it updates context in an unsafe way form.helper.layout = Layout( "checkboxes", InlineCheckboxes("alphacheckboxes"), "numeric_multiple_checkboxes" ) context = {"form": form} response = render( request=None, template_name="crispy_render_template.html", context=context ) print(response.content) assert response.content.count(b"form-check-inline") == 3 assert response.content.count(b"form-check-input") > 0 @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_bootstrap5_form_inline(): form = SampleForm() form.helper = FormHelper() form.helper.form_class = "form-inline" form.helper.field_template = "bootstrap5/layout/inline_field.html" form.helper.layout = Layout("email", "password1", "last_name") form.helper.form_class = "row row-cols-lg-auto" if django.VERSION < (5, 0): expected = "test_bootstrap5_form_inline_lt50.html" else: expected = "test_bootstrap5_form_inline.html" assert parse_form(form) == parse_expected(expected) def test_select(): form = InputsForm() form.helper = FormHelper() form.helper.layout = Layout("select_input") assert parse_form(form) == parse_expected("test_select.html") def test_select_prepended(): form = InputsForm() form.helper = FormHelper() form.helper.layout = Layout( PrependedText("select_input", "bar"), AppendedText("select_input", "bar"), PrependedAppendedText("select_input", "£", ".00"), ) assert parse_form(form) == parse_expected("test_prepended_appended_select.html") def test_update_attributes_class(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout("email", Field("password1"), "password2") form.helper["password1"].update_attributes(css_class="hello") html = render_crispy_form(form) assert html.count(' class="hello') == 1 form.helper = FormHelper() form.helper.layout = Layout( "email", Field("password1", css_class="hello"), "password2", ) form.helper["password1"].update_attributes(css_class="hello2") html = render_crispy_form(form) assert html.count(' class="hello hello2') == 1 def test_file_field(): form = FileForm() form.helper = FormHelper() form.helper.layout = Layout("clearable_file") assert parse_form(form) == parse_expected("test_clearable_file_field.html") form.helper.layout = Layout("file_field") assert parse_form(form) == parse_expected("test_file_field.html") form = FileFormRequired({}) form.helper = FormHelper() form.helper.layout = Layout("clearable_file") if django.VERSION < (5, 0): expected = "test_clearable_file_field_failing_lt50.html" else: expected = "test_clearable_file_field_failing.html" assert parse_form(form) == parse_expected(expected) form.helper.layout = Layout("file_field") if django.VERSION < (5, 0): expected = "test_file_field_failing_lt50.html" else: expected = "test_file_field_failing.html" assert parse_form(form) == parse_expected(expected) def test_row(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout( Row( Column("first_name"), Column("last_name"), ) ) assert parse_form(form) == parse_expected("row.html") def test_html_label_escape(): form = LabelForm() form.helper = FormHelper() form.helper.layout = Layout("text_input") html = render_crispy_form(form) assert "<>&" in html @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_tabular_formset_layout(): SampleFormSet = formset_factory(SampleForm, extra=3) formset = SampleFormSet() formset.helper = FormHelper() formset.helper.template = "bootstrap5/table_inline_formset.html" if django.VERSION < (5, 0): expected = "test_tabular_formset_layout_lt50.html" else: expected = "test_tabular_formset_layout.html" assert parse_form(formset) == parse_expected(expected) SampleFormSet = formset_factory(SampleForm, extra=3) data = { "form-TOTAL_FORMS": "1", "form-INITIAL_FORMS": "0", } formset = SampleFormSet(data) formset.helper = FormHelper() formset.helper.template = "bootstrap5/table_inline_formset.html" if django.VERSION < (5, 0): expected = "test_tabular_formset_layout_failing_lt50.html" else: expected = "test_tabular_formset_layout_failing.html" assert parse_form(formset) == parse_expected(expected) def test_flat_attrs_safe(): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout( Row( aria_labelledby="test<>%", ) ) form.helper.form_tag = False assert parse_form(form) == parse_expected("flat_attrs.html") def test_help_text_no_escape(): form = HelpTextForm() form.helper = FormHelper() form.helper.form_tag = False if django.VERSION < (5, 0): expected = "help_text_escape_lt50.html" else: expected = "help_text_escape.html" assert parse_form(form) == parse_expected(expected) crispy-bootstrap5-2023.10/tests/test_layout_objects.py000066400000000000000000000454741452011332500231050ustar00rootroot00000000000000import random import django import pytest from crispy_forms.bootstrap import ( Accordion, AccordionGroup, Alert, AppendedText, FieldWithButtons, InlineCheckboxes, InlineField, InlineRadios, PrependedAppendedText, PrependedText, StrictButton, Tab, TabHolder, ) from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Field, Layout, MultiWidgetField from crispy_forms.utils import render_crispy_form from django import forms from django.template import Context, Template from django.test import override_settings from django.utils.translation import activate, deactivate from django.utils.translation import gettext as _ from crispy_bootstrap5.bootstrap5 import BS5Accordion, FloatingField from .forms import ( CheckboxesSampleForm, CustomCheckboxSelectMultiple, CustomRadioSelect, GroupedChoiceForm, InputsForm, SampleForm, SampleFormCustomWidgets, ) from .utils import parse_expected, parse_form CONVERTERS = { "textinput": "inputtext textinput textInput", "fileinput": "fileinput fileUpload", "passwordinput": "textinput textInput", } def test_field_with_custom_template(): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( Field("email", template="custom_field_template.html") ) html = render_crispy_form(test_form) assert "

    Special custom field

    " in html def test_multiwidget_field(): template = Template( """ {% load crispy_forms_tags %} {% crispy form %} """ ) test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( MultiWidgetField( "datetime_field", attrs=( {"rel": "test_dateinput"}, {"rel": "test_timeinput", "style": "width: 30px;", "type": "hidden"}, ), ) ) c = Context({"form": test_form}) html = template.render(c) assert html.count('class="dateinput') == 1 assert html.count('rel="test_dateinput"') == 1 assert html.count('rel="test_timeinput"') == 2 assert html.count('style="width: 30px;"') == 2 assert html.count('type="hidden"') == 2 def test_field_type_hidden(): template = Template( """ {% load crispy_forms_tags %} {% crispy test_form %} """ ) test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( Field("email", type="hidden", data_test=12), Field("datetime_field"), ) c = Context({"test_form": test_form}) html = template.render(c) # Check form parameters assert html.count('data-test="12"') == 1 assert html.count('name="email"') == 1 assert html.count('class="dateinput') == 1 assert html.count('class="timeinput') == 1 def test_field_wrapper_class(settings): form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout(Field("email", wrapper_class="testing")) html = render_crispy_form(form) if settings.CRISPY_TEMPLATE_PACK == "bootstrap": assert html.count('class="control-group testing"') == 1 elif settings.CRISPY_TEMPLATE_PACK in ("bootstrap3", "bootstrap4"): assert html.count('class="form-group testing"') == 1 def test_html_with_carriage_returns(settings): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( HTML( """ if (a==b){ // some comment a+1; foo(); } """ ) ) html = render_crispy_form(test_form) if settings.CRISPY_TEMPLATE_PACK == "uni_form": assert html.count("\n") == 23 elif settings.CRISPY_TEMPLATE_PACK == "bootstrap": assert html.count("\n") == 25 else: assert html.count("\n") == 27 def test_i18n(): activate("es") form = SampleForm() form.helper = FormHelper() form.helper.layout = Layout(HTML(_("Enter a valid value."))) html = render_crispy_form(form) assert "Introduzca un valor válido" in html deactivate() def test_remove_labels(): form = SampleForm() # remove boolean field as label is still printed in boostrap del form.fields["is_company"] for fields in form: fields.label = False html = render_crispy_form(form) assert "&", "gmail.com", css_class="form-control-lg" ), AppendedText("password1", "#"), PrependedText("password2", "$"), ) if django.VERSION < (5, 0): expected = "test_prepended_appended_text_lt50.html" else: expected = "test_prepended_appended_text.html" assert parse_form(test_form) == parse_expected(expected) def test_inline_radios(self): test_form = CheckboxesSampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout(InlineRadios("inline_radios")) html = render_crispy_form(test_form) assert html.count('form-check-inline"') == 2 @override_settings(CRISPY_CLASS_CONVERTERS=CONVERTERS) def test_accordion_and_accordiongroup(self): random.seed(0) form = SampleForm() form.helper = FormHelper() form.helper.form_tag = False form.helper.layout = Layout( Accordion( AccordionGroup("one", "first_name"), AccordionGroup("two", "password1", "password2"), ) ) assert parse_form(form) == parse_expected("accordion.html") def test_accordion_active_false_not_rendered(self): test_form = SampleForm() test_form.helper = FormHelper() test_form.helper.layout = Layout( Accordion( AccordionGroup("one", "first_name"), # there is no ``active`` kwarg here. ) ) # The first time, there should be one of them there. html = render_crispy_form(test_form) accordion_class = "collapse show" assert ( html.count('
    " ) == 1 ) assert html.count("tab-pane") == 2 assert html.count('class="tab-pane first-tab-class active"') == 1 assert html.count('
    everything is fine test_form = SampleForm() html = render_crispy_form(test_form) # second render of form => first tab should be active, # but not duplicate class test_form = SampleForm() html = render_crispy_form(test_form) assert html.count('class="nav-item active active"') == 0 # render a new form, now with errors test_form = SampleForm(data={"val1": "foo"}) html = render_crispy_form(test_form) tab_class = "tab-pane" # if settings.CRISPY_TEMPLATE_PACK == 'bootstrap4': # tab_class = 'nav-link' # else: # tab_class = 'tab-pane' # tab 1 should not be active assert html.count('