Creating a User Profile model in Django to hold additional user info
Tags: django, python
Published: 2023-03-04 13:39:49

---
title: Creating a User Profile model in Django to hold additional user info
published: true
date: 2023-03-04 13:37:22 UTC
tags: Django,Python
canonical_url: https://www.rockandnull.com/django-user-profile/
---

 ![Creating a User Profile model in Django to hold additional user info](https://images.unsplash.com/photo-1511367461989-f85a21fda167?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMTc3M3wwfDF8c2VhcmNofDF8fHByb2ZpbGV8ZW58MHx8fHwxNjc3OTM2OTk2&ixlib=rb-4.0.3&q=80&w=2000)

Creating a new user in a Django project is quite straightforward and well-documented.

But most of the time, you want to capture more data than the basic user model provided by Django. You see, the default Django user model only contains fields for the first name, last name, and email. The typical registration flow usually requires more data to be captured. In this case, the recommended way is to create a user profile model, and "link" it to the user using a one-to-one relationship.

In this post, we will show a complete example of declaring a user profile model, the registration form that contains both the user fields and the user profile fields, and finally the view that will handle the creation of the user and user profile instances in the database.

## Model 

```
class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                on_delete=models.CASCADE)
    id_number = models.CharField(max_length=20, unique=True)
    occupation = models.CharField(max_length=50, blank=True)
    address = models.CharField(max_length=100)
    street_number = models.CharField(max_length=10)
    flat_number = models.CharField(max_length=10, blank=True)
    zip_code = models.CharField(max_length=10)
    city = models.CharField(max_length=50)
```

_models.py_

The only highlight in this model declaration is the use of `OneToOneField` relationships. This means that instances of this `UserProfile` model can point to one and only one instance of the `User` model.

Also, note that we are not referencing directly the `User` model, but we are using `settings.AUTH_USER_MODEL` as [recommended in the documentation](https://docs.djangoproject.com/en/dev/topics/auth/customizing/#referencing-the-user-model) to support custom-defined models. Finally, we are using `on_delete=models.CASCADE` to ensure that when a `User` is deleted, the "linked" `UserProfile` will be deleted as well.

## Form

```
class RegistrationForm(forms.Form):
    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)
    email = forms.EmailField(
        validators=[validate_email], 
        widget=forms.EmailInput())
    password = forms.CharField(
        max_length=200, 
        widget=forms.PasswordInput(), 
        validators=[validate_password]
    )
    
    id_number = forms.CharField(max_length=20)
    occupation = forms.CharField(max_length=50, required=False)
    address = forms.CharField(max_length=100)
    street_number = forms.CharField(max_length=10)
    flat_number = forms.CharField(max_length=10, required=False)
    zip_code = forms.CharField(max_length=10)
    city = forms.CharField(max_length=10)
```

_forms.py_

This is the form used to present and validate the registration page. Notice that the first 4 fields refer to the `User` model, and the rest of them in the `UserProfile` model.

## View

```
class RegistrationView(FormView):
    template_name = "registration.html"
    form_class = RegistrationForm
    success_url = reverse_lazy("register_successful")

    @transaction.atomic
    def form_valid(self, form):
        user = CustomUser.objects.create_user(
            email=form.cleaned_data["email"],
            password=form.cleaned_data["password"],
            first_name=form.cleaned_data["first_name"],
            last_name=form.cleaned_data["last_name"],
        )
        CustomUserProfile.objects.create(
            user=user,
            id_number=form.cleaned_data["id_number"],
            occupation=form.cleaned_data["occupation"],
            address=form.cleaned_data["address"],
            street_number=form.cleaned_data["street_number"],
            flat_number=form.cleaned_data["flat_number"],
            zip_code=form.cleaned_data["zip_code"],
            city=form.cleaned_data["city"]).save()
```

_views.py_

This is a Class-Based View (CBV) that is specifically for rendering and saving models based on a provided form (notice that it's extending `FormView` class).

The main work of this view takes place in the `form_valid()` method. This is called when the elements in the form are validated and this is called for you to implement the business logic of constructing and saving the models.

Since you will be saving 2 instances, one `User` and one `UserProfile`, notice that we are using the `@transaction.atomic` annotation to ensure that if for some reason one of the model creation fails, the database will roll back the changes. (read this for more details on [Django and atomic transactions](https://dev.to/rockandnull/atomic-transactions-in-django-19m5)).

The rest of the code is just for first creating a `User` using the `create_user()` method and then a `UserProfile` that references the newly created `User`.

Hopefully, this was a quick and to-the-point tutorial on how to create a `User` with a `UserProfile` to hold additional data about your users.

Happy coding!