Set up a Django project with 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 mysiteuv 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.pyAdd Django as a dependency
uv add djangouv 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:
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 migrateDjango 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 runserverThe 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 pollsDjango 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:
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 syncuv 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
- The official Django tutorial builds the polls app out into a working web application. Run every command in it with
uv run python manage.py …instead of plainpython manage.py …. - Setting up testing with pytest and uv covers adding pytest as a dev dependency. Layer
pytest-djangoon top withuv add --dev pytest-djangoto test Django views and models. - Setting up GitHub Actions with uv shows how to run those tests in CI.
- How to use uv in a Dockerfile walks through containerizing a uv-based project for deployment.