$ coolprojects/_
<django/>

My favorite Django 6 Setup

@mikael.eklund · Apr 22, 2026 · [django] · 7 min

My favorite Django 6 Setup

Requirements is just uv installed before:
https://docs.astral.sh/uv/getting-started/installation/#pypi

create a project folder and enter that.

Install Django

uv add django
uv run django-admin startproject core .
uv run manage.py startapp app
uv run manage.py migrate
uv run manage.py createsuperuser

core/settings.py

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "app", # Added this
]

mkdir templates
in core/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # 'DIRS': [], originally
        'DIRS': [BASE_DIR / 'templates'], # Changed to this
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

in core/urls.py

from django.contrib import admin
from django.urls import include, path  # Add include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("app.urls")),  # Added this
]

create app/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.index, name="home"),
]

add this to core/settings.py

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/6.0/howto/static-files/

STATIC_URL = "static/"
STATICFILES_DIRS = [BASE_DIR / "static"]  # Added this

Install Daisy UI including Tailwind css

https://daisyui.com/docs/install/django/

mkdir static
mkdir static/css
cd static/css
curl -sL daisyui.com/fast | bash
cd back to project root

Install Alpinejs

mkdir static/src
curl -o static/src/alpine.min.js https://cdn.jsdelivr.net/npm/alpinejs@3.15.11/dist/cdn.min.js

Install htmx

uv add django_htmx
in core/settings.py

# Application definition

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "app",
    "django_htmx",  # Added this
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "django_htmx.middleware.HtmxMiddleware",  # Added this
]

Install font awasome

https://fontawesome.com/download
mkdir static/webfonts
wget https://use.fontawesome.com/releases/v7.2.0/fontawesome-free-7.2.0-web.zip

