Authentication & permissions
Django ships a complete authentication system: a User model, password hashing, sessions, login views and a
permission framework. Learning to use it well saves you from writing (and securing) your own.
Login and logout with built-in views
# config/urls.py
from django.urls import include, path
urlpatterns = [
path("accounts/", include("django.contrib.auth.urls")),
# provides login/, logout/, password_change/, password_reset/ ...
]
# config/settings.py
LOGIN_URL = "login"
LOGIN_REDIRECT_URL = "dashboard"
LOGOUT_REDIRECT_URL = "home"
Create templates/registration/login.html with a form that posts to {% url 'login' %}. Since Django 5.0 the logout
view only accepts POST, so log out with a small form containing {% csrf_token %}, not a plain link.
A custom user model from day one
Start every new project with a custom user model, even if it is empty. Switching later is painful.
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
phone = models.CharField(max_length=15, blank=True)
# settings.py
AUTH_USER_MODEL = "accounts.User"
Refer to it in code with get_user_model() or, in model fields, settings.AUTH_USER_MODEL.
Protecting views
from django.contrib.auth.decorators import login_required, permission_required
@login_required
def dashboard(request):
return render(request, "dashboard.html", {"orders": request.user.orders.all()})
@permission_required("shop.change_product", raise_exception=True)
def edit_price(request, pk):
...
For class-based views use LoginRequiredMixin and PermissionRequiredMixin, listed before the view class.
Permissions and groups
Every model automatically gets four permissions: add_, change_, delete_ and view_, named like
shop.change_product. Assign them to groups (roles) and put users in groups.
| Group | Permissions |
|---|---|
| Cashier | view_product, add_order |
| Store manager | all of the above + change_product, change_order |
| Accountant | view_order, custom export_gst_report |
class Order(models.Model):
...
class Meta:
permissions = [("export_gst_report", "Can export GST report")]
Check permissions anywhere with request.user.has_perm("shop.export_gst_report"), and in templates with
{% if perms.shop.change_product %}.
Object-level rules
Built-in permissions are per model, not per row. For "users may only edit their own orders", filter the queryset:
from django.shortcuts import get_object_or_404
@login_required
def order_detail(request, pk):
order = get_object_or_404(Order, pk=pk, customer=request.user) # 404 for other people's orders
return render(request, "shop/order_detail.html", {"order": order})
Key takeaways
- Use the built-in auth views and password hashers; never store or compare raw passwords.
- Create a custom user model before your first migration.
- Group permissions into roles instead of assigning them user by user.
- Enforce ownership by filtering querysets, and remember hiding a button in a template is not security.
Practice
Add a Cashier and a Manager group to your shop project. Protect a price-edit view so only managers can use it,
show an "Edit" button only when perms.shop.change_product is true, and make order pages visible only to their owner.