Easily create User Profiles with Django AllAuth

When I first started learning the Python framework Django, I spent way too much time researching how to set up customised User model which had additional information.

When using django-allauth as an authentication package I struggled when trying to extend the base User model to encapsulate different fields.

Although you can extend the AbstractUser or BaseAbstractUser, update the AUTH_USER_MODEL in the settings.py, and create a customised user sign-up form by extending the UserCreationForm that includes the added fields, I found this process to be quite cumbersome.

You can read more on this solution here.

An easier solution I discovered was to simply attach a Profile model to the User Model and create a signal function to automatically link the two in a one-to-one relationship as soon as a User Model is created.

It's simple and effective, and you don't risk running into any issues by altering the User model. Let me walk you through it.

1. Create the Profile Model

Firstly, we need to create the Profile Model. A simple profile would include age, gender, and date of birth.

class Profile(models.Model):
    """
    Model to represent extended auth User Class to add additional
    profile information.
    """

    GENDER_CHOICES = [
        ('M', 'Male'),
        ('F', 'Female'),
        ('O', 'Other'),
    ]

    user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
    age = models.PositiveIntegerField(null=True, blank=True)
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True, blank=True)
    date_of_birth = models.DateField(null=True, blank=True)

    def __str__(self):
        return f"Profile for {self.user.username}"

2. Create a signal function

Once we have the user model created we need to create a signal handler function that automatically syncs the profile to a user whenever a new user model is created.

def create_user_profile(instance, created, *args, **kwargs):
    """
    Signal handler function to create a user profile when a
    new user is created.

    This function is connected to the User model's post_save signal.
    kwargs are required for dispatch signals

    """
    if created:
        Profile.objects.create(user=instance)

3. Establish the connection

Finally we must establish a connection between a signal and the signal handler function.

models.signals.post_save.connect(create_user_profile, sender=User)

Essentially this ensures that whenever a new user is created and saved (e.g., during user registration), the create_user_profile function will be invoked automatically.

Admin

If you want to view the established connection in the admin portal, simply register the model. I prefer registering the model as an inline to the User to emphasise the one-to-one connection.

  1. Create the UserAdmin Modal
  2. Unregister the User
  3. Reregister the User as the newly created UserAdmin
class UserAdmin(admin.ModelAdmin):
    """
    Admin model configuration for user accounts.

    This class defines the admin panel configuration for user accounts,
    allowing administrators to manage user information such as username,
    first name, last name, and email. It also includes an inline
    representation of user profiles using the `ProfileInline` class

    Example:
        To use this admin configuration for user accounts:

        admin.site.register(User, UserAdmin)

    """

    model = User
    fields = ("username", "first_name", "last_name", "email", 'password')
    inlines = [ProfileInline, AssignedSongInline]

I hope this information helps any new Django developers in creating more complex user models and demystifies the some basic concepts of signals and post_save functionality.

It certainly had me stumped for a while.

Avatar for DarrachBarneveld

Written by DarrachBarneveld

Addicted to building

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.