unzip fontawesome-free-7.2.0-web.zip
cp fontawesome-free-7.2.0-web/css/all.min.css static/src/fa.min.css
cp -r fontawesome-free-7.2.0-web/webfonts/* static/webfonts/
rm -rf fontawesome-free-7.2.0-web fontawesome-free-7.2.0-web.zip

Install django-cotton

uv add django-cotton
“Add ‘django_cotton’, to your INSTALLED_APPS:”

Install django-tasks

uv add django-tasks-db

add “django_tasks_db”, to “INSTALLED_APPS”

add to core/settings.py

TASKS = {
    "default": {
        "BACKEND": "django_tasks_db.DatabaseBackend",
    }
}

Create view

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.views.decorators.http import require_POST
from django_tasks_db.models import DBTaskResult

from . import tasks


def _recent_tasks():
    return DBTaskResult.objects.all().order_by("-enqueued_at")[:8]


def index(request):
    return render(request, "index.html", {"tasks": _recent_tasks()})


@require_POST
def enqueue_report(request):
    label = (request.POST.get("label") or "Untitled report").strip()
    tasks.generate_report.enqueue(label)
    if request.htmx:
        return render(request, "index.html#task-list", {"tasks": _recent_tasks()})
    return HttpResponseRedirect(reverse("home"))


def task_list(request):
    return render(request, "index.html#task-list", {"tasks": _recent_tasks()})

add urls in addp/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="home"),
    path("tasks/enqueue/", views.enqueue_report, name="enqueue_report"),  # Added this
    path("tasks/", views.task_list, name="task_list"),  # Added this
]

add app/tasks.py

import random
import time

from django.tasks import task


@task()
def generate_report(label: str) -> dict:
    duration = random.uniform(2, 5)
    time.sleep(duration)
    return {"label": label, "duration_seconds": round(duration, 2)}

add templates/base.html

{% load static django_htmx %}
<!doctype html>
<html lang="en" data-theme="dim">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{% block title %}blogpost · Django 6 dashboard{% endblock %}</title>
  <link rel="stylesheet" href="{% static 'css/output.css' %}">
  <link rel="stylesheet" href="{% static 'src/fa.min.css' %}">
</head>
<body class="min-h-screen bg-base-200 text-base-content">

  <div class="navbar bg-base-100 shadow-sm sticky top-0 z-30"
       x-data="{ theme: document.documentElement.dataset.theme }">
    <div class="flex-1">
      <a href="{% url 'home' %}" class="btn btn-ghost text-lg gap-2">
        <i class="fa-solid fa-feather-pointed text-primary"></i>
        blogpost
      </a>
    </div>
    <div class="flex-none gap-2">
      <span class="badge badge-outline hidden sm:inline-flex" x-text="`theme: ${theme}`"></span>
      <label class="swap swap-rotate btn btn-ghost btn-circle" title="Toggle theme">
        <input type="checkbox"
               :checked="theme === 'emerald'"
               @change="theme = theme === 'dim' ? 'emerald' : 'dim'; document.documentElement.dataset.theme = theme">
        <i class="swap-on fa-solid fa-sun"></i>
        <i class="swap-off fa-solid fa-moon"></i>
      </label>
    </div>
  </div>

  <main class="max-w-6xl mx-auto px-4 py-8 space-y-8">
    {% block content %}{% endblock %}
  </main>

  <script src="{% static 'src/alpine.min.js' %}" defer></script>
  {% htmx_script %}
  {% django_htmx_script %}
</body>
</html>

add templates/index.html

{% extends "base.html" %}
{% load static %}

{% block content %}

  <section class="hero bg-base-100 rounded-box shadow">
    <div class="hero-content flex-col lg:flex-row-reverse gap-10 py-10">
      <div class="text-6xl opacity-80">
        <i class="fa-brands fa-python text-primary"></i>
        <i class="fa-solid fa-arrow-right mx-2 text-base-content/30"></i>
        <i class="fa-solid fa-gauge-high text-secondary"></i>
      </div>
      <div class="max-w-xl">
        <div class="badge badge-primary badge-soft mb-3">Django 6.0 · Python 3.13</div>
        <h1 class="text-4xl font-bold leading-tight">What's new, live.</h1>
        <p class="py-4 opacity-80">
          A working tour of two headline features from Django 6 —
          <strong>native background Tasks</strong> and <strong>template partials</strong> —
          wired up to the frontend stack this project is built on.
        </p>
        <div class="flex gap-2 flex-wrap">
          <span class="badge badge-outline">uv</span>
          <span class="badge badge-outline">Tailwind v4</span>
          <span class="badge badge-outline">daisyUI</span>
          <span class="badge badge-outline">HTMX</span>
          <span class="badge badge-outline">Alpine.js</span>
          <span class="badge badge-outline">django-cotton</span>
          <span class="badge badge-outline">Font Awesome</span>
        </div>
      </div>
    </div>
  </section>

  <section class="card bg-base-100 shadow">
    <div class="card-body">
      <div class="flex items-start justify-between flex-wrap gap-4">
        <div class="max-w-2xl">
          <h2 class="card-title">
            <i class="fa-solid fa-list-check text-primary"></i>
            Background Tasks
            <span class="badge badge-success badge-soft">new in 6.0</span>
          </h2>
          <p class="opacity-70 mt-1">
            Enqueue a job from the browser — it lands in SQLite via
            <code class="kbd kbd-sm">django-tasks-db</code> and a worker picks it up.
            Watch the row flip from <span class="badge badge-ghost badge-sm">READY</span>
            to <span class="badge badge-warning badge-sm">RUNNING</span>
            to <span class="badge badge-success badge-sm">SUCCESSFUL</span>.
          </p>
          <p class="text-xs opacity-60 mt-2">
            <i class="fa-solid fa-circle-info"></i>
            Nothing will actually run until you start a worker:
            <code class="kbd kbd-sm">uv run python manage.py db_worker</code>
          </p>
        </div>
        <form hx-post="{% url 'enqueue_report' %}"
              hx-target="#task-list"
              hx-swap="outerHTML"
              class="join">
          {% csrf_token %}
          <input name="label"
                 placeholder="Report name"
                 class="input input-bordered join-item"
                 required>
          <button class="btn btn-primary join-item">
            <i class="fa-solid fa-paper-plane"></i> Enqueue
          </button>
        </form>
      </div>

      <div hx-get="{% url 'task_list' %}"
           hx-trigger="every 2s"
           hx-target="#task-list"
           hx-swap="outerHTML"
           class="text-xs opacity-60 mt-4 flex items-center gap-2">
        <span class="loading loading-dots loading-xs"></span>
        polling every 2s via HTMX — the server returns
        <code>index.html#task-list</code>, a template partial
      </div>

      {% partialdef task-list inline %}
      <div id="task-list" class="overflow-x-auto mt-2">
        <table class="table table-sm table-zebra">
          <thead>
            <tr>
              <th>Status</th>
              <th>Task</th>
              <th>Args</th>
              <th>Enqueued</th>
              <th>Finished</th>
              <th>Result</th>
            </tr>
          </thead>
          <tbody>
            {% for t in tasks %}
              <tr>
                <td>
                  {% if t.status == "SUCCESSFUL" %}
                    <span class="badge badge-success badge-sm">{{ t.status }}</span>
                  {% elif t.status == "FAILED" %}
                    <span class="badge badge-error badge-sm">{{ t.status }}</span>
                  {% elif t.status == "RUNNING" %}
                    <span class="badge badge-warning badge-sm">{{ t.status }}</span>
                  {% else %}
                    <span class="badge badge-ghost badge-sm">{{ t.status }}</span>
                  {% endif %}
                </td>
                <td class="font-mono text-xs">{{ t.task_name }}</td>
                <td class="font-mono text-xs opacity-70">{{ t.args_kwargs.args|join:", " }}</td>
                <td class="text-xs opacity-70">{{ t.enqueued_at|date:"H:i:s" }}</td>
                <td class="text-xs opacity-70">{{ t.finished_at|date:"H:i:s"|default:"—" }}</td>
                <td class="text-xs opacity-70">{{ t.return_value|default:"—" }}</td>
              </tr>
            {% empty %}
              <tr><td colspan="6" class="text-center opacity-60 py-6">No tasks yet — enqueue one above.</td></tr>
            {% endfor %}
          </tbody>
        </table>
      </div>
      {% endpartialdef %}
    </div>
  </section>

  <section class="card bg-base-100 shadow">
    <div class="card-body">
      <h2 class="card-title">
        <i class="fa-solid fa-puzzle-piece text-secondary"></i>
        Template partials
        <span class="badge badge-success badge-soft">new in 6.0</span>
      </h2>
      <p class="opacity-70">
        Reusable template fragments defined inline. Declare with
        <code class="kbd kbd-sm">{% verbatim %}{% partialdef name %}{% endverbatim %}</code>,
        then render from a view as <code class="kbd kbd-sm">"index.html#name"</code>.
        The task table above is the very partial being re-rendered on every HTMX poll —
        no second template file needed.
      </p>
      <div class="mockup-code text-xs">
<pre data-prefix="1"><code>{% verbatim %}{% partialdef task-list inline %}{% endverbatim %}</code></pre>
<pre data-prefix="2"><code>  &lt;table&gt;...&lt;/table&gt;</code></pre>
<pre data-prefix="3"><code>{% verbatim %}{% endpartialdef %}{% endverbatim %}</code></pre>
<pre data-prefix="·"><code></code></pre>
<pre data-prefix="$" class="text-success"><code># in the view</code></pre>
<pre data-prefix=">"><code>return render(request, "index.html#task-list", ctx)</code></pre>
      </div>
    </div>
  </section>

  <section>
    <h2 class="text-2xl font-bold mb-4 px-2 flex items-center gap-2">
      <i class="fa-solid fa-layer-group text-accent"></i> Frontend stack
    </h2>
    <div class="grid md:grid-cols-2 gap-4">

      <c-feature icon="fa-palette" icon-bg="bg-primary/15 text-primary" title="daisyUI" badge="Tailwind v4">
        Pre-built semantic components —
        <span class="kbd kbd-xs">card</span>,
        <span class="kbd kbd-xs">badge</span>,
        <span class="kbd kbd-xs">btn</span>,
        <span class="kbd kbd-xs">stats</span> —
        and themeable via <code>data-theme</code>. The navbar toggle flips between
        <code>dim</code> and <code>emerald</code> at runtime.
      </c-feature>

      <c-feature icon="fa-bolt" icon-bg="bg-secondary/15 text-secondary" title="HTMX" badge="hx-*">
        The form posts and the poller both live entirely in HTML attributes. The
        <code>django-htmx</code> middleware exposes <code>request.htmx</code> so views
        can return a partial on HTMX requests and a full page otherwise.
      </c-feature>

      <c-feature icon="fa-mountain" icon-bg="bg-accent/15 text-accent" title="Alpine.js" badge="x-data">
        Tiny client-side state for interactions that don't need the server. The theme
        toggle uses <code>x-data</code> + <code>@change</code> to rewrite
        <code>document.documentElement.dataset.theme</code>. Quick demo:
        <div class="mt-3" x-data="{ n: 0 }">
          <button class="btn btn-xs btn-accent" @click="n++">
            Clicked <span class="font-mono ml-1" x-text="n"></span> times
          </button>
        </div>
      </c-feature>

      <c-feature icon="fa-cubes" icon-bg="bg-info/15 text-info" title="django-cotton" badge="&lt;c-*&gt;">
        Every card in this grid is rendered by a single
        <code class="kbd kbd-sm">&lt;c-feature&gt;</code> component at
        <code>templates/cotton/feature.html</code>. Attributes become template variables
        (<code>icon</code>, <code>title</code>, <code>badge</code>) and children flow
        into <code>{% verbatim %}{{ slot }}{% endverbatim %}</code>.
      </c-feature>

      <c-feature icon="fa-icons" icon-bg="bg-warning/15 text-warning" title="Font Awesome" badge="self-hosted">
        Every icon on this page — the navbar feather, the task checkmark, the stack tiles
        themselves — is Font Awesome, served locally from
        <code>static/src/fa.min.css</code> + <code>static/webfonts/</code>, no CDN.
        All three families are available:
        <div class="flex items-center gap-4 mt-3 text-2xl">
          <span title="solid"><i class="fa-solid fa-rocket text-primary"></i></span>
          <span title="regular"><i class="fa-regular fa-heart text-secondary"></i></span>
          <span title="brands"><i class="fa-brands fa-github"></i></span>
          <span title="brands"><i class="fa-brands fa-python text-info"></i></span>
          <span title="brands"><i class="fa-brands fa-js text-warning"></i></span>
          <span title="solid"><i class="fa-solid fa-database text-success"></i></span>
        </div>
      </c-feature>

    </div>
  </section>

{% endblock %}

create template/cotton
create template/cotton/feature.html

<div class="card bg-base-100 shadow">
  <div class="card-body">
    <h3 class="card-title gap-3">
      <span class="{{ icon_bg|default:'bg-primary/15 text-primary' }} rounded-lg w-10 h-10 grid place-items-center shrink-0">
        <i class="fa-solid {{ icon }}"></i>
      </span>
      <span>{{ title }}</span>
      {% if badge %}<span class="badge badge-soft badge-sm">{{ badge }}</span>{% endif %}
    </h3>
    <div class="opacity-80 space-y-2 text-sm">{{ slot }}</div>
  </div>
</div>

uv run manage.py migrate

rebuild CSS on template edits
static/css/tailwindcss -i static/css/input.css -o static/css/output.css

start server
uv run manage.py runserver
in another terminal start task worker
uv run python manage.py db_worker