Django from Basics to Production · Django Fundamentals
Users, Login and Permissions
Use Django's auth system: create users with hashed passwords, log in, protect views, and check permissions.
What you'll build
A user with a securely hashed password, a protected view that requires login, a view that requires a specific permission, and requests as anonymous, as a regular user and as a user granted the permission.
The idea
django.contrib.authprovides users, groups, permissions and password hashing (PBKDF2 by default; Argon2 and scrypt are supported).authenticate()andlogin()establish a session; the test client'sforce_login()does it in tests.@login_requiredand@permission_requiredprotect function views (LoginRequiredMixinandPermissionRequiredMixinfor classes).- Permissions like
shop.change_orderare created per model; assign them through groups.
The code
import django
from django.conf import settings
settings.configure(
SECRET_KEY="dev-only", ALLOWED_HOSTS=["testserver"], ROOT_URLCONF=__name__, LOGIN_URL="/login/",
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
INSTALLED_APPS=["django.contrib.contenttypes", "django.contrib.auth", "django.contrib.sessions"],
MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware"],
)
django.setup()
from django.core.management import call_command
call_command("migrate", verbosity=0)
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import Permission, User
from django.http import JsonResponse
from django.urls import path
@login_required
def dashboard(request):
return JsonResponse({"hello": request.user.username})
@permission_required("auth.view_user", raise_exception=True)
def user_list(request):
return JsonResponse({"users": list(User.objects.values_list("username", flat=True))})
urlpatterns = [path("dashboard/", dashboard), path("users/", user_list)]
ana = User.objects.create_user("ana", "[email protected]", "correct horse battery")
print("stored password:", ana.password.split("$")[0], "... (algorithm, never the password)")
from django.test import Client
c = Client()
r = c.get("/dashboard/")
print("anonymous /dashboard/:", r.status_code, "->", r.headers.get("Location"))
c.force_login(ana)
print("ana /dashboard/:", c.get("/dashboard/").json())
print("ana /users/ without permission:", c.get("/users/").status_code)
ana.user_permissions.add(Permission.objects.get(codename="view_user"))
c.force_login(User.objects.get(pk=ana.pk)) # reload: permissions are cached per user object
print("ana /users/ with permission:", c.get("/users/").json())
Output
stored password: pbkdf2_sha256 ... (algorithm, never the password)
anonymous /dashboard/: 302 -> /login/?next=/dashboard/
ana /dashboard/: {'hello': 'ana'}
ana /users/ without permission: 403
ana /users/ with permission: {'users': ['ana']}Run this on your computer — see “Get Started” for setup.
Common mistakes
- Storing passwords yourself instead of
set_password()/create_user(). - Relying on hiding links instead of protecting views.
- Checking
is_staffor usernames in code instead of permissions and groups.
Key points
- Django hashes passwords and manages sessions for you.
- Protect views with
login_requiredand permission checks. - Grant permissions through groups; check them in views (and object ownership in queries).
Further reading: User authentication in Django · Password management
Practice questions
How does Django store user passwords?
Answer
As a salted hash with the algorithm and iteration count (PBKDF2 by default), never in plain text; set it with set_password() or create_user().
What does @login_required do for anonymous users?
Answer
It redirects them to settings.LOGIN_URL with the original path in the next parameter.
Why are permissions better than checking usernames or is_staff in code?
Answer
They're data you can grant through groups without code changes, and they express exactly which action is allowed.
Open in the app to mark lessons complete, take notes and track your progress.