creating profile instance during user creation

Hi. I a have created a CustomUser model, user Profile model and Address model where profile is a 1:1 with CustomUser and Profile and ManytoOne with Profile and Address.

In order to view a profile on a user (or an address on a profile) I assume I need to instantiate the profile (and address) during creation of the user. Below is what I have done to do this for the profile (and would presume to do the same on the address). My question is there a better way to do this, as I could see that perhaps as the models getting more involved and relational this may be tricky? Thanks!

models.py

class CustomUser(AbstractUser):
    # custom user account with username as primary identifier   
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=150, unique=True)
    password = models.CharField(max_length=30)
  
    objects = CustomUserManager()
    
    REQUIRED_FIELDS = ['email']
    USERNAME_FIELD = 'username'
    
    class Meta:
        verbose_name = 'user'
        verbose_name_plural = 'users'
       

class Profile(models.Model):
    user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
    bio = models.TextField()
    profile_pic = models.ImageField(upload_to='profile_pics', default='default.jpg')
    first_name = models.CharField(max_length=50)
    middle_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    date_of_birth = models.DateField(null=True, blank=True)
    avatar = models.ImageField(upload_to='avatars', default='default.jpg')
    primary_language = models.CharField(max_length=50)
    secondary_language = models.CharField(max_length=50)
    primary_phone_number = models.CharField(max_length=15)
    pager_number = models.CharField(max_length=15)
    work_department = models.ForeignKey('Department', on_delete=models.CASCADE)
    is_provider = models.BooleanField(default=False)
    is_patient = models.BooleanField(default=False)
    address = models.ForeignKey('Address', on_delete=models.CASCADE)
    social_security_number = models.CharField(max_length=15)
    tax_identification_number = models.CharField(max_length=15)
    
    def __str__(self):
        return self.user.username
    
class Address(models.Model):
    
    street_number = models.CharField(max_length=10)
    street_name= models.CharField(max_length=100)
    apt_number = models.CharField(max_length=10)    
    city = models.CharField(max_length=50)
    state = models.CharField(max_length=50)
    country = models.CharField(max_length=50)
    postal_code = models.CharField(max_length=10)

views.py

def register_view(request):
    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
       
        if form.is_valid():
            password1 = form.cleaned_data['password1']
            password2 = form.cleaned_data['password2']
            if password1 == password2:
                user = form.save(commit=False)
                user.set_password(password1)
                form.save()
                Profile.objects.create(user=user)
                messages.success(request, "User created successfully. Login to get started")
                return render(request, reverse('login'), {'form': form})
            else:
                messages.error(request, "Passwords do not match")
                return render(request, 'registration/register.html', {'form': form})
    else:
        form = CustomUserCreationForm()
    context = {'form': form}
    return render(request, 'registration/register.html', context)


def update_profile(request):
    if request.method == 'POST':
        user_form = CustomUserCreationForm(request.POST, instance=request.user)
        profile_form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            messages.success(request, "Profile updated successfully")
            return redirect(reverse('profile'))
        else:
            messages.error(request, "Profile update failed. Please try again")
            return render(request, 'registration/profile.html', {'user_form': user_form, 'profile_form': profile_form})
    user_form = CustomUserCreationForm(instance=request.user)
    profile_form = ProfileForm(instance=request.user.profile)
    return render(request, 'registration/profile.html', {'user_form': user_form, 'profile_form': profile_form})

There’s nothing wrong with what you have here.

There is also an alternative approach - in every location where you would be using that profile, you could replace the get() query with a get_or_create() call. (This function tries a get first, and catches the exception if it doesn’t exist.)

In my mind, the choice between the two depends upon whether the profile is truly required, and whether or not the base object can be related to more than one profile.

perfect … follow up question:

how do i create an Address instance at the same time based on the fact that it is based on Profile which is based on User? I tried this change to my udpateprofile view and it seemed to work but want to know if there is a better more correct way to be doing this …

views.py

def update_profile(request):
    if request.method == 'POST':
        user_form = CustomUserCreationForm(request.POST, instance=request.user)
        profile_form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
        address_form = AddressForm(request.POST, instance=request.user.profile.address)

        if user_form.is_valid() and profile_form.is_valid() and address_form.is_valid():
            user_form.save()
            prof = profile_form.save(commit=False)
            add = address_form.save()
            prof.address_id = add.id 
            prof.save()                                 
            messages.success(request, "Profile updated successfully")
            return redirect(reverse('profile'))
        else:
            messages.error(request, "Profile update failed. Please try again")
            return render(request, 'registration/profile.html', {'user_form': user_form, 'profile_form': profile_form})

    user_form = CustomUserCreationForm(instance=request.user)
    profile_form = ProfileForm(instance=request.user.profile)
    address_form = AddressForm(instance=request.user.profile.address)
    context = {'user_form': user_form, 
               'profile_form': profile_form, 
               'address_form' : address_form}
    return render(request, 'registration/profile.html', context=context)