DEV Community

The Practical Developer
The Practical Developer

Posted on Originally published at practicaldevblog.blogspot.com

Django Explained: Python's Batteries-Included Framework

Introduction

Django is Python's most popular web framework, and its philosophy is the opposite of minimal: "batteries included". Where some frameworks give you a router and leave the rest to third-party packages, Django ships an ORM, an admin panel, authentication, forms, and security protections in the box — which is why it powers Instagram, Pinterest, and countless internal tools.

Models: describe data, get everything else

In Django you describe your data once, as Python classes, and the framework generates the database tables, the forms, and much of the admin interface from that description:

# blog/models.py
from django.db import models
class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    author = models.ForeignKey("auth.User", on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    def __str__(self):
        return self.title
Enter fullscreen mode Exit fullscreen mode

ForeignKey is the relationship to a user (the same idea as Postgres's REFERENCES), and on_delete=models.CASCADE says deleting a user deletes their posts too. Run two commands (makemigrations, migrate) and the tables exist — no SQL written by hand.

The admin panel: free, and genuinely useful

Django's most famous feature is its automatic admin panel: a complete, secure interface for managing your data, generated from your models with zero extra code. Register a model, log in, and you can create, edit, search, and filter posts immediately. For content-driven sites and internal tools, this alone can save weeks — it is the feature other frameworks' users most often envy.

Views and URLs: explicit and readable

Django keeps routing explicit: a URL file maps paths to view functions, and views are plain Python:

# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path("posts/", views.post_list, name="post_list"),
    path("posts/<int:post_id>/", views.post_detail, name="post_detail"),
]
# blog/views.py
def post_detail(request, post_id):
    post = Post.objects.get(id=post_id)
    return render(request, "blog/detail.html", {"post": post})
Enter fullscreen mode Exit fullscreen mode

<int:post_id> captures a number from the URL and passes it to the view, which fetches the post and renders a template. Nothing is hidden by magic — the flow from URL to database to page is visible in a few lines, which makes Django applications unusually easy to debug.

Takeaway

Describe data once and get tables, forms, and an admin panel; explicit routing; and security (CSRF protection, SQL injection prevention, password hashing) on by default. Django's trade is flexibility for completeness — and for most web applications, that trade wins.

Originally published at https://practicaldevblog.blogspot.com/2026/10/django-explained-pythons-batteries.html

Top comments (0)