Search code examples
pythondjangodjango-formsoptgroup

Python/Django MultipleChoiceField with OptGroup Options


I am having a little bit of trouble figuring out how to configure optgroup tags in my Django Form using MutlipleChoiceField select options.

Basically I have created an interface that uses Ajax to allow the allowance or removal of permissions for my project and I use a form to prepopulate the granted and not granted permissions in 2 MutlipleChoiceField select boxes.

There are currently about 190 permissions for the project so the select box looks quite overwhelming at the moment, what I want is to group these options using an optgroup html tag. I understand how it works if I am statically typing the choices for the form, but at the moment with my current code, I cannot see a way to easily group them by app_label to add the correct optgroup. Can somebody help me out?

Here is my code:

from django import forms
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q


class GroupForm(forms.ModelForm):
    class Meta:
        model = Group
        fields = ['permissions']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if 'instance' in kwargs:
            instance = kwargs['instance']

            granted_permissions = list()
            not_granted_permissions = list()
            all_permissions = Permission.objects.all().order_by('id', 'content_type__app_label')
            for permission in all_permissions:
                if permission in instance.permissions.all().order_by('id', 'content_type__app_label'):
                    # I NEED OPTGROUP HERE FOR EACH ITEMS content_type__app_label
                    granted_permissions.append([permission.id, permission.name])
                else:
                      # I NEED OPTGROUP HERE FOR EACH ITEMS content_type__app_label
                    not_granted_permissions.append([permission.id, permission.name])

            self.fields['permissions'] = forms.MultipleChoiceField(
                label='Granted Permissions',
                required=False,
                choices=granted_permissions)

            self.fields['not_granted_permissions'] = forms.MultipleChoiceField(
                label='Not Granted Permissions',
                required=False,
                choices=not_granted_permissions)

Solution

  • I ended up completing the issue this way, now everything works as expected and the optgroups are valid:.

    from django import forms
    from django.contrib.auth.models import Group, Permission
    from django.contrib.contenttypes.models import ContentType
    from django.db.models import Q
    
    
    class GroupForm(forms.ModelForm):
        class Meta:
            model = Group
            fields = ['permissions']
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            if 'instance' in kwargs:
                instance = kwargs['instance']
    
                all_permissions = Permission.objects.exclude(
                    Q(content_type=ContentType.objects.get(app_label='contenttypes', model='contenttype')) |
                    Q(content_type=ContentType.objects.get(app_label='sessions', model='session')) |
                    Q(codename='add_group') |
                    Q(codename='delete_group') |
                    Q(codename='add_permission') |
                    Q(codename='delete_permission') |
                    Q(codename='change_permission')
                ).order_by('id', 'content_type__app_label')
    
                granted_builder = dict()
                not_granted_builder = dict()
    
                for permission in all_permissions:
                    if permission in instance.permissions.all().order_by('id', 'content_type__app_label'):
                        if not granted_builder.get(permission.content_type.app_label):
                            granted_builder[permission.content_type.app_label] = [[permission.id, permission.name]]
                        else:
                            # we have a list, so append the permission to it
                            granted_builder[permission.content_type.app_label].append([permission.id, permission.name])
                    else:
                        if not not_granted_builder.get(permission.content_type.app_label):
                            not_granted_builder[permission.content_type.app_label] = [[permission.id, permission.name]]
                        else:
                            # we have a list, so append the permission to it
                            not_granted_builder[permission.content_type.app_label].append([permission.id, permission.name])
    
                # loop through granted permissions
                final_granted_permissions = list()
                for app_label, list_of_options in granted_builder.items():
                    # [optgroup, [options]]
                    final_granted_permissions.append([app_label.title(), list_of_options])
    
                # loop through not granted permissions
                final_not_granted_permissions = list()
                for app_label, list_of_options in not_granted_builder.items():
                    # [optgroup, [options]]
                    final_not_granted_permissions.append([app_label.title(), list_of_options])
    
                self.fields['group_name'] = forms.CharField(
                    required=False,
                    widget=forms.HiddenInput(attrs={'value': instance.name}))
    
                self.fields['permissions'] = forms.MultipleChoiceField(
                    label='Granted Permissions',
                    required=False,
                    choices=final_granted_permissions)
    
                self.fields['not_granted_permissions'] = forms.MultipleChoiceField(
                    label='Not Granted Permissions',
                    required=False,
                    choices=final_not_granted_permissions)