- Как настроить раздачу статических файлов в FastAPI
- Как создать утилиту setup_static
- Как организовать структуру директории static
- Как использовать фильтр static в шаблонах
Зачем нужна статика
Статические файлы — это файлы, которые не меняются в процессе работы приложения:
- CSS — стили оформления сайта
- JavaScript — интерактивность на страницах
- Изображения — логотипы, иконки, фоны
FastAPI не умеет раздавать статику по умолчанию — это нужно настраивать отдельно через StaticFiles.
Структура проекта
В проекте есть следующая структура файлов:
pytexpert.ru/
├── app/
│ ├── core/
│ │ ├── base/
│ │ │ ├── base.css
│ │ │ ├── base.js
│ │ │ └── images/
│ │ │ └── logo.png
│ │ └── (другие модули)
│ ├── static/
│ │ └── (синхронизируется из core/)
│ ├── utils/
│ │ ├── cors.py
│ │ ├── log.py
│ │ ├── routes.py
│ │ └── static.py ← создаём сейчас
│ └── main.py
├── .env
├── .env_example
├── requirements.txt
└── .gitignore
Статические файлы хранятся в app/core/ и синхронизируются в app/static/ при запуске приложения. Синхронизация копирует файлы из app/core/ в app/static/, сохраняя структуру папок.
Например:
app/core/base/base.css→app/static/core/base/base.cssapp/core/base/images/logo.png→app/static/core/base/images/logo.png
Создание утилиты static.py
Создадим файл app/utils/static.py:
import shutil
import time
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse
STATIC_VERSION = str(int(time.time()))
APP_DIR = Path(__file__).parent.parent
CORE_DIR = APP_DIR / "core"
STATIC_DIR = APP_DIR / "static"
DOWNLOAD_DIR = APP_DIR.parent / "download"
def get_static_version() -> str:
return STATIC_VERSION
def sync_static():
"""Синхронизация статических файлов из core/ в static/"""
extensions = {'.css', '.js', '.woff2', '.woff', '.ttf', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.map'}
if not CORE_DIR.exists():
return 0, 0
copied = 0
skipped = 0
for filepath in CORE_DIR.rglob('*'):
if not filepath.is_file():
continue
if filepath.suffix not in extensions:
continue
rel_path = filepath.relative_to(APP_DIR)
dest_path = STATIC_DIR / rel_path
if dest_path.exists() and filepath.stat().st_mtime <= dest_path.stat().st_mtime:
skipped += 1
continue
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(filepath, dest_path)
copied += 1
return copied, skipped
def setup_static(app: FastAPI) -> None:
"""Настройка раздачи статических файлов"""
STATIC_DIR.mkdir(parents=True, exist_ok=True)
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
sync_static()
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
app.mount("/download", StaticFiles(directory=str(DOWNLOAD_DIR)), name="download")
@app.get("/favicon.ico", include_in_schema=False)
async def favicon_redirect():
return RedirectResponse(url="/static/core/base/images/favicon.ico")
Разбор кода static.py
- sync_static() — копирует файлы из
app/core/вapp/static/. Копируются только файлы с нужными расширениями. - STATIC_VERSION — версия статики на основе времени запуска. Используется для обновления кеша браузера.
- app.mount() — монтирует директорию по указанному URL. Теперь файлы из
app/static/доступны по адресу/static/... - /favicon.ico — редирект на реальный файл фавикона.
app/static/core/base/base.css будет доступен по URL /static/core/base/base.css.
Подключение к main.py
Обновим app/main.py, чтобы подключить статику:
from fastapi import FastAPI
from .config import settings
from .utils.cors import setup_cors
from .utils.lifespan import get_lifespan
from .utils.routes import setup_routes
from .utils.static import setup_static
# Создаём приложение
app = FastAPI(
title=settings.APP_NAME,
description=settings.APP_DESCRIPTION,
version=settings.APP_VERSION,
lifespan=get_lifespan()
)
# Настройки
setup_cors(app) # из статьи про CORS
setup_static(app) # статика
setup_routes(app) # из статьи про роутинг
# Запуск
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host=settings.APP_HOST,
port=settings.APP_PORT
)
Разбор обновлённого main.py
- setup_cors(app) — настройка CORS
- setup_static(app) — подключаем раздачу статических файлов
- setup_routes(app) — подключаем роутеры
- lifespan — управление жизненным циклом с логированием
Фильтр static в шаблонах
В шаблонах мы используем фильтр |static для генерации путей к статическим файлам.
Пример из базового шаблона app/core/base/base.html:
<link rel="stylesheet" href="/static/core/base/base.css?v=1789232230">
<script src="/static/core/base/base.js?v=1789232230"></script>
Фильтр static преобразует путь core/base/base.css в полный URL /static/core/base/base.css.
Фильтр определён в app/core/base/route.py:
def static_url(path: str) -> str:
return f"/static/{path}"
templates.env.globals["static"] = static_url
Проверка работы
Запустите сервер:
uvicorn app.main:app --reload
Теперь проверьте доступность статических файлов в браузере:
http://127.0.0.1:8000/static/core/base/base.css— должен открыться CSS-файлhttp://127.0.0.1:8000/static/core/base/images/logo.png— должно отобразиться изображение
- Запустите сервер:
uvicorn app.main:app --reload - Перейдите по адресу
/static/core/base/base.css— должен открыться CSS - Перейдите по адресу
/static/core/base/images/logo.png— должно открыться изображение
Итог
Сегодня мы сделали:
- ✅ Создали
app/utils/static.py— настройку раздачи статики с синхронизацией - ✅ Подключили статику в
app/main.py - ✅ Узнали, как работает фильтр
|staticв шаблонах - ✅ Проверили работу статики в браузере
Теперь ваше приложение умеет отдавать статические файлы. Это основа для стилизации сайта — в следующих статьях мы создадим базовый шаблон и главную страницу.
После проверки закоммитьте и запушьте изменения в GitHub:
git add .
git commit -m "Добавлена раздача статических файлов"
git push
Ищете хостинг для размещения вашего проекта в сети Интернет?
Посмотрите здесь →
Реклама. ООО «Бегет» ИНН 7801451618 erid: 2VtzqxQSRkE