Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for crispy forms with custom layouts #4

Open
mschoettle opened this issue Apr 21, 2023 · 6 comments · May be fixed by #11
Open

Support for crispy forms with custom layouts #4

mschoettle opened this issue Apr 21, 2023 · 6 comments · May be fixed by #11

Comments

@mschoettle
Copy link

In our project we use django-crispy-forms and define the form layout in the form's __init__.

For the example in this repo it could look as follows (using the Bootstrap 5 template pack) to align the two fields next to each other in one row:

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Column, Row

def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.helper = FormHelper()
        self.helper.form_tag = False

        self.helper.layout = Layout(
            Row(
                Column('make'),
                Column('model'),
            ),
        )

If I want to now make the model field only appear when a make is selected by adding include=lambda form: form['make'].value() to the model field, crispy forms fails because initially the model field is not part of the form ("Could not resolve form field 'model'.").

I don't think it's a bug but I am wondering if there is a way to support this. I suspect it would require additional logic to create the form layout on the go.

@j4mie
Copy link
Member

j4mie commented Jul 21, 2023

Sorry for the slow reply on this. I don't use django-crispy-forms myself so I don't know much about it, but from looking at the example I think you're right - you'd need to introspect which fields are present in your __init__ and change your layout accordingly.

@fordmustang5l
Copy link

Hi! I use both crispy and this package. I don’t (currently) change the layout, but what I have done is set the Model field to be hidden if the Make is blank. Not sure if that’s a suitable workaround

@nerdoc
Copy link

nerdoc commented Nov 26, 2023

You could change the DynamicFormMixin code to the following:

  ...
                else:
                    self.fields[name].widget = forms.HiddenInput()
                    #del self.fields[name]

Hiding a field is better, as it still includes it in the form, and CrispyForms or the validation won't complain.
Even better would be additionally removing the "required" attribute.

@nerdoc
Copy link

nerdoc commented Nov 26, 2023

You could add an attribute to DynamicFormMixin (like hide_unincluded_fields = False) to either hide the field (as Hiddeninput), or just disable them.

Even better would be to make the "including" configureable on a per-field basis: the "include" parameter should hide the field, and a new, "disabled" parameter could dynamically disable the field, depending on the lambda outcome.

This would mask the "disabled" parameter of the read field, and would make it dynamic!

nerdoc added a commit to nerdoc/django-forms-dynamic that referenced this issue Nov 26, 2023
@nerdoc nerdoc linked a pull request Nov 26, 2023 that will close this issue
@nerdoc
Copy link

nerdoc commented Nov 29, 2023

This is even more complicated, as hiding the field does a good job when just using django-forms-dynamic. But when you are using e.g. HTMX to dynamically change field querysets in the frontend together with crispy forms, it gets messy, as crispy forms adds a wrapper div which contains the label. And hiding just the e.g. input field does not hide the label.
Hiding there must be done by adding a display:none to the wrapper div's class. Which would be easier by "d-none" when using Bootstrap.
Any ideas?

@nerdoc
Copy link

nerdoc commented Dec 2, 2023

I managed to solve the problem by reloading the whole form using HTMX, and hx-includeing all parameters. This works almost perfect. I put the hx-include="[name=field1],[name=field2],..." at the form tag, as it is inherited. So I can reload the whole crispy form without modifications using a GET request. You just need to make sure that the GET parameters are placed into the fields again, I am using a simple mixin here:

class PrepopulateFormViewMixin:
    """Prepopulates the form with values from GET parameters."""
    def get_initial(self):
        initial = super().get_initial()
        initial.update(clean_dict(self.request.GET.dict()))
        return initial

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants