How to loop over form field choices and display associated model instance fields

前端 未结 8 828
广开言路
广开言路 2021-02-02 14:44

I have a ModelForm with a multiple choice field. The choices are populated instances of Hikers belonging to a specific Club.

I want to customize the way my form displays

8条回答
  •  挽巷
    挽巷 (楼主)
    2021-02-02 15:13

    This is surprisingly tricky, but you can do it using ModelMultipleChoiceField, CheckboxSelectMultiple, and a custom template filter. The form and widget classes get most of the way there, but the template filter works out which widget to give you for each instance in the queryset. See below...

    Generic solution

    # forms.py
    from django import forms
    from .models import MyModel
    
    class MyForm(forms.Form):
        my_models = forms.ModelMultipleChoiceField(
                                          widget=forms.CheckboxSelectMultiple,
                                          queryset=None) 
    
        def __init__(self, *args, **kwargs):
            super(MyForm, self).__init__(*args, **kwargs)
            self.fields['my_models'].queryset = MyModel.objects.all()
    
    
    # myapp/templatetags/myapp.py
    from django import template
    from copy import copy
    
    register = template.Library()
    
    @register.filter
    def instances_and_widgets(bound_field):
        """Returns a list of two-tuples of instances and widgets, designed to
        be used with ModelMultipleChoiceField and CheckboxSelectMultiple widgets.
    
        Allows templates to loop over a multiple checkbox field and display the
        related model instance, such as for a table with checkboxes.
    
        Usage:
           {% for instance, widget in form.my_field_name|instances_and_widgets %}
               

    {{ instance }}: {{ widget }}

    {% endfor %} """ instance_widgets = [] index = 0 for instance in bound_field.field.queryset.all(): widget = copy(bound_field[index]) # Hide the choice label so it just renders as a checkbox widget.choice_label = '' instance_widgets.append((instance, widget)) index += 1 return instance_widgets # template.html {% load myapp %}
    {% csrf_token %} {% for instance, widget in form.job_applications|instances_and_widgets %} {% endfor %}
    {{ instance.pk }}, {{ instance }} {{ widget }}

    Specific to you

    It should work if you adjust the form like so:

    class ClubForm(forms.ModelForm):
    
        def __init__(self, *args, **kwargs):
            cluk_pk = kwargs.pop('club_pk')
            super(ClubForm, self).__init__(*args, **kwargs)
            self.fields['hikers'].queryset = Club.objects.filter(pk=club_pk)
    
        class Meta:
            model = Club
            fields = ('hikers',)
            widgets = {'hikers': forms.CheckboxSelectMultiple}
    

提交回复
热议问题