Skip to content

atik-gohel/Django-Authentication-Login-Logout-Signup-Custom-User

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Django-Authentication-Login-Logout-Signup-Custom-User

Django Authentication – How to build Login/Logout/Signup for custom User

Database name : atikgohel

you can change database name : DjangoAuth\settings.py line number 85

Building user User authentication is not easy, in almost case, it’s complicated. Fortunately, Django has a powerful built-in User authentication that helps us create our Authentication system fast. By default, the User model in Django auth app contains fields: username, password, email, first_name, last_name… However, using our own custom user model allows us deal with user profile more comfortably. For example, what if we want to add more fields: full_name or age?

In this tutorial, we’re gonna look at way to customize authentication in Django (version 3.1.7) using subclass of AbstractBaseUser: AbstractUser. All User authentication data will be stored in MySQL database that we’ll show you how to config the datasource.

Django Custom Authentication Project overview

We will build a Dajngo Project with Authentication app that has login/logout/signup with custom fields such as full name and age:

We will code our custom signup() function, login() and logout() is automatically implemented by Django auth.

All User data will be saved in MySQL/PostgreSQL database.

Project Structure

Here is the folders and files structure that we will create in the next steps.

Create a manually highlighted folder and file
1. authen\apps.py
2. authen\forms.py
3. static folder
4. static\css folder -> bootstrap.css
5. static\js folder -> bootstrap.js
6. templates floder
7. templates\auth folder
8. templates\auth\base.html
9. templates\auth\index.html
10.templates\auth\login.html
11.templates\auth\signup.html

! Go to the following link and copy and paste it It's just a bootstrap file !
bootstrap.css
bootstrap.js

Setup Django Custom Authentication Project

Create Django project named DjangoAuth with command:
django-admin startproject DjangoAuth

Run following commands to create new Django App named authen inside the project:
cd DjangoAuth
python manage.py startapp authen

Open authen\apps.py, we can see AuthenConfig class (subclass of the django.apps.AppConfig) that represents our Django app and its configuration:

 
from django.apps import AppConfig

class AuthenConfig(AppConfig): name; = 'authen'

Open settings.py, find INSTALLED_APPS, then add:

  INSTALLED_APPS = [
      ...
      'authen.apps.AuthenConfig',
  ]

