Reputation: 75
I am struggling to figure out how to save User Profile for a new user created within Django Admin.
I have a custom User model and a simple user Profile with OneToOneField:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(_('staff'), default=False)
is_active = models.BooleanField(_('active'), default=True)
...
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(_('first name'), max_length=80, blank=False)
last_name = models.CharField(_('last name'), max_length=80, blank=False)
...
I have the following user creation form:
class UserAdminCreationForm(forms.ModelForm):
password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
password2 = forms.CharField(
label="Password confirmation", widget=forms.PasswordInput
)
first_name = forms.CharField(label="First name")
last_name = forms.CharField(label="Last name")
class Meta:
model = User
fields = ("email",)
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
My admin Add user form is rendered correctly and includes fields from both User and Profile models. After saving the form, a new user and a new profile are created in the database. However, the first_name and last_name fields in profile table are empty.
What I need to do to ensure that profile is saved together with the new user?
UPDATE
Overwriting the save method and ignoring the commit parameter worked for me:
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.save()
profile, created = Profile.objects.update_or_create(user=user)
profile.first_name = self.cleaned_data["first_name"]
profile.last_name = self.cleaned_data["last_name"]
profile.save()
return user
Upvotes: 2
Views: 1560
Reputation: 6127
Rather than making a custom form you can edit both models in one admin change form by using an inline.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .models import Profile
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'Profile'
fk_name = 'user'
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, )
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super().get_inline_instances(request, obj)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
Since you're already defining your own custom user model, I would recommend doing away with the profile model entirely. It's just going to cause excess queries retrieving profile fields from the user instances.
UDPATE if you want to continue using your form:
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
Profile.objects.update_or_create(
user=user,
defaults={
'first_name': self.cleaned_data['first_name'],
'last_name': self.cleaned_data['last_name'],
}
)
profile.first_name = self.cleaned_data["first_name"]
profile.save()
return user
Upvotes: 2