# Django, Flask and FastAPI

> How midcode starts a Django, Flask or FastAPI site with the project's own Python, edits its templates on the canvas, and where pages and styles come from.

- Page: https://midcode.app/docs/frameworks/python
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt
- Status: This ships with the next release of midcode. The version you can download today (1.1.2) doesn't have it yet.

midcode opens a Django, Flask or FastAPI project, starts it with the project's own Python, and edits the HTML written in its templates: text, classes, attributes, new elements and their order. What the template language works out (`{{ post.title }}`, `{% for %}`, a form Django renders) is left to your code.

Python renders the page, so midcode finds each element in your templates after the page loads. [Sites a server renders](https://midcode.app/docs/frameworks/server-templates.md) explains how that works and what it means for editing.

## At a glance

| | Django, Flask and FastAPI |
| --- | --- |
| Detected by | `manage.py` (Django); a `Flask(…)` or `FastAPI(…)` app in a project that depends on that package |
| Runs with | `manage.py runserver`, `flask run --debug` or `uvicorn --reload`, with the project's own Python |
| Elements are marked by | midcode, after each page loads, by matching it against the project's templates |
| Editing | Text, classes, attributes and tag; insert, move, duplicate and remove |
| Pages | Django's `urls.py`, Flask's `@app.route`. FastAPI: type the path |
| Styles | Tailwind 4 classes, or `mid:` utilities with `midcode.css` in the static folder (Django, Flask) |
| Tried with | Django with its `.venv`, Flask, and FastAPI with Jinja. Every element written in the templates was found |

## How midcode runs it

| Stack | Recognised by | Command |
| --- | --- | --- |
| Django | `manage.py` at the top of the folder | `<python> manage.py runserver 127.0.0.1:$PORT` |
| Flask | The project depends on `flask`, and a file makes the app: `app = Flask(__name__)` | `<python> -m flask --app <module> run --debug --host 127.0.0.1 --port $PORT` |
| FastAPI | The project depends on `fastapi`, and a file makes the app: `app = FastAPI()` | `<python> -m uvicorn <module>:<name> --reload --host 127.0.0.1 --port $PORT` |

`$PORT` is a free port midcode picks. The command runs in your login shell, in the project's folder, and what it prints is in the **Dev server log** in the top bar. Nothing in the project is changed to run it.

"Depends on" means the package is named in `requirements.txt`, `requirements/base.txt`, `requirements/dev.txt`, `pyproject.toml`, `Pipfile` or `setup.py`. For Flask, an `app.py` that imports it is enough.

The app is looked for in the `.py` files at the top of the project (`app.py`, `main.py`, `wsgi.py`, `asgi.py`, `application.py`, `run.py` and `server.py` first) and one folder in (`<folder>/__init__.py`, `app.py`, `main.py`). For Flask, a `create_app()` or `make_app()` factory counts too. For FastAPI the app has to be assigned to a name.

`--debug` makes Flask read your templates and code again when they change. `--reload` restarts Uvicorn when your code changes.

### Which Python

midcode uses the first of these it finds:

1. The project's virtual environment: `.venv/bin/python`, `venv/bin/python` or `env/bin/python`.
2. `uv run python`, when there's a `uv.lock`.
3. `poetry run python`, when there's a `poetry.lock`.
4. `pipenv run python`, when there's a `Pipfile`.
5. `python3` from your shell.

So a Django project with a `.venv` runs as `.venv/bin/python manage.py runserver 127.0.0.1:$PORT`.

### What you need

Python and the project's packages installed, and the site in a state where it runs (its database migrated, its settings in place). midcode doesn't run `pip install` or `migrate` on a project it opens.

[New project](https://midcode.app/docs/start/new-project.md) makes a Django or a Flask site with its own `.venv`, a `templates/` and a `static/` folder.

## What you can edit

Anything written as HTML in a template. In Django's and Jinja's templates, `{{ }}` and `{% %}` are never touched, and `{# #}` is a comment.

```html title="templates/home.html"
{% extends "base.html" %}
{% block content %}
<section class="hero">
  <h1>Fresh bread, every morning</h1>
  {% include "partials/hours.html" %}
  {% for loaf in loaves %}
  <article class="loaf"><h2>{{ loaf.name }}</h2></article>
  {% endfor %}
</section>
{% endblock %}
```

- The heading is written here: double-click it on the canvas to change it.
- What `base.html` and `partials/hours.html` write is edited in those files. The right panel shows which file and line an element comes from, under **Code**.
- The `<article>` is one line shown once per loaf: a class you add applies to all of them. The loaf's name is the template's to print.

```diff title="templates/home.html"
-<section class="hero">
-  <h1>Fresh bread, every morning</h1>
+<section class="hero mid:p-6">
+  <h1>Bread, pastries and coffee</h1>
```

After an edit midcode reloads the page in every breakpoint.

## Pages

**Pages** in the top bar is filled from your routes, read as text (nothing is run):

- **Django**: the `ROOT_URLCONF` of the settings module that `manage.py` names. Every `path("about/", …)` is a page. `include("blog.urls")` is followed, under its prefix, up to three levels. The admin and Django's own includes are left out. `<int:id>` makes a dynamic page, shown as `[id]`.
- **Flask**: every `@app.route("/about")` or `@bp.route(…)` in the project's `.py` files, up to three folders deep. A route that only takes `POST` isn't a page. `<name>` and `<int:id>` are dynamic.
- **FastAPI**: not read. Type a path in **Pages** and press `Enter`.

Where it can, midcode also knows the template behind a page: `TemplateView.as_view(template_name="home.html")` or the `render(request, "home.html")` of the view a Django route names, and the `render_template("home.html")` of a Flask view. Templates are looked for in `templates/` and in `<app>/templates/`.

A dynamic page finds its real addresses from the running site's sitemap and links ([Pages and navigation](https://midcode.app/docs/editor/pages.md)).

midcode doesn't add pages here: in Django, Flask and FastAPI a page is a route and a view written in Python, yours or your agent's to write.

## Site settings

The globe in the top bar opens [Site settings](https://midcode.app/docs/editor/site-settings.md). The title, the description, the language, search engines, the favicon and the social image are written as tags in the `<head>` of a template: the first one that closes a `<head>`, in alphabetical order of its path (often `templates/base.html`).

What the template computes there (`<title>{% block title %}Bakery{% endblock %}</title>`) shows as code and isn't written. Images are copied to the same folder as `midcode.css` (the table below). In a FastAPI project, or a Django one without `STATICFILES_DIRS`, midcode has no such folder (unless the project has a `public/` one) and asks you to put the image where the site serves it and link it in the code. This wasn't tried on a Django, Flask or FastAPI site.

## Styles

With Tailwind 4 in the project, midcode writes Tailwind's classes. It starts only the web server: if a separate command compiles your CSS, keep it running yourself.

Without Tailwind, midcode writes `mid:` utilities and keeps their CSS in `midcode.css` ([Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md)). Where the file goes depends on the stack:

| Stack | `midcode.css` goes in | Linked as |
| --- | --- | --- |
| Flask | `static/` beside the app, or `<package>/static/` when the app is a package | `/static/midcode.css` |
| Django | The folder that `STATICFILES_DIRS` names in the settings file | Under `STATIC_URL`, for example `/static/midcode.css` |
| FastAPI | The top of the project, or `src/` when there is one | Not linked: add the `<link>` yourself |

On your first style edit, every template that closes a `<head>` gets the link, as one undoable step:

```diff title="templates/base.html"
     <link rel="stylesheet" href="{% static 'style.css' %}">
+    <link rel="stylesheet" href="/static/midcode.css">
   </head>
```

The link is a plain path, not `{% static %}`. If your production site serves static files from another address, change that line to `{% static 'midcode.css' %}`.

In Django, midcode reads `STATICFILES_DIRS` and `STATIC_URL` from the settings file that `manage.py` names. If that file doesn't set `STATICFILES_DIRS`, or the folder doesn't exist, midcode writes `midcode.css` at the top of the project and says it couldn't tell where to import it.

## Limits

- Text the template prints, including `{% translate "…" %}`, isn't editable on the canvas.
- HTML that Python builds (`{{ form.as_p }}`, a widget, the Django admin) isn't written in your templates, so it isn't found.
- Templates inside the virtual environment are never read.
- Django's `re_path()` and routes built in code aren't listed. A Flask blueprint's `url_prefix` isn't added to its routes.
- If the project's `package.json` lists Vite or another framework midcode knows, the folder opens as that kind of project and its templates aren't matched.
- A virtual environment is the path that was tried. `uv`, Poetry and Pipenv are chosen by their lockfile and weren't tried. Neither were settings split across several files.
- [The free canvas](https://midcode.app/docs/editor/free-canvas.md) is for Next.js projects.

## Troubleshooting

**"No module named django" (or flask, uvicorn) in the log.** midcode picked a Python that doesn't have the project's packages. Put the environment in `.venv` at the top of the project, or set the command yourself.

**midcode asks "How does this site run?".** It didn't find where the Flask or FastAPI app is made, for example because it's deeper than one folder in. Type the command and the address. `$PORT` is the port midcode picked. They're saved in `.midcode/server.json`:

```json title=".midcode/server.json"
{
  "command": "uv run uvicorn src.shop.main:app --reload --host 127.0.0.1 --port $PORT",
  "url": "http://127.0.0.1:$PORT"
}
```

**A Django error page about the database.** The site is running and needs its migrations. Run them in a terminal, then press **Reload breakpoints**.

**The site runs in Docker.** Leave the command empty and give midcode the address where it answers. The templates on your Mac are matched the same way ([Any other stack](https://midcode.app/docs/frameworks/custom-server.md)).