Specify Derectory

  • Specify template directory
  •   TEMPLATES = [
          {
              'BACKEND': 'django.template.backends.django.DjangoTemplates',
              'DIRS': [os.path.join(BASE_DIR, 'templates')],
              'APP_DIRS': True,
              ...
          },
      ]
    

  • Specify static directory
  • STATIC_URL = '/static/'
    

    STATICFILES_DIRS = [ BASE_DIR / "static" ]

    Config Django project to work with database

    • MySQL Database

    Install & Import Python MySQL Client

    We have to install Python MySQL Client to work with MySQL database.
    In this tutorial, we use pymysql: pip install pymysql.

    Once the installation is successful, import this module in DjangoAuth/__init__.py:

    import pymysql
    
    pymysql.install_as_MySQLdb()
    

    • Setup MySQL Database engine

    Open settings.py and change declaration of DATABASES:

      DATABASES = {
           'default': {
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'atikGohel',
              'USER' : 'root',
              'PASSWORD' : '',
              'HOST': 'localhost',
              'PORT' : '3306',
          }
      }
    

    Create Custom User Model

    • Create a new Custom User Model

    In authen/models.py, create a new User model called CustomUser that extends AbstractUser (a subclass of AbstractBaseUser), then add two custom fields: full_name and age:

      from django.db import models
      from django.contrib.auth.models import AbstractUser
    
      class CustomUser(AbstractUser):
          full_name = models.CharField(max_length=100, blank=False)
          age = models.PositiveIntegerField(null=True, blank=True)
    

    age field uses both null and blank:
    null is for database. null=True indicates that we can store it in database entry as NULL (no value).
    blank is for validation. blank=True accepts empty value for the form field, so blank=False indicates that the value is required.


    • Specify Custom User Model in setting.py

    In setting.py, we add AUTH_USER_MODEL config to specify our custom user model instead of Django built-in User model. The model is named CustomUser and exists within authen app, so we refer to it as authen.CustomUser:

      AUTH_USER_MODEL = 'authen.CustomUser'
    

    Create a new form for UserCreationForm

    Now we create a new file in the authen app called forms.py:

    from django import forms
    from django.contrib.auth.forms import UserCreationForm, UserChangeForm
    from .models import CustomUser
    

    class SignUpForm(UserCreationForm): full_name = forms.CharField(max_length=100, help_text='Required. 100 charaters of fewer.') class Meta: model = CustomUser fields = UserCreationForm.Meta.fields + ('full_name', 'age',)

    Our SignUpForm extends the UserCreationForm.

    We set model to CustomUser and use default fields by Meta.fields which includes all default fields (including username, first_name, last_name, email, password, groups…). We simply plus our custom fields (full_name, age) at the end and it will display automatically on signup page.

    When a user signs up for a new account, the default form only asks for a username, email, and password. Now, it also requires full_name and age.

    Activate the User Model

    Now our new database model is created, we need to update Django in 2 steps:

    • Create migration file

    Run the command:
    python manage.py makemigrations authen

    We can see output text:

      Migrations for 'authen':
      authen\migrations\0001_initial.py
        - Create model CustomUser
    

    It indicates that the authen/migrations/0001_initial.py file includes code to create CustomUser data model:

    # Generated by Django 3.1.7 on 2021-04-06 18:05
    
    import django.contrib.auth.models
    import django.contrib.auth.validators
    from django.db import migrations, models
    import django.utils.timezone
    
    class Migration(migrations.Migration):
    
      initial = True
    
      dependencies = [
        ('auth', '0012_alter_user_first_name_max_length'),
        ]
    
      operations = [
          migrations.CreateModel(
              name='CustomUser',
              fields=[
                  ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                  ('password', models.CharField(max_length=128, verbose_name='password')),
                  ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
                  ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
                  ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
                  ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
                  ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
                  ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
                  ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
                  ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
                  ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
                  ('full_name', models.CharField(max_length=100)),
                  ('age', models.PositiveIntegerField(blank=True, null=True)),
                  ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
                  ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
              ],
              options={
                  'verbose_name': 'user',
                  'verbose_name_plural': 'users',
                  'abstract': False,
              },
              managers=[
                  ('objects', django.contrib.auth.models.UserManager()),
              ],
          ),
        ]
    
     

    The generated code defines a subclass of the django.db.migrations.Migration. It has an operation for creating CustomUser model table. Call to migrations.CreateModel() method will create a table that allows the underlying database to persist the model.

    You can see that we have not only user default fields but also custom fields (full_name, age).

    • Generate database table

    Run the following Python script to apply the generated migration:
    python manage.py migrate

    The output text:

     
    Operations to perform:
      Apply all migrations: admin, auth, authen, contenttypes, sessions
    Running migrations:
      Applying contenttypes.0001_initial... OK
      Applying contenttypes.0002_remove_content_type_name... OK
      Applying auth.0001_initial... OK
      Applying auth.0002_alter_permission_name_max_length... OK
      Applying auth.0004_alter_user_username_opts... OK
      Applying auth.0005_alter_user_last_login_null... OK
      Applying auth.0006_require_contenttypes_0002... OK
      Applying auth.0007_alter_validators_add_error_messages... OK        
      Applying auth.0008_alter_user_username_max_length... OK
      Applying auth.0009_alter_user_last_name_max_length... OK
      Applying auth.0010_alter_group_name_max_length... OK
      Applying auth.0011_update_proxy_permissions... OK
      Applying auth.0012_alter_user_first_name_max_length... OK
      Applying admin.0002_logentry_remove_auto_add... OK
      Applying admin.0003_logentry_add_action_flag_choices... OK
    

    A warning may come to you, ignore it 👇👇

    System check identified some issues:
    

    WARNINGS: ?: (mysql.W002) MariaDB Strict Mode is not set for database connection 'default' HINT: MariaDB's Strict Mode fixes many data integrity problems in MariaDB, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it. See: https://docs.djangoproject.com/en/3.1/ref/databases/#mysql-sql-mode
    Operations to perform: Apply all migrations: admin, auth, authen, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0001_initial... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK
    Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK

    Check MySQL Database, for example, now we can see that a table for CustomUser model was generated and it’s named authen_customuser:

    Set urlpatterns & handle signup/login/logout requests

    • Set url patterns

    Open the project-level DjangoAuth\urls.py file

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include('authen.urls')),
    ]
    

    Open the app-level authen\urls.py file, we’re gonna use built-in django.contrib.auth module to handle login/logout requests:

    from django.urls import path
    from django.contrib.auth import views as auth_views
    from authen import views
    
    urlpatterns = [
        path('', views.home, name="home"),
        path('login/', auth_views.LoginView.as_view(template_name='auth/login.html'),name='login'),
        path('logout/', auth_views.LogoutView.as_view(), name='logout'),
        path('signup/', views.signup, name="signup")
    ]
    

    Using auth_views.LogoutView helps us handle logout request automatically, we only need to call {% url 'logout' %} where we want to make logout event in the HTML template.

    The next step is to specify where to redirect the user upon a successful login/logout.
    Open project setting.py, then set values for LOGIN_REDIRECT_URL and LOGOUT_REDIRECT_URL:

     
    LOGIN_REDIRECT_URL = 'home'
    LOGOUT_REDIRECT_URL = 'home'
    

    Now, after login/logout, if we don’t indicate where to come, the user will be redirected to the 'home' template which is our homepage.

    • Custom signup request

    Inside authen/views.py, define functions for handling signup request and homepage:

    from django.shortcuts import render, redirect
    from django.contrib.auth import login, authenticate
    from .forms import SignUpForm
    
    def home(request):
        return render(request, 'auth/home.html')
    
    def signup(request):
        if request.method == 'POST':
            form = SignUpForm(request.POST)
            if form.is_valid():
                user = form.save()
                user.save()
                raw_password = form.cleaned_data.get('password1')
                user = authenticate(username=user.username, password=raw_password)
                login(request, user)
                return redirect('home')
        else:
            form = SignUpForm()
        return render(request, 'auth/signup.html', { 'form' : form })
    

    Now we dive into signup() function. It gets user data from HTTP POST request which is handled by SignUpForm, save user to database.

    Then we use authenticate() function and login() function from django.contrib.auth to log the user in.

    If the process is successful, redirect to homepage, otherwise, return to signup.html template.

    Edit or Create Django template for User Authentication

    • Edit or Create base.html

    In templates\auth folder, edit or create new HTML file named base.html:

    The most powerful – and thus the most complex – part of Django’s template engine is template inheritance. Template inheritance allows you to build a base “skeleton” template that contains all the common elements of your site and defines blocks that child templates can override.

    it's template inheritance

     
      <!DOCTYPE html> {% load static %}
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title> {% block title %}{% endblock title %} | Atik Gohel </title>
          <link rel="stylesheet" href="{% static 'css/bootstrap.css'%}">
      </head>
      <body>
          <div class="container mt-5">
              <h3 class="text-center alert alert-danger"> Login and Singup Example </h3>
          </div>
          {% block body %}{% endblock body %}
          <script src=" {% static 'js/bootstrap.js' %}"> </script>
      </body>
      </html>
      
    

    • Edit or Create home.html

    We’re gonna use Django built-in url templatetag to create links for login/logout/signup requests.

    In templates\auth folder, edit or create new HTML file named home.html:

     
      {% extends 'auth/base.html' %}
      {% block title %}Home{% endblock title %}
    
      {% block body %}
    
        <div class="container">
          <div class="row">
              <div class="clo-12">
                  <div class="alert alert-info">this is home page</div>
    
                  {% if user.is_authenticated %} 
    
                      Hi {{ user.full_name }}, Welcome to My site!
                      <a href="logout/" class="btn btn-danger">Logout</a> 
    
                  {% else %}
    
                      You are not logged in!
                      <br>
                      <a href="{% url 'login' %}" class="btn btn-primary mt-3">
                          Log In
                      </a>
                      <a href="{% url 'signup' %}" class="btn btn-primary mt-3">
                          SignUp
                      </a>
    
                  {% endif %}
              </div>
          </div>
      </div>
    
      {% endblock body %}
      
    

    If you use {% extends %} in a template, it must be the first template tag in that template. Template inheritance won’t work, otherwise.
    {% block title %}{% endblock title %} it is title block
    {% block body %}{% endblock body %} it is title block

    We can use is_authenticated attribute to specify whether the user is logged in or not, then show his full name for a website greeting.

    So, when user aren’t logged in, it looks like:

    • Edit or Create Django custom Signup.html

    – We’re gonna use HTML <form> tag with HTTP POST method.
    – We add {% csrf_token %}to protect our form from cross-site scripting attacks.

    In templates\auth folder, edit or create new HTML file named signup.html:

      {% extends 'auth/base.html' %}
      {% block title %}Singup{% endblock title %} 
      {% block body %}
      <div class="container">
          <div class="row">
              <div class="clo-12">
                  <div class="alert alert-info">this is Singup page</div>
                  <a href="{% url 'home' %}" class="btn btn-primary mt-3">Home</a>
                  <a href="{% url 'login' %}" class="btn btn-primary mt-3">Login</a>
                  <br>
                  <form method="post">
                      {% csrf_token %}
                      {% for field in form %}
                      <p>
                          {{ field.label_tag }}<br>
                          {{ field }}
                          {% if field.help_text %}
                              <small style="color: green">{{ field.help_text }}</small>
                          {% endif %}
                          {% for error in field.errors %}
                              </p><p style="color: red">{{ error }}</p>
                          {% endfor %}
                      <p></p>
                      {% endfor %}
                      <button type="submit" class="btn btn-primary mt-3">Sign up </button>
                  </form>
              </div>
          </div>
      </div>
    
      {% endblock body %}
    

    it looks like:

    • Create Django custom login.html

    In templates\auth folder, edit or create new HTML file named login.html:

    {% extends 'auth/base.html' %} 
    {% block title %}Login{% endblock title %} 
    
    {% block body %}
    
    <div class="container">
        <div class="row">
            <div class="clo-12">
    
                <div class="alert alert-info">this is Login page</div>
                
                <a href="{% url 'home' %}" class="btn btn-primary mt-3">Home</a>
                <a href="{% url 'signup' %}" class="btn btn-primary mt-3">SignUp</a>
                <br><br><br>
                <form method="post">
                    {% csrf_token %}
                    {{ form.as_p }}
                    <button type="submit" class="btn btn-primary mt-3">Login</button>
                </form>
                
            </div>
        </div>
    </div>
    
    {% endblock body %}
    
    

    We use {{ form.as_p }} to render it within paragraph <p> tags.
    It looks like:

    Congratulations & Now Run Progrma

    – Run Django project with command:
    python manage.py runserver

    – Open browser with url http://localhost:8000/, then go to /signup page and fill your information:

    Click on Sign up Button, if the process is sucessful, the browser will turn into /home page with your information:

    Click on Log out and see the result:

    Go to /login page and fill Username and Password:

    Click on Login to check authentication.

    Now check MySQL database:

    Source Code

    🙌 you can Download Source code 🙌


    By Atik Gohel | April 7, 2021.