mrkeyoor.com_
Wed 05 Aug 05:04 UTC
PyPIWeb Backendupdated 05 Aug 2026

django

Django is the batteries-included Python web framework: ORM with migrations, URL routing, templates, forms, sessions, authentication, an auto-generated admin interface, caching, and a security layer (CSRF, XSS escaping, SQL injection protection) all ship in the box and are designed to work together. You define models once and get database schema, admin CRUD screens, and form validation from the same definitions. It has been maintained since 2005 by the Django Software Foundation with a predictable release schedule and LTS versions. Version 6.0 requires Python 3.12+ and continues the gradual buildout of async views and background task hooks on top of a fundamentally sync-first core.

Verdict

Still the best default for database-backed web products in Python; the admin plus ORM plus auth combination remains unmatched. Choose FastAPI instead when the product is primarily an async API.

API stability5/5Formal deprecation policy: features are deprecated over two releases before removal, LTS versions get three years of support, and upgrade guides are thorough; code from years ago mostly upgrades with mechanical changes.
Docs5/5docs.djangoproject.com is a reference point for the whole industry: versioned tutorials, topic guides, how-tos, and a complete reference, all kept current with each release; the README itself points you through a reading order.
Maintenance5/5Maintained since 2005 by the Django Software Foundation with paid fellows doing triage, a published release calendar, prompt security releases, and steady activity (pushed 2026-08-03, 88k stars).
Ecosystem5/513M weekly downloads and one of the deepest package ecosystems anywhere: DRF, allauth, debug-toolbar, channels, wagtail, plus commercial hosting support everywhere Python runs.

Use it if

  • You are building a database-backed product (SaaS, marketplace, internal tools) and want auth, admin, ORM, and migrations working together on day one
  • The auto-generated admin alone would save you weeks; for content and ops teams it is a free back office
  • You value boring stability: documented deprecation policy, LTS releases, and upgrade paths that thousands of teams walk every cycle
  • You are hiring; Django developers and battle-tested packages (django-rest-framework, allauth, celery integrations) are easy to find
Skip it if

Setup reality

pip install django is painless (two direct dependencies: asgiref and sqlparse), and django-admin startproject gives you a running site in minutes, but note 6.0 requires Python 3.12 or newer. The honest cost comes later: settings.py grows unwieldy without a pattern for env-based config, static files in production confuse everyone the first time (collectstatic, STORAGES, WhiteNoise or a CDN), and deployment means picking an ASGI/WSGI server (gunicorn, uvicorn) plus a database driver, none of which the tutorial covers deeply. The project layout (project vs app distinction) also puzzles newcomers. Migrations are excellent but merge conflicts on migration files are a recurring team annoyance.

Patterns

Start a project and appproject-setup

python -m pip install django
django-admin startproject mysite
cd mysite
python manage.py startapp blog
python manage.py runserver

Register the new app in INSTALLED_APPS in settings.py or its models and templates are invisible to Django.

Define a modeldefine-model

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField(blank=True)
    author = models.ForeignKey(
        "auth.User", on_delete=models.CASCADE, related_name="posts"
    )
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

on_delete is mandatory on ForeignKey; auto_now_add is set once at insert and cannot be edited in admin.

Create and apply migrationsmigrations

python manage.py makemigrations
python manage.py migrate
python manage.py showmigrations

Commit migration files to git; two branches adding migrations to the same app need makemigrations --merge afterwards.

Query with the ORMorm-queries

from blog.models import Post

recent = (
    Post.objects.filter(title__icontains="django")
    .select_related("author")
    .order_by("-created_at")[:10]
)
count = Post.objects.filter(author__username="ada").count()

QuerySets are lazy and cached after first evaluation; select_related prevents the N+1 query on author access.

Wire a view to a URLview-and-url

# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, "blog/detail.html", {"post": post})

# mysite/urls.py
from django.urls import path
from blog import views

urlpatterns = [
    path("posts/<int:pk>/", views.post_detail, name="post_detail"),
]

get_object_or_404 turns a missing row into a 404 instead of an unhandled DoesNotExist exception.

Render data in a templatetemplate-render

{# blog/templates/blog/detail.html #}
<h1>{{ post.title }}</h1>
<p>by {{ post.author.username }} on {{ post.created_at|date:"Y-m-d" }}</p>
{% if post.body %}
  <div>{{ post.body|linebreaks }}</div>
{% endif %}

Templates auto-escape HTML by default; only use the safe filter on content you fully control.

Validate input with a ModelFormmodel-form

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ["title", "body"]

# in a view
form = PostForm(request.POST or None)
if request.method == "POST" and form.is_valid():
    post = form.save(commit=False)
    post.author = request.user
    post.save()

commit=False lets you set fields the form does not expose before the row is written.

Get a free admin interfaceadmin-register

# blog/admin.py
from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ("title", "author", "created_at")
    search_fields = ("title",)
    list_filter = ("created_at",)

# then: python manage.py createsuperuser

The admin is for trusted staff, not end users; putting customers in it is a common and regrettable shortcut.

Require login for a viewauth-protect-view

from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request, "dashboard.html", {"user": request.user})

Unauthenticated users are redirected to settings.LOGIN_URL, which you almost always need to set explicitly.

List page with a class-based viewclass-based-view

from django.views.generic import ListView
from .models import Post

class PostListView(ListView):
    model = Post
    paginate_by = 20
    ordering = "-created_at"

# urls.py
path("posts/", PostListView.as_view(), name="post_list")

The default template name is blog/post_list.html and the context variable is object_list (or post_list); both trip up beginners.

Set a custom user model on day onecustom-user-model

# accounts/models.py
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

# settings.py
AUTH_USER_MODEL = "accounts.User"

Do this before the first migrate; switching user models on a live database later is genuinely painful.

Read settings from the environmentsettings-env

# settings.py
import os

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",")

DEBUG=True in production leaks stack traces and settings; ALLOWED_HOSTS must be set once DEBUG is off or every request 400s.

Alternatives

PackageRegistryPick it when
fastapiPyPIYou are building an API-first or async-heavy service and want typed request/response models
flaskPyPIYou want a small, unopinionated core and are happy assembling your own stack piece by piece
litestarPyPIYou want a modern typed ASGI framework with more batteries than Flask but less legacy than Django