The Spectrum Dispatch News

technology

What Developers Love About Django: Middleware, Models, and Simplicity

A developer reflects on Django’s strengths after years of use, highlighting middleware flexibility, model inheritance, and strict view patterns that reduce cognitive load.

What Developers Love About Django: Middleware, Models, and Simplicity

The author of the essay explains that their initial goal was to celebrate the parts of Django that have proven useful and enduring over years of use in building Buttondown. They note that one challenge in praising Django is its tendency to become ‘invisible’ in a codebase — blending so smoothly with Pythonic and business logic that it’s hard to isolate Django-specific contributions. Despite this poetic observation, they set out to identify which parts of Django provided the most long-term leverage. First, they highlight Django’s middleware system, describing it as simple yet powerful. Middlewares are functions that operate on the request/response lifecycle, requiring only that they adopt the correct protocol. The author cites practical uses such as routing requests by subdomain, tracking UTM parameters, setting security headers, recording pageviews, enriching logs, and stamping responses with the deployed git SHA to detect stale browser tabs. They argue that more Django developers should take advantage of this tool. Second, they discuss model design, noting that while they avoid polymorphic models due to complexity and lack of use cases, every model in their codebase inherits from a base model. This base class provides opt-in, additive features: UUID primary keys, creation timestamps, type-prefixed IDs (e.g., sub_…, em_…), implicit change tracking via handle__change methods, durable provenance through transition tables, per-field validation hooks, and soft-delete support. The author emphasizes that adding new functionality — like provenance tracking — requires only a single method in the model, with no migrations or refactoring needed. Third, they describe their ‘actions’ pattern, where model behaviors (e.g., banning a subscriber) live in dedicated files under an actions/ folder, each exposing a single call() function to prevent method sprawl on models. Fourth, they outline strict view conventions: views must be function-based, live in their own files, and expose a function named ‘view’. This doctrinaire approach reduces context-switching and avoids unnecessary indirection, making code more maintainable by prioritizing failure avoidance over clever solutions. Fifth, they touch on testing, noting their use of pytest and pytest-django with hand-constructed fixtures for performance, avoiding tools like Factory Boy. Tests are plain functions that assert against real database rows. The author also discusses what they don’t use in Django: signals (due to anti-pattern risks in internal code), class-based views (because switching between function- and class-based views hurts readability), conventional Django apps (due to migration complexity and unclear benefits over simple folder grouping), checks (though they acknowledge their utility), and Django’s form abstraction (opting instead for a hydration-based frontend where Django renders a thin shell and seeds data for Vue to hydrate). Finally, they reveal that they chose Django initially simply because it was what they knew at the time — a ‘boring but revealing’ reason that underscores how familiarity and trust in a framework can outweigh novelty.

What Developers Love About Django: Middleware, Models, and Simplicity

Key facts

  • Every model in Buttondown inherits from a base model that provides UUID primary keys, creation dates, type-prefixed IDs, implicit change tracking, durable provenance, per-field validation, and soft-delete support.
  • Django middleware functions act on the request/response lifecycle and are used for tasks such as subdomain routing, UTM tracking, CSP headers, pageview recording, log enrichment, and build version stamping.
  • Buttondown’s views must be function-based, live in individual files, and expose a function named ‘view’ to reduce context-switching and improve maintainability.
  • The team avoids Django’s form abstraction, instead using a hydration pattern where Django renders a thin shell and seeds data for Vue to hydrate client-side.
  • Buttondown uses only one signal (a link to django-allauth) and avoids internal signals due to concerns about code reasoning and performance optimization.
  • The author selected Django for Buttondown because it was what they knew at the outset, not due to any technical evaluation.

Sources

← All posts