DEV Community

Cover image for Day 69 of 100 Days Of Code — Authentication in Django
M Saad Ahmad
M Saad Ahmad

Posted on

Day 69 of 100 Days Of Code — Authentication in Django

Yesterday I built forms that let users submit data. Today, for day 69, it was Authentication in Django. Django ships with a complete authentication system out of the box. User registration, login, logout, password handling, and protecting pages from unauthenticated access. All of it, without installing a single extra package.


What Django Gives You for Free

Django's django.contrib.auth app is included by default in INSTALLED_APPS. It provides:

  • A built-in User model
  • Login and logout views
  • Password hashing
  • Session management
  • Permission and group system
  • Decorators to protect views

You don't build any of this from scratch.


The Built-in User Model

Django's User model lives in django.contrib.auth.models. It comes with these fields by default:

  • username
  • password — stored as a hash, never plain text
  • email
  • first_name, last_name
  • is_active
  • is_staff — can access admin panel
  • is_superuser — has all permissions
  • date_joined
  • last_login
from django.contrib.auth.models import User

# create a user
user = User.objects.create_user(username='haris', password='securepass123')

# get a user
user = User.objects.get(username='haris')
print(user.email)
print(user.is_authenticated)  # True
Enter fullscreen mode Exit fullscreen mode

Always use create_user() instead of create(); it handles password hashing automatically.


User Registration

Django doesn't provide a registration view out of the box, but it does provide UserCreationForm: a built-in form for creating new users.

# core/forms.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class RegisterForm(UserCreationForm):
    email = forms.EmailField(required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']
Enter fullscreen mode Exit fullscreen mode

UserCreationForm already handles password confirmation and validation. We extend it here just to add an email field.

# core/views.py
from django.shortcuts import render, redirect
from .forms import RegisterForm

def register(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')
    else:
        form = RegisterForm()

    return render(request, 'core/register.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode
<!-- core/templates/core/register.html -->
{% extends 'core/base.html' %}

{% block content %}
    <h1>Register</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Create Account</button>
    </form>
    <p>Already have an account? <a href="{% url 'login' %}">Login</a></p>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Login and Logout

Django provides built-in views for login and logout. You just wire them to URLs.

# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('core.urls')),
    path('login/', auth_views.LoginView.as_view(template_name='core/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]
Enter fullscreen mode Exit fullscreen mode

You provide your own template for login, Django handles all the logic.

<!-- core/templates/core/login.html -->
{% extends 'core/base.html' %}

{% block content %}
    <h1>Login</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Login</button>
    </form>
    <p>Don't have an account? <a href="{% url 'register' %}">Register</a></p>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

After a successful login, Django redirects to /accounts/profile/ by default. Override this in settings.py:

LOGIN_REDIRECT_URL = 'home'
LOGOUT_REDIRECT_URL = 'login'
Enter fullscreen mode Exit fullscreen mode

Accessing the Current User in Views

Once a user is logged in, Django attaches them to every request as request.user.

def home(request):
    print(request.user)               # the User object
    print(request.user.username)      # 'haris'
    print(request.user.is_authenticated)  # True if logged in, False if not
Enter fullscreen mode Exit fullscreen mode

An unauthenticated user is represented by AnonymousUser. Always check is_authenticated before accessing user data.


Accessing the Current User in Templates

Django automatically makes request.user available in every template through the request context processor, which is included by default.

{% if request.user.is_authenticated %}
    <p>Welcome, {{ request.user.username }}</p>
    <a href="{% url 'logout' %}">Logout</a>
{% else %}
    <a href="{% url 'login' %}">Login</a>
    <a href="{% url 'register' %}">Register</a>
{% endif %}
Enter fullscreen mode Exit fullscreen mode

Put this in your base.html so it appears on every page.


Protecting Views

The @login_required Decorator

Use this on any view that requires authentication:

from django.contrib.auth.decorators import login_required

@login_required
def create_post(request):
    ...
Enter fullscreen mode Exit fullscreen mode

If an unauthenticated user tries to access this view, Django redirects them to the login page automatically. Set where Django sends them in settings.py:

LOGIN_URL = 'login'
Enter fullscreen mode Exit fullscreen mode

Checking in the View Manually

Sometimes you need more control:

def some_view(request):
    if not request.user.is_authenticated:
        return redirect('login')
    ...
Enter fullscreen mode Exit fullscreen mode

Linking Posts to Users

In most apps, content belongs to a user. Add a ForeignKey to the User model on your Post:

from django.contrib.auth.models import User

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
Enter fullscreen mode Exit fullscreen mode

on_delete=models.CASCADE means if the user is deleted, their posts are deleted too.

When saving a post from a form, attach the current user before saving:

@login_required
def create_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect('home')
    else:
        form = PostForm()

    return render(request, 'core/create_post.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode

commit=False tells Django to create the object in memory but not save to the database yet, giving you a chance to set extra fields like author before the actual save.


Restricting Actions to the Owner

You don't want users editing each other's posts:

@login_required
def edit_post(request, pk):
    post = get_object_or_404(Post, pk=pk)

    if post.author != request.user:
        return redirect('home')

    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            form.save()
            return redirect('home')
    else:
        form = PostForm(instance=post)

    return render(request, 'core/edit_post.html', {'form': form})
Enter fullscreen mode Exit fullscreen mode

URL Setup

# core/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('register/', views.register, name='register'),
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
    path('post/create/', views.create_post, name='create_post'),
    path('post/<int:pk>/edit/', views.edit_post, name='edit_post'),
]
Enter fullscreen mode Exit fullscreen mode

The Full Authentication Flow

User visits /post/create/
        ↓
@login_required checks request.user.is_authenticated
        ↓
Not authenticated → redirect to /login/
        ↓
User fills in login form → POST to /login/
        ↓
Django validates credentials, creates session
        ↓
Redirect to LOGIN_REDIRECT_URL → /
        ↓
User visits /post/create/ again
        ↓
Now authenticated → view runs normally
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

Django's authentication system is one of the best reasons to use it. Registration, login, logout, session management, password hashing, all handled. Today the app became a real multi-user system where posts belong to users, protected pages require login, and only owners can edit their own content.

Thanks for reading. Feel free to share your thoughts!

Top comments (0)