Filter permissions on a form.

I’m trying to filter the permissions on a form and display only the name.

I have come up with two possible solutions but they do not cover the requirements

The first shows the permissions correctly but shows a validation error saying that the selection is not valid.

class Permisos(ModelChoiceField):
    def label_from_instance(self, obj):
        return "{}".format(obj.name)
		

class UsuarioCreateForm(forms.ModelForm):
    user_permissions = Permisos(     queryset=Permission.objects.filter(content_type=ContentType.objects.get_for_model(RightsSupport)),
         to_field_name='pk', widget=forms.SelectMultiple(), label='Permisos de usuario', required=False,
         help_text="Permisos específicos para este usuario.", )

    class Meta:
        model = Usuario

The second one has no errors but shows application|model|permission. I just want the permission to show.

class UsuarioCreateForm(forms.ModelForm):
    class Meta:
        model = Usuario

        fields = [
            'user_permissions',
        ]
		
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['user_permissions'].queryset = Permission.objects.filter(content_type=ContentType.objects.get_for_model(RightsSupport))	

What could I do to achieve it?
Thanks

Your first option superficially looks correct. Can you show the rendered HTML created for that form field?

<select name="user_permissions" aria-describedby="id_user_permissions_helptext" id="id_user_permissions" multiple="">
<option value="">---------</option>
<option value="21">Crear usuarios</option>
<option value="23">Eliminar usuarios</option>
<option value="22">Modificar usuarios</option>
</select>

And if you select one of these named options, you get the validation error?

Yes, any option generates a validation error.

Please post the view that is using this form.

class ViewCreate(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView):
    template_name = 'crud/baseformcreate.html'
    success_message = REGISTRO_CREADO
    permission_required = ['usuarios.add_usuario']
    model = Usuario
    form_class = UsuarioCreateForm
    success_url = reverse_lazy('usuarios')	

    def __init__(self):
        super().__init__()
        enctype = ""
        self.extra_context = {'title': 'Agregar ' + self.model._meta.verbose_name,
                              'enctype': enctype,
                              'success_url': self.success_url}

In your template, are you rendering the entire form as {{form}}, or are you rendering the field individually?

Individually. The two options show identical html, except for the permission name and only the first option gives a validation error, the second works correctly except for the compound name part.

I just noticed that you don’t have the fields attribute in the Meta class in your first form. (Is it there and you just don’t show it? What else is in that form?)

Second option

<select name="user_permissions" aria-describedby="id_user_permissions_helptext" id="id_user_permissions" multiple="">
<option value="21" selected="">Core | rights support | Crear usuarios</option>
<option value="23">Core | rights support | Eliminar usuarios</option>
<option value="22">Core | rights support | Modificar usuarios</option>
</select>

It gives the same validation error by setting the fields attribute or not.

Please post the complete form that you are using in the first version.

class Permisos(ModelChoiceField):
    def label_from_instance(self, obj):
        return "{}".format(obj.name)


class UsuarioCreateForm(forms.ModelForm):
    password1 = forms.CharField(
        label="Contraseña",
        strip=False,
        widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
        help_text=password_validation.password_validators_help_text_html(),
    )
    password2 = forms.CharField(
        label="Repita la contraseña",
        widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
        strip=False,
        help_text="Entre la misma contraseña anterior para su verificación.",
    )
    user_permissions = Permisos(
        queryset=Permission.objects.filter(content_type=ContentType.objects.get_for_model(RightsSupport)),
        to_field_name='pk', widget=forms.SelectMultiple(), label='Permisos de usuario', required=False,
        help_text="Permisos específicos para este usuario.", )

    field_order = ['usuario',
                   'nombre',
                   'password1',
                   'password2',
                   'is_active',
                   'email',
                   'telefono',
                   '__all__']

    class Meta:
        model = Usuario

        fields = [
            'usuario',
            'email',
            'nombre',
            'is_active',
            'telefono',
            'groups',
            #'user_permissions',
        ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['nombre'].required = True
        # self.fields['user_permissions'].queryset = Permission.objects.filter(content_type=ContentType.objects.get_for_model(RightsSupport))

    def clean(self):
        cleaned_data = super().clean()
        if 'email' in cleaned_data and cleaned_data['email'] is not None:
            if Usuario.objects.filter(email=cleaned_data['email'].lower()).exists():
                self.add_error('email', 'Este correo ya existe.')
        if 'usuario' in cleaned_data:
            if Usuario.objects.filter(usuario=cleaned_data['usuario'].lower()).exists():
                self.add_error('usuario', 'Este usuario ya existe.')
        if ('password1' in cleaned_data) and ('password2' in cleaned_data):
            if cleaned_data['password1'] != cleaned_data['password2']:
                self.add_error('password2', 'Las contraseñas no coinciden.')

    def clean_password1(self):
        password1 = self.cleaned_data.get('password1')
        try:
            password_validation.validate_password(password1, self.instance)
        except forms.ValidationError as error:

            # Method inherited from BaseForm
            self.add_error('password1', error)
        return password1

    @transaction.atomic
    def save(self):
        cleaned_data = self.cleaned_data
        p = cleaned_data.copy()
        clave = cleaned_data['password1']
        grupos = cleaned_data['groups']
        user_permissions = cleaned_data['user_permissions']
        del p['password1']
        del p['password2']
        del p['groups']
        del p['user_permissions']
        u = Usuario(**p)
        u.set_password(clave)
        u.date_joined = timezone.now()
        u.save()
        u.groups.set(grupos)
        u.user_permissions.set(user_permissions)
        u.save()
        return super()

I found the error after running step by step and following the validation path.
I was using the wrong inheritance, ModelChoiceField instead of ModelMultipleChoiceField.

class Permisos(ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        return "{}".format(obj.name)
1 Like