htmldjangodjango-crispy-forms

Change crispy forms string representation


Crispy forms use the string representation of objects provided by the str method of the objects' class: I need to change this behaviour (please, help me).

In my crispy form the labels of the choices in a CheckboxSelectMultiple() field populate from the default str method of the objects represented. The set of objects is defined in a list containing ids, with those ids crispy calls the str methods.

Is it possible to write a custom string representation (for instance as a class' @property) and tell crispy to use that instead? If yes, which point in the pipeline would give the best programmer's practice (models/views/forms/template) ?

this image is just a dummy example for better illustrating the problem Overriding the default str method provides the desired labelling (as suggested in this post) but is totally unacceptable because of side effects.

models.py

class School(models.Model):

    nice_name_for_forms = models.CharField(max_length=100)  
    
    @property
    def label_from_instance(self):
        return '%s' % (self.nice_name_for_forms)

views.py

school_list = School.objects.all().values_list('id', flat=True)
form = MyForm(request.POST, school_list=school_list)

forms.py

class MyForm(forms.ModelForm):
    
    class Meta:
        model = MyForm
        fields = '__all__'      
        labels = {'schools' : 'My Schools'}
                
        widgets = {'schools' : forms.CheckboxSelectMultiple()}

    def __init__(self, *args, **kwargs):
        self.school_list = kwargs.pop('school_list')        
        super().__init__(*args, **kwargs)
        self.fields['schools'].queryset = self.fields['schools'].queryset.filter(id__in=self.school_list).distinct()

        self.helper = FormHelper()
        self.helper.use_custom_control = False
        self.helper.layout = Layout(
            Row(CheckboxAllFieldCompact('schools', wrapper_class='col-4 col-md-2'))

checkbox_all_field.html

<!-- crispy/checkbox_all_field.html -->
{% load crispy_forms_field %}
{% load i18n %}
<div id="div_checkbox_all_{{ field.html_name }}" class="no-form-control control-group {{ wrapper_class }}">
    <div class="controls" style="max-height:250px;overflow:auto">
        <label for="{{ field.name_for_label }}" class="label_title inline">{{ field.label }}</label>
        <br />
        <label class="block">
            <button id="check_all_{{ field.html_name }}" type="button" class="btn btn-default btn-sm" actif="false">{% translate 'Select all' %}</button>
        </label>
        {% crispy_field field %}
    </div>
</div>

Solution

  • In my opinion, the cleanest way for changing the string representation of objects used in crispy forms' fields involves writing a @staticmethod label_for_schools in forms.py within the relevant class:

    #forms.py
    
    class MyForm(forms.ModelForm):
    
        class Meta:
    
            model = MyForm
            fields = '__all__'      
            labels = {'schools' : 'My Schools'}
                    
            widgets = {'schools' : forms.CheckboxSelectMultiple()}
    
        @staticmethod
        def label_for_schools(self):
            if self.nice_name_for_forms:
                return '%s' % (self.nice_name_for_forms)
    
        def __init__(self, *args, **kwargs):
            self.school_list = kwargs.pop('school_list')        
            super().__init__(*args, **kwargs)
            self.fields['schools'].queryset = self.fields['schools'].queryset.filter(id__in=self.school_list).distinct()
    
            self.helper = FormHelper()
            self.helper.use_custom_control = False
            self.helper.layout = Layout(
                Row(CheckboxAllFieldCompact('schools', wrapper_class='col-4 col-md-2'))