0

Context

I override AbstractBaseUser in order to provide it with extra fields I needed. I can create a User no problem, and I can use my User objects to connect to my app. My issue starts when I try to modify an existing user from the admin view.

In my admin view for the User model, I show an inline for a Permission model related to my users and I use an override of ModelForm specific to my Model. Here is its code :

from django.contrib.auth import get_user_model

class CustomUserAdminForm(forms.ModelForm):
    """
    Comment
    """
    class Meta:
        model = User
        fields = [
            "email",
            "password",
            "is_admin"
        ]

    def save(self, commit=True):
        """
        Save override.
        """
        # Raise in case of errors.
        if self.errors:
            raise ValueError(
                "The %s could not be %s because the data didn't validate." % (
                    self.instance._meta.object_name,
                    "created" if self.instance._state.adding else "changed",
                )
            )

        # Get custom User model.
        my_user_model = get_user_model()

        # Find user if the object exists.
        try:
            self.instance = my_user_model.objects.get(email=self.cleaned_data["email"])
        except my_user_model.DoesNotExist:
            self.instance = my_user_model()

        self.instance.email = self.cleaned_data["email"]
        self.instance.is_admin = self.cleaned_data["is_admin"]

        # Saving the hash of the password.
        if not self.cleaned_data["password"].startswith("pbkdf2_sha256$"):
            self.instance.set_password(self.cleaned_data["password"])

        if commit:
            # Copied from BaseModelForm.
            # https://github.com/django/django/blob/master/django/forms/models.py
            # If committing, save the instance and the m2m data immediately.
            self.instance.save()
            self._save_m2m()

        else :
            # If not committing, add a method to the form to allow deferred
            # saving of m2m data.
            self.save_m2m = self._save_m2m

        return self.instance

What happens

When I click on "save" from the admin page (after modifying the email), I get redirected to the change page with the error message "Correct the error" but no field is highlighted. Furthermore, my many_to_many field gets cleared.

What I tried

I added some print in order to know whether or not the form save method was run. It appears it does run and complete, with commit == False, so this isn't where things go wrong.

I tried simplifying things by getting rid of the commit field, but it created another User rather than modifying the existing one. With the code I posted above, the redirection doesn't create a new object, but it loses the many_to_many relations.

I tried adding permission_set to the Meta class of my Form, but it raised a FieldError.

Question

Where could the error come from ?

PleaseHelp
  • 133
  • 9

1 Answers1

0

Reading this answer I changed my form so it inherited from django.contrib.auth.forms.UserChangeForm rather than from django.forms.ModelForm.

Also, I made a mistake when trying to get the existing object.

I used

try:
    self.instance = my_user_model.objects.get(email=self.cleaned_data["email"])
except my_user_model.DoesNotExist:
        self.instance = my_user_model()

rather than

try:
    self.instance = my_user_model.objects.get(email=self.initial["email"])
except my_user_model.DoesNotExist:
    self.instance = my_user_model()

which explains why it created a new object each time.

These two modifications fixed my problem.

I don't know if this can help anyone, kindly notify me if you think I should delete this.

PleaseHelp
  • 133
  • 9