This post cross-published with OnePublish
Hello DEV Network!
In this tutorial we will cover advanced user registration view with Django.
First let’s build basic sign up view then we are going to add some extra fields and confirmation mail function to make it advanced.
I am assuming that you already created your Django project.
Basic Registration
Django authentication framework provides a form named UserCreationForm (which inherits from ModelForm class) to handle the creation of new users. It has three fields namely username, password1 and password2 (for password confirmation).
In your views.py:
from django.shortcuts import render
from django.contrib.auth import login, authenticate
from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import render, redirect
def home_view(request):
return render(request, 'home.html')
def signup_view(request):
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password)
login(request, user)
return redirect('home')
return render(request, 'signup.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode
cleaned_data is holding the validated form data and authenticate() method takes credentials as keyword arguments, username and password for the default case, checks them against each authentication backend, and returns a User object if the credentials are valid for a backend.
Once user is verified, login() method takes an HttpRequest object and a User object and saves the user’s ID in the session, using Django’s session framework. Finally, redirect() method is basically redirecting the logged in user to home URL.
urls.py
from django.contrib import admin
from django.urls import path
from accounts.views import home_view, signup_view
urlpatterns = [
path('admin/', admin.site.urls),
path('', home_view, name="home"),
path('signup/', signup_view, name="signup")
]
Enter fullscreen mode Exit fullscreen mode
Last step for basic registration, in your signup.html template:
<h2>Sign up Form</h2>
<form method="post">
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% if field.help_text %}
<small style="color: grey">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}
<button type="submit">Sign up</button>
</form>
Enter fullscreen mode Exit fullscreen mode
We are displaying each field in our form with help texts to avoid errors. If any errors occurs while registration it is nice to display user what causes these errors.
Registration with extra fields
What if we want register user with email or username? So, create forms.py and extend the UserCreationForm.
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class SignUpForm(UserCreationForm):
username = forms.CharField(max_length=30)
email = forms.EmailField(max_length=200)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2', )
Enter fullscreen mode Exit fullscreen mode
Then , just change the UserCreationForm to SignUpForm in views.py:
from django.shortcuts import render
from django.contrib.auth import login, authenticate
from .forms import SignUpForm
from django.shortcuts import render, redirect
def home_view(request):
return render(request, 'home.html')
def signup_view(request):
form = SignUpForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password)
login(request, user)
return redirect('home')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode
That’s it! Now, user can register by his/her username and email as well.
Registration with Profile Model
In my view, this is the best and recommended way to implement registration system in your Django application. We are going to use Django Signals to create a user profile right after user registered. So, take a look models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
email = models.EmailField(max_length=150)
bio = models.TextField()
def __str__(self):
return self.user.username
@receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
Enter fullscreen mode Exit fullscreen mode
With the @receiver decorator, we can link a signal with a function. So, every time that a User model instance ends to run its save() method (or when user register ends), the update_profile_signal will start to work right after user saved.
- sender – The model class.
- instance – The actual instance being saved.
- created – A boolean; True if a new record was created.
Well, we want from user to enter his/her information into extra fields above to complete registration. Let’s update forms.py :
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=100, help_text='Last Name')
last_name = forms.CharField(max_length=100, help_text='Last Name')
email = forms.EmailField(max_length=150, help_text='Email')
class Meta:
model = User
fields = ('username', 'first_name', 'last_name',
'email', 'password1', 'password2',)
Enter fullscreen mode Exit fullscreen mode
and in views.py:
from django.shortcuts import render
from django.contrib.auth import login, authenticate
from .forms import SignUpForm
from django.shortcuts import render, redirect
def home_view(request):
return render(request, 'home.html')
def signup_view(request):
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.profile.first_name = form.cleaned_data.get('first_name')
user.profile.last_name = form.cleaned_data.get('last_name')
user.profile.email = form.cleaned_data.get('email')
user.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password)
login(request, user)
return redirect('home')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode
We are using refresh_from_db() method to handle synchronism issue, basically reloading the database after the signal, so by this method our profile instance will load. Once profile instance loaded, set the cleaned data to the fields and save the user model.
Registration with Confirmation Mail
At this stage, we are going to configure email backend to send confirmation links. Let’s test it on console for this tutorial.
Add the following line to your settings.py:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Enter fullscreen mode Exit fullscreen mode
Then, update models.py by adding confirmation check field.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
email = models.EmailField(max_length=150)
signup_confirmation = models.BooleanField(default=False)
def __str__(self):
return self.user.username
@receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
Enter fullscreen mode Exit fullscreen mode
Now, let’s create a new module named tokens.py
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils import six
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (
six.text_type(user.pk) + six.text_type(timestamp) +
six.text_type(user.profile.signup_confirmation)
)
account_activation_token = AccountActivationTokenGenerator()
Enter fullscreen mode Exit fullscreen mode
PasswordResetTokenGenerator is generating a token without persisting it in the database so, we extended it to create a unique token generator to confirm registration or email address. This make use of your project’s SECRET_KEY, so it is a secure and reliable method.
Once user clicked the link, it will no longer be valid.The default value for the PASSWORD_RESET_TIMEOUT_DAYS is 7 days but you can change its value in the settings.py .
Here is the forms.py:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=100, help_text='Last Name')
last_name = forms.CharField(max_length=100, help_text='Last Name')
email = forms.EmailField(max_length=150, help_text='Email')
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2', )
Enter fullscreen mode Exit fullscreen mode
and in views.py we will no longer authenticate the user, instead we will send activation link.
views.py
def signup_view(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.profile.first_name = form.cleaned_data.get('first_name')
user.profile.last_name = form.cleaned_data.get('last_name')
user.profile.email = form.cleaned_data.get('email')
# user can't login until link confirmed user.is_active = False
user.save()
current_site = get_current_site(request)
subject = 'Please Activate Your Account'
# load a template like get_template() # and calls its render() method immediately. message = render_to_string('activation_request.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
# method will generate a hash value with user related data 'token': account_activation_token.make_token(user),
})
user.email_user(subject, message)
return redirect('activation_sent')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode
So basically we are generating activation url with user related data and sending it. Note that, we are setting user.is_active = False that means user can’t login until he/she confirmed the registration.
Now, create activation_request.html template which will ask from user to confirm the link (This will show up in your console).
{% autoescape off %}
Hi {{ user.username }},
Please click the following link to confirm your registration:
http://{{ domain }}{% url 'activate' uidb64=uid token=token %}
{% endautoescape %}
Enter fullscreen mode Exit fullscreen mode
Check your console:
Create one more template named activation_sent.html. This will inform user that to check his/her mail to confirm registration
<h3>Activation link sent! Please check your console or mail.</h3>
Enter fullscreen mode Exit fullscreen mode
When user clicked the link, we have to check if the user exists and the token is valid.
def activate(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
# checking if the user exists, if the token is valid. if user is not None and account_activation_token.check_token(user, token):
# if valid set active true user.is_active = True
# set signup_confirmation true user.profile.signup_confirmation = True
user.save()
login(request, user)
return redirect('home')
else:
return render(request, 'activation_invalid.html')
Enter fullscreen mode Exit fullscreen mode
Once registration confirmed, user becomes active and be able to login.
Full code of views.py:
from django.contrib.auth import login, authenticate
from django.shortcuts import render, redirect, get_object_or_404, HttpResponseRedirect
from django.contrib.sites.shortcuts import get_current_site
from django.utils.encoding import force_text
from django.contrib.auth.models import User
from django.db import IntegrityError
from django.utils.http import urlsafe_base64_decode
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from .tokens import account_activation_token
from django.template.loader import render_to_string
from .forms import SignUpForm
from .tokens import account_activation_token
def home_view(request):
return render(request, 'home.html')
def activation_sent_view(request):
return render(request, 'activation_sent.html')
def activate(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
# checking if the user exists, if the token is valid. if user is not None and account_activation_token.check_token(user, token):
# if valid set active true user.is_active = True
# set signup_confirmation true user.profile.signup_confirmation = True
user.save()
login(request, user)
return redirect('home')
else:
return render(request, 'activation_invalid.html')
def signup_view(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.profile.first_name = form.cleaned_data.get('first_name')
user.profile.last_name = form.cleaned_data.get('last_name')
user.profile.email = form.cleaned_data.get('email')
# user can't login until link confirmed user.is_active = False
user.save()
current_site = get_current_site(request)
subject = 'Please Activate Your Account'
# load a template like get_template() # and calls its render() method immediately. message = render_to_string('activation_request.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
# method will generate a hash value with user related data 'token': account_activation_token.make_token(user),
})
user.email_user(subject, message)
return redirect('activation_sent')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode
Finally, let’s configure our urls.py:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from accounts.views import home_view, signup_view, activation_sent_view, activate
urlpatterns = [
path('admin/', admin.site.urls),
path('', home_view, name="home"),
path('signup/', signup_view, name="signup"),
path('sent/', activation_sent_view, name="activation_sent"),
path('activate/<slug:uidb64>/<slug:token>/', activate, name='activate'),
]
Enter fullscreen mode Exit fullscreen mode
I pushed all these projects in my GitHub you can clone which project you want by selecting right branch.
thepylot / django-advanced-signup-tutorial
Create Advanced User Sign Up View in Django | Step-by-Step
django-advanced-signup-tutorial
Create Advanced User Sign Up View in Django | Step-by-Step
Getting Started
This tutorial works on Python 3+ and Django 2+.
Clone the project by selecting right branch and run following commands:
python3 manage.py makemigrations accounts
python3 manage.py migrate
python3 manage.py runserver
Great! In this tutorial we used console to check our link but you can configure a production quality email service to send actual confirmation mail. My next tutorial will cover this topic so make sure you are following me on social media and check Reverse Python for more articles like this.
原文链接:Create Advanced User Sign Up View in Django | Step-by-Step
暂无评论内容