Django Master Hub + Recipes





Django Master Hub + Recipes (Python Web) | Pythoneo






Django Master Hub + Recipes (Python Web)

Project structure, models/ORM, forms/CBVs, templates, auth, DRF, settings/env, static/media, testing, deployment—copy‑paste ready.

Updated: 2025‑08‑20
Covers: Django 4.x → 5.x
License: CC BY 4.0
Start

Quickstart & Project Structure

# Install
pip install "Django>=4.2" dj-database-url python-dotenv

# Create project & app
django-admin startproject config .
python manage.py startapp core

# Project layout (suggested)
.
├─ config/              # settings, urls, wsgi/asgi
│  ├─ settings.py
│  ├─ urls.py
│  ├─ asgi.py / wsgi.py
├─ core/                # your app(s)
│  ├─ models.py, views.py, urls.py, forms.py, tests.py
├─ templates/           # global templates
├─ static/              # global static
├─ media/               # uploaded files
├─ .env                 # environment variables (never commit)
└─ manage.py
Keep apps small and focused. Use a config package for settings separation (base/dev/prod) or environment‑driven settings with python‑dotenv.
Data

Models, Migrations, ORM

# core/models.py
from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

class Post(models.Model):
    title = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(unique=True)
    body = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)

    class Meta:
        ordering = ["-created"]

    def __str__(self): return self.title
# Migrations
python manage.py makemigrations
python manage.py migrate

# ORM examples
from core.models import Post
Post.objects.filter(published=True).select_related("author")[:10]
Post.objects.create(title="Hello", slug="hello", body="...", author=request.user)
Use select_related/prefetch_related to avoid N+1 queries. Add DB indexes on frequent filters. Keep migrations atomic and review before applying.
Control

Views & Class‑Based Views (CBVs)

# core/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path("", views.HomeView.as_view(), name="home"),
    path("post/<slug:slug>/", views.PostDetail.as_view(), name="post-detail"),
]

# config/urls.py
from django.urls import path, include
urlpatterns = [ path("", include("core.urls")) ]
# core/views.py
from django.views.generic import TemplateView, DetailView, ListView
from .models import Post

class HomeView(ListView):
    model = Post
    queryset = Post.objects.filter(published=True)
    template_name = "home.html"
    context_object_name = "posts"
    paginate_by = 10

class PostDetail(DetailView):
    model = Post
    template_name = "post_detail.html"
Prefer CBVs for common patterns (ListView, DetailView, Create/Update/DeleteView). Use mixins for auth/permissions. Fall back to FBVs for very custom flows.
View

Templates & Tags

# templates/base.html
<!doctype html>
<html>
  <head> <title>{% block title %}Site{% endblock %}</title> </head>
  <body>
    <header><a href="/">Home</a></header>
    <main>{% block content %}{% endblock %}</main>
  </body>
</html>

# templates/home.html
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
  {% for post in posts %}
    <article>
      <h2><a href="{% url 'post-detail' slug=post.slug %}">{{ post.title }}</a></h2>
      <p>{{ post.created|date:"Y-m-d" }} by {{ post.author }}</p>
    </article>
  {% empty %}
    <p>No posts yet.</p>
  {% endfor %}
  {% if is_paginated %}
    {% if page_obj.has_previous %}<a href="?page={{ page_obj.previous_page_number }}">Prev</a>{% endif %}
    Page {{ page_obj.number }} / {{ paginator.num_pages }}
    {% if page_obj.has_next %}<a href="?page={{ page_obj.next_page_number }}">Next</a>{% endif %}
  {% endif %}
{% endblock %}
Forms

Forms & Validation

# core/forms.py
from django import forms
from .models import Post

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

    def clean_title(self):
        t = self.cleaned_data["title"]
        if len(t) < 3:
            raise forms.ValidationError("Title too short")
        return t
# core/views.py (CreateView)
from django.views.generic import CreateView
from django.urls import reverse_lazy
from .forms import PostForm

class PostCreate(CreateView):
    form_class = PostForm
    template_name = "post_form.html"
    success_url = reverse_lazy("home")
Prefer ModelForms for CRUD. Use crispy‑forms or your CSS framework for styling. Validate user input on both client and server.
Security

Auth: Users, Permissions

# URLs: login/logout
from django.urls import path
from django.contrib.auth import views as auth_views
urlpatterns = [
  path("login/", auth_views.LoginView.as_view(template_name="login.html"), name="login"),
  path("logout/", auth_views.LogoutView.as_view(), name="logout"),
]
# Require login on CBV
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
class DashboardView(LoginRequiredMixin, TemplateView):
    template_name = "dashboard.html"

class ManagePosts(PermissionRequiredMixin, ListView):
    permission_required = "core.change_post"
    model = Post
