One-to-one relationship: linking a user model to a custom profile model in Django

The translation of the article was prepared on the eve of the start of the course "Web-developer in Python" .






Pff ... Databases again?







There are three main relationships in a relational database:



  • One-to-one relationship;
  • One-to-many relationship;
  • Many-to-many relationship.


In this article, we will deal with the first of these, the one-to-one relationship.



Usually Django already has a user model that comes with the framework. It comes with its own fields, methods, attributes, etc. The downside to this user model is that it doesn't allow you to add custom fields apart from the defaults already provided in Django. This can be a serious problem as a developer may need to completely configure a user / client profile from a group of authenticated users. For example, a blog site might need an author profile, which will include a user's photo, contact address, hobby, niche, etc. And the user model that comes with Django doesn't allow for this.



To solve this problem, the developers create a custom profile model and connect it to the default user model in Django using a one-to-one relationship. It so happens that the user is guaranteed to be connected to one profile and vice versa. In addition, this mechanic allows for better control over the customization of the profile model.



Now I will show you how you can do this in Django.



1. Use the default Django user model

In the application where you want to create the profile, create a new forms.py file . In forms.py, import the following modules:



from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User


Create a class to inherit from UserCreationForm. Inside this class, create another meta-class that will have two variables: modeland fields. The variable modelwill hold your user model, and the variable will hold the fieldsform fields that will be created.



class createUserForm(UserCreationForm):
    class meta:
        model = User
        fields = ['username', 'password1', 'password2']


The code above will create a form with fields for username, password, and password confirmation.



2. Create your custom user profile model.



In your models.py file , import the default user model.



from django.contrib.auth.models import User


Next, you need to create your own profile model, and also create a user field with a one-to-one relationship with the default user model in Django.



class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True,)
    name = models.CharField(max_length=200, null=True)
    email = models.CharField(max_length=200, null=True)
    address = models.CharField(max_length=200, null=True)

    def __str__(self):
        return self.name


3. Create a shape for your profile model.



Open the form.py file and import your profile model from models.py , also add a few other imports that will come in handy when creating your profile form.



from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import Profile


Then create a class that will inherit from forms.ModelForm. In this class, create another meta class containing two variables: modeland fields. The variable modelcontains the profile model, and fieldsthe form fields that will be created.



class profileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ['name', 'email', 'address']
#The labels attribute is optional. It is used to define the labels of the form fields created   
        labels = {
                "name": _("Name     "),
                "email": _("Email Address"),
                "address": _("Street Address"),
                }


Now that the forms are ready, we will define the logic in views.py before rendering it to our templates.



4. Add the logic to views.py .



To work with forms created in forms.py, you need to import them into our views.py , and add a few more modules that will come in handy for creating logic.



from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login,
from django.contrib import messages
from .models import Profile
from .forms import createUserForm, profileForm


Now let's create a registration page. Let's just call it registerPage . Let's create an empty context dictionary and return the render.



def registerPage(request):    
    context = {}
    return render(request, 'app_name/register.html', context)


Let's assign values ​​from the form to variables by calling the POST method. Then we will pass the variables to the context dictionary.



def registerPage(request):
    if request.method == 'POST':
        form = createUserForm(request.POST)
        profile_form = profileForm(request.POST)

    context = {'form': form, 'profile_form': profile_form}
    return render(request, 'app_name/register.html', context)


Then we will validate both forms and save them afterwards.



def registerPage(request):
    if request.method == 'POST':
        form = createUserForm(request.POST)
        profile_form = profileForm(request.POST)

        if form.is_valid() and profile_form.is_valid():
            user = form.save()

            #we don't save the profile_form here because we have to first get the value of profile_form, assign the user to the OneToOneField created in models before we now save the profile_form. 

            profile = profile_form.save(commit=False)
            profile.user = user

            profile.save()

    context = {'form': form, 'profile_form': profile_form}
    return render(request, 'app_name/register.html', context)


If the form values ​​are validated and saved, we will display a success message and redirect the user to the login page.



def registerPage(request):
    if request.method == 'POST':
        form = createUserForm(request.POST)
        profile_form = profileForm(request.POST)

        if form.is_valid() and profile_form.is_valid():
            user = form.save()

            #we don't save the profile_form here because we have to first get the value of profile_form, assign the user to the OneToOneField created in models before we now save the profile_form. 

            profile = profile_form.save(commit=False)
            profile.user = user

            profile.save()

            messages.success(request,  'Your account has been successfully created')

            return redirect('login')

    context = {'form': form, 'profile_form': profile_form}
    return render(request, 'app_name/register.html', context)


5. Render the template



In the file, register.htmlcreate a form tag using the POST method and actionwith an empty string value. In the form tag csrf_token, we will place it in the django template format, and then we will dynamically render the forms (user and profile forms). Also, let's not forget about the submit button.



<form method="POST" action="">
                {% csrf_token %}
                    <h3>Register Profile</h3>

                    <div class="form-field">
                        {{profile_form.name.label_tag}}
                        {{profile_form.name}}
                    </div>
                    <div class="form-field">
                        {{form.username.errors}}
                        {{form.username.label_tag}}
                        {{form.username}}
                    </div>
                    <div class="form-field">
                        {{profile_form.email.label_tag}}
                        {{profile_form.email}}
                    </div>
                    <div class="form-field">
                        {{profile_form.address.label_tag}}
                        {{profile_form.address}}
                    </div>
                    <div class="form-field">
                        {{form.password1.errors}}
                        {{form.password1.label_tag}}
                        {{form.password1}}
                    </div>
                    <div class="form-field">
                        {{form.password2.errors}}
                        {{form.password2.label_tag}}
                        {{form.password2}}
                    </div>

                <hr>
                <input id="form-button" class="btn btn-success btn-block" type="submit" value="Create Profile">
                <br>
                    {{form.non_field_errors}}
                <p>Already have an account? <a href="{% url 'login' %}">Login</a></p>
            </form>


Since after filling out the form, checking it and saving it, we redirect to the login page, a message about the success of the operation will be displayed on the login page at the bottom right before the words β€œDon't have an account? Register " .



 <form method="POST" action="">
                   ...
                 <hr>
                <input id="form-button" class="btn btn-success btn-block" type="submit" value="Login">
                
                {% for message in messages %}
                <p>{{message}}</p>
                {% endfor %}
                <p>Don't have an account? <a href="{% url 'store:register' %}">Register</a></p>
            </form>


This is how you can create a profile model for your site, linked to your user model in a one-to-one relationship.








All Articles