Some notes on starting to use Django

📝 Julia Evans 🔗 jvns.ca 📅 2026-01-27 ⭐ Rating: ★★★★★

Julia Evans' personal first impressions learning Django after avoiding web frameworks for years. High-value practical notes from a developer who normally writes single-file Go binaries or static sites.

Core Insights

1. Less Magic Than Rails — Explicit Over Convention

After trying Rails in 2020 and finding it hard to return to projects after months away (conventions like resources :topics hide the actual configuration), Julia finds Django more "explicit." Five main files in a small project: urls.py, models.py, views.py, admin.py, tests.py — everything else is explicitly referenced. This makes long-term project maintenance much easier.

2. Built-in Admin Interface

Django's admin is surprisingly powerful with minimal code. Example customization:

@admin.register(Zine)
class ZineAdmin(admin.ModelAdmin):
    list_display = ["name", "publication_date", "free", "slug", "image_preview"]
    search_fields = ["name", "slug"]
    readonly_fields = ["image_preview"]
    ordering = ["-publication_date"]

3. ORM: Rethinking the "I Can Write My Own SQL" Attitude

Julia moved from "ORMs? Who needs them?" to genuine appreciation. Django's join syntax using __ is elegant:

Zine.objects
  .exclude(product__order__email_hash=email_hash)

This query spans 5 tables. The tradeoff: zero performance concern for small sites, much less typing, much easier to read.

4. Automatic Migrations Feel Like Magic

Changing models.py → Django generates migration scripts like migrations/0006_delete_imageblob.py. Critical for projects where the data model evolves frequently. "It really feels like magic."

5. SQLite in Production: A Practical Choice

After bad experiences with Postgres complexity, Julia runs all small sites on SQLite. Backup is trivial: VACUUM INTO → copy single file. Used on Mess with DNS which has significantly more writes. References these instructions for production SQLite.

6. Batteries-Included: Email, CSRF, CSP All Built In

Configuration to save emails to file in dev mode:

# settings/dev.py
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = BASE_DIR / "emails"

Production SMTP config with environment variables is similarly minimal.

7. The Settings File Anxiety

One remaining friction: settings.py uses global variables. No IDE autocomplete/lint. A typo like WSGI_APPLICATOIN vs WSGI_APPLICATION would be silent. Python language server support can't help here — unlike typed code.

Key Takeaways

Tags

django web-framework python orm sqlite personal-experience developer-experience 2026