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

|crispy not compatible with Django's custom form.Select create_option() #166

Open
sethbam9 opened this issue May 1, 2024 · 0 comments
Open

Comments

@sethbam9
Copy link

sethbam9 commented May 1, 2024

The functionality

Django forms give the ability to override the create_option() function for custom Select fields.

You can use this to set custom attributes for each select option.

The function is triggered when the form is called in the template. So if we have {{ form.custom_select_field }} in the html file, create_option is called when the file is rendered. This works fine with the above syntax, but when using {{ form.field|as_crispy_field }} the create_option no longer executes. (Note: though I verified that the CustomSelect's __init__ method was still being called.

Source code

requirements.txt

python = "^3.9"
django = "^4.2.1"
django-widget-tweaks = "^1.4.12"
django-crispy-forms = "^2.0"

forms.py

class RegionSelect(forms.Select):
    _region_map = None

    @property
    def region_map(self):
        if self._region_map is None:
            self._region_map = {r.id: r for r in Region.objects.all()}
        return self._region_map

    def create_option(
        self, name, value, label, selected, index, subindex=None, attrs=None
    ):
        option = super().create_option(
            name, value, label, selected, index, subindex, attrs
        )
        region = self.region_map.get(value)
        if region:
            option["attrs"]["label"] = region.name
            option["attrs"]["data-country-id"] = region.country_id
        return option


class AddressForm(forms.ModelForm):
    street = forms.CharField(
        max_length=255,
        required=False
    )
    city = forms.CharField(max_length=100, required=False)
    state = forms.ModelChoiceField(
        queryset=Region.objects.all(),
        required=False,
        widget=RegionSelect()
    )
    country = forms.ModelChoiceField(
        queryset=Country.objects.all(),
        required=False
    )
    postal_code = forms.CharField(max_length=20, required=False)

    class Meta:
        model = Address
        fields = "__all__"

form.html

{% extends 'main/base.html' %}
{% load static %}
{% load widget_tweaks %}
{% load tailwind_filters %}
{% load crispy_forms_tags %}

{% block content %}

<form class="rounded-xl bg-white p-5 shadow-lg shadow-gray-500/40 sm:p-10 sm:px-20"
      method="POST">
    {% csrf_token %}
    {{ form.street|as_crispy_field }}
    <div id="hidden_address_fields" class="hidden">
        <div class="grid grid-cols-1 sm:grid-cols-2 sm:gap-4">
            <div>{{ form.country|as_crispy_field }}</div>
            <div>{{ form.state }}</div>
        </div>
        {{ form.city|as_crispy_field }}
        {{ form.postal_code|as_crispy_field }}
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

{% endblock content %}

Notes

All the form fields render fine with the styling, but the custom select widget only works for {{ form.state }} without the as_crispy_field.

I put a print statement in RegionSelect.create_option() and confirmed that it never even executes when we use crispy.

This behavior is also true with {{ form }} working as expected, but {{ form|crispy }} causing RegionSelect.create_option() to not execute.

@sethbam9 sethbam9 changed the title |as_crispy_field not compatible with Django's custom form.Select create_option() |crispy not compatible with Django's custom form.Select create_option() May 1, 2024
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

No branches or pull requests

1 participant