Skip to content

Set up a Django project with uv

uv

Most Django setup guides still tell you to install Python and a virtual environment by hand before you reach runserver. This tutorial uses uv for the Python install and the virtual environment. Every manage.py command runs against a pinned interpreter.

Confirm uv is installed

If you do not already have uv, follow the installation guide. No separate Python install is required; uv will download an interpreter on first use.

Verify the install:

$ uv --version
uv 0.11.7

The exact version will vary. This tutorial was verified with uv 0.11.7 and uv 0.11.8.

Initialize the uv project

Create the project shell with uv init. Pin to Python 3.13 explicitly so the rest of the tutorial is reproducible regardless of what your system has installed.

uv init --python 3.13 mysite
cd mysite

uv init --python 3.13 writes the starter files for the project:

    • .python-version
    • README.md
    • main.py
    • pyproject.toml

Notice the .python-version file. uv reads it on every uv sync and uv run so the project stays on Python 3.13, even on machines where the system Python is something else.

Open pyproject.toml:

[project]
name = "mysite"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

Note

Without --python 3.13, uv writes whichever Python it finds newest on your machine into requires-python. On a fresh Mac with Python 3.14 installed, that produces requires-python = ">=3.14", which can later block uv python pin 3.13. Pinning at init time avoids that conflict.

Django will replace main.py with its own entry point shortly. Delete the placeholder so it does not collide:

rm main.py

Add Django as a dependency

uv add django

uv resolves a compatible Django version and creates .venv/ for the project. It then downloads Django with its two runtime dependencies and writes a uv.lock that pins every transitive package:

$ uv add django
Using CPython 3.13.13
Creating virtual environment at: .venv
Resolved 5 packages in 231ms
Downloading django (8.0MiB)
 Downloaded django
Prepared 3 packages in 621ms
Installed 3 packages in 88ms
 + asgiref==3.11.1
 + django==6.0.4
 + sqlparse==0.5.5

Notice the new .venv/ directory. That is where Django and its dependencies live. You never source .venv/bin/activate; every Django command in this tutorial runs through uv run, which uses that venv automatically. The exact Django version on your machine will be whatever is current on PyPI when you run the command.

pyproject.toml now records the dependency:

dependencies = [
    "django>=6.0.4",
]

Tip

Commit pyproject.toml and uv.lock to version control, and add .venv/ and db.sqlite3 to .gitignore. The lockfile guarantees teammates recreate the same environment with uv sync; the venv and the development database should not be shared.

Generate the Django project files

Django’s startproject command scaffolds the project layout. Run it through uv run so it executes against the project’s venv:

uv run django-admin startproject mysite_config .

The trailing . tells Django to put manage.py in the current directory instead of creating yet another nested folder. After running, the directory looks like this:

    • .python-version
    • README.md
    • manage.py
      • __init__.py
      • asgi.py
      • settings.py
      • urls.py
      • wsgi.py
    • pyproject.toml
    • uv.lock

Open manage.py. The first lines look like this:

manage.py
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite_config.settings')

manage.py is the project’s command-line entry point. It points Django at mysite_config.settings. You will run it as uv run python manage.py <command> from now on.

Apply the initial migrations

Django’s built-in apps use database tables. Run the initial migrations now so the default admin and authentication features are ready when you start the server.

uv run python manage.py migrate

Django prints an OK line for each migration as it runs:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  ...
  Applying sessions.0001_initial... OK

Notice the new db.sqlite3 file in the project root. Django’s default DATABASES setting in mysite_config/settings.py points at BASE_DIR / 'db.sqlite3', which keeps the SQLite file next to manage.py. It does not live inside .venv/, so a uv sync --reinstall (or even rm -rf .venv) leaves your data alone.

Switching to Postgres or MySQL later is a settings.py change plus uv add psycopg[binary] or uv add mysqlclient.

Run the development server

uv run python manage.py runserver

The server starts on port 8000:

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
Django version 6.0.4, using settings 'mysite_config.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Visit http://127.0.0.1:8000/ in a browser. Django’s “The install worked successfully! Congratulations!” rocket page confirms that uv and Django are wired up correctly. Press CONTROL-C to stop the server.

Note

If you see error: Failed to spawn: python instead, you almost certainly ran the command outside the project directory. uv run looks for pyproject.toml and uv.lock in the current directory tree; from anywhere else it has no project to use. cd back into mysite/ and try again.

Add your first app

A Django project is a collection of apps. Create one:

uv run python manage.py startapp polls

Django generates a polls/ directory with the app skeleton:

    • __init__.py
    • admin.py
    • apps.py
    • models.py
    • tests.py
    • views.py

Django does not auto-discover apps. Register polls in mysite_config/settings.py by adding it to INSTALLED_APPS:

mysite_config/settings.py
INSTALLED_APPS = [
    # ...existing built-in apps...
    'polls',
]

The string 'polls' matches the directory name and the name attribute in polls/apps.py. Once registered, Django loads the app and includes its migrations. Models appear in the admin site only after you register them in polls/admin.py.

Run uv run python manage.py check to confirm the project still passes its system check after the edit.

Reproduce the environment

A teammate cloning the repo runs one command:

uv sync

uv reads .python-version and uv.lock, downloads Python 3.13 if needed, creates .venv/, and installs the pinned versions of Django and its dependencies. From there they have the same uv run python manage.py runserver you do. To switch Python versions later, run uv python pin 3.14 (or whichever version you want); the new value must satisfy the requires-python bound in pyproject.toml. See How to change the Python version of a uv project for the full workflow.

Review the project structure

    • .python-version
    • README.md
    • db.sqlite3
    • manage.py
      • __init__.py
      • asgi.py
      • settings.py
      • urls.py
      • wsgi.py
      • __init__.py
      • admin.py
      • apps.py
      • models.py
      • tests.py
      • views.py
    • pyproject.toml
    • uv.lock

Next steps

Last updated on

Please submit corrections and feedback...