Consider a custom user model from day one (AUTH_USER_MODEL). Use groups/permissions for role‑based access. Always enable CSRF protection.
API

Django REST Framework (DRF)

# Install
pip install djangorestframework

# settings.py
INSTALLED_APPS = ["rest_framework", "core", ...]

# core/api.py
from rest_framework import serializers, viewsets, routers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta: model = Post; fields = ["id","title","slug","body","author","created","published"]

class PostViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Post.objects.filter(published=True)
    serializer_class = PostSerializer

# config/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from core.api import PostViewSet
router = DefaultRouter(); router.register("posts", PostViewSet)
urlpatterns = [ path("api/", include(router.urls)) ]
Add pagination, throttling, and authentication (Token/JWT) for production APIs. Use select_related in viewsets to reduce queries.
Hooks

Custom Middleware

# core/middleware.py
import time
class TimingMiddleware:
    def __init__(self, get_response): self.get_response = get_response
    def __call__(self, request):
        t0 = time.time()
        resp = self.get_response(request)
        resp["X-Render-Time"] = f"{(time.time()-t0)*1000:.1f}ms"
        return resp

# settings.py
MIDDLEWARE += ["core.middleware.TimingMiddleware"]
Config

Settings, Environments, .env

# settings.py (snippet)
import os
from pathlib import Path
import dj_database_url
from dotenv import load_dotenv
load_dotenv()

BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY","change-me")
DEBUG = os.getenv("DEBUG","0") == "1"
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS","localhost,127.0.0.1").split(",")

DATABASES = {"default": dj_database_url.parse(os.getenv("DATABASE_URL","sqlite:///db.sqlite3"))}

STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

CSRF_TRUSTED_ORIGINS = os.getenv("CSRF_TRUSTED","").split(",") if os.getenv("CSRF_TRUSTED") else []
Never commit secrets. Use environment variables or a secrets manager. Set DEBUG=0 in production and configure ALLOWED_HOSTS.
Assets

Static & Media Files

# Development
python manage.py collectstatic  # for production

# URL config
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [...] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
In production, serve static via WhiteNoise or a CDN; media via object storage (S3, GCS) using django‑storages.
Quality

Testing & Factories

# Install
pip install pytest pytest-django model-bakery

# pytest.ini
[pytest]
DJANGO_SETTINGS_MODULE = config.settings
python_files = tests.py test_*.py

# tests/test_post.py
import pytest
from model_bakery import baker
from django.urls import reverse

@pytest.mark.django_db
def test_home_lists_posts(client):
    baker.make("core.Post", published=True, _quantity=3)
    resp = client.get(reverse("home"))
    assert resp.status_code == 200
    assert "posts" in resp.context
Use pytest‑django and factories (model‑bakery/factory_boy). Keep tests fast and isolated; use TransactionTestCase for DB‑level behavior when needed.
Ship

Deployment (Gunicorn/Nginx)

# Gunicorn
pip install gunicorn
gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3

# Systemd service (snippet)
[Service]
User=www-data
WorkingDirectory=/srv/app
ExecStart=/srv/app/.venv/bin/gunicorn config.wsgi:application -b 127.0.0.1:8000 -w 3
Environment="DJANGO_SECRET_KEY=..."
Environment="DATABASE_URL=postgres://..."
Restart=always
Put Nginx in front for TLS and static caching. Use Postgres in production, Redis for caching/sessions, and run migrations on deploy.
Fix

Troubleshooting & FAQ

Static files 404 in production

Run collectstatic; configure STATIC_ROOT; serve via WhiteNoise/CDN and correct URL paths.

CSRF verification failed

Ensure CSRF cookie is sent; add domain to CSRF_TRUSTED_ORIGINS; include {% csrf_token %} in forms.

N+1 query performance

Add select_related/prefetch_related; use Django Debug Toolbar in dev to spot issues.

DisallowedHost

Add your domain to ALLOWED_HOSTS; check reverse proxy Host header.

Media uploads not saving

Verify MEDIA_ROOT permissions and model FileField upload_to; ensure request.FILES used in form.

Migrations conflicts

Rebase/merge carefully; run makemigrations per app; resolve conflicting dependencies then migrate.

FAQ

Custom user model—when? Start with a custom user immediately if you expect to extend fields later; changing mid‑project is painful.

Function‑ vs class‑based views? CBVs speed up CRUD and list/detail patterns; FBVs for bespoke flows or when clarity matters more than reuse.

Monolith vs multiple apps? Keep a single project, multiple small apps by domain (accounts, blog, billing). Aim for loose coupling and clear boundaries.

If this page helped, consider linking it under “Python Web,” “Django Recipes,” or “Backend Engineering” resources. Sharing supports more free content like this.

© 2025 Pythoneo · Designed to be highly linkable: comprehensive, evergreen, copy‑paste friendly, with testing and deployment built‑in.