Автоматическая генерация OpenAPI спецификаций в Python

Раздел: Разработка на Python -> API

Введение в OpenAPI (Swagger) в Python

Какое решение для OpenAPI является наиболее эффективным и современным?

FastAPI - современный веб-фреймворк, который из коробки генерирует полноценную OpenAPI спецификацию на основе Pydantic моделей и объявленных эндпоинтов. Это самый быстрый и простой способ получить документированное API.

Пример простого приложения:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="Пример API", version="1.0.0")

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = False

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

@app.post("/items/", response_model=Item)
def create_item(item: Item):
    return item

Telegram file python (работа с файлами telegram в python)

После запуска (uvicorn main:app --reload) эндпоинты /docs (Swagger UI) и /redoc (Redoc) содержат полную документацию. Модели Pydantic автоматически преобразуются в схемы OpenAPI.

Типичная ошибка: забыть указать response_model - тогда ответ не будет документирован. Если для одного эндпоинта нужны разные коды ответов, используется параметр responses:

@app.get("/error", responses={404: {"model": Item, "description": "Item not found"}})
def read_error():
    ...

компас python api (api компас 3d для python)

Вопрос: Как настроить метаинформацию OpenAPI?

FastAPI позволяет задать openapi_tags, description, version и другие поля через конструктор FastAPI(). Для добавления дополнительных OpenAPI полей используется openapi_extra.

Как реализовать OpenAPI в Flask?

Для Flask существует несколько библиотек. Рассмотрим flask-smorest - одну из наиболее поддерживаемых. Она работает на основе Marshmallow схем.

from flask import Flask
from flask.views import MethodView
from flask_smorest import Api, Blueprint
from marshmallow import Schema, fields

app = Flask(__name__)
app.config["API_TITLE"] = "Flask API"
app.config["API_VERSION"] = "v1"
api = Api(app)

blp = Blueprint("items", __name__, description="Товары")

class ItemSchema(Schema):
    id = fields.Int(dump_only=True)
    name = fields.Str(required=True)
    price = fields.Float()

@blp.route("/items")
class Items(MethodView):
    @blp.response(ItemSchema(many=True))
    def get(self):
        return [{"id": 1, "name": "Ноутбук", "price": 1000.0}]

    @blp.arguments(ItemSchema)
    @blp.response(ItemSchema, code=201)
    def post(self, new_item):
        return new_item

api.register_blueprint(blp)

if __name__ == "__main__":
    app.run()

Open api python (openapi (swagger) в python)

В результате по адресу /swagger-ui появится документация. Файл openapi.json доступен по /openapi.json.

Ошибка: при отсутствии blp.arguments или неправильном порядке декораторов документация может не сгенерироваться. Следует соблюдать порядок: arguments, затем response.

Как интегрировать OpenAPI в Django REST Framework?

Лучший выбор - drf-spectacular. Он поддерживает все возможности DRF и генерирует корректную OpenAPI 3.0 спецификацию.

# settings.py
INSTALLED_APPS = [
    ...
    'drf_spectacular',
]

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

SPECTACULAR_SETTINGS = {
    'TITLE': 'Django API',
    'VERSION': '1.0.0',
}

проверка ключа python (проверка api ключа в python)

# views.py
from rest_framework import viewsets
from .models import Item
from .serializers import ItemSerializer
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample

@extend_schema(tags=['items'])
class ItemViewSet(viewsets.ModelViewSet):
    queryset = Item.objects.all()
    serializer_class = ItemSerializer

    @extend_schema(
        summary="Получить список товаров",
        parameters=[
            OpenApiParameter(name='search', type=str),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

Добавив path('api/schema/', SpectacularAPIView.as_view(), name='schema') в urls, получаем /api/schema/. Swagger UI подключается через SpectacularSwaggerView.

Проблема: для вьюх, не наследующих от стандартных представлений DRF, нужно вручную аннотировать параметры с помощью @extend_schema. Иначе документация может быть неполной.

Как создать OpenAPI спецификацию вручную с помощью apispec?

Если требуется полный контроль, используется библиотека apispec. Она позволяет описать пути и схемы вручную.

from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from marshmallow import Schema, fields

spec = APISpec(
    title="Manual API",
    version="1.0.0",
    openapi_version="3.0.2",
    plugins=[MarshmallowPlugin()],
)

class Item(Schema):
    id = fields.Int()
    name = fields.Str()

spec.components.schema("Item", schema=Item)

spec.path(
    path="/items",
    operations=dict(
        get=dict(
            summary="Get items",
            responses={"200": {"content": {"application/json": {"schema": "Item"}}}}
        )
    )
)

# Сохранение в файл
import json
with open("openapi.json", "w") as f:
    json.dump(spec.to_dict(), f, indent=2)

Ошибка: несоответствие структуры OpenAPI. Рекомендуется сверить сгенерированный JSON с помощью валидатора (например, prance).

Расширенные примеры работы с OpenAPI в Python

Кастомизация метаданных и группировка эндпоинтов в FastAPI

Пример
from fastapi import FastAPI, APIRouter
from pydantic import BaseModel

app = FastAPI(
    title="Сложное API",
    version="2.0.0",
    description="API с дополнительными метаданными",
    openapi_tags=[
        {"name": "users", "description": "Операции с пользователями"},
        {"name": "items", "description": "Управление товарами"},
    ]
)

router_items = APIRouter(tags=["items"])

class ItemCreate(BaseModel):
    name: str
    price: float

@router_items.post("/items", response_model=ItemCreate, summary="Создать товар")
def create_item(item: ItemCreate):
    return item

app.include_router(router_items)

# Добавление произвольного поля в OpenAPI
app.openapi_extra = {
    "x-logo": {
        "url": "https://example.com/logo.png"
    }
}

Результат: в /openapi.json появится секция x-logo, а теги объединят эндпоинты. Swagger UI отобразит группы.

Добавление схемы безопасности (Bearer Token) в FastAPI

Пример
from fastapi.security import HTTPBearer
from fastapi import FastAPI, Depends

app = FastAPI()
security = HTTPBearer()

@app.get("/secure", dependencies=[Depends(security)])
def secure_endpoint():
    return {"message": "Доступ разрешён"}

# Автоматически в OpenAPI добавится компонент securitySchemes с bearerAuth
// Фрагмент openapi.json
"components": {
  "securitySchemes": {
    "HTTPBearer": {
      "type": "http",
      "scheme": "bearer"
    }
  }
},
"security": [{"HTTPBearer": []}]

Генерация клиентского кода с помощью openapi-generator

После получения openapi.json можно сгенерировать клиент на любом языке.

Пример
# Установка генератора
npm install @openapitools/openapi-generator-cli -g

# Генерация Python клиента
openapi-generator-cli generate -i openapi.json -g python -o ./python-client

# Генерация TypeScript клиента
openapi-generator-cli generate -i openapi.json -g typescript-axios -o ./ts-client

В полученном клиенте все эндпоинты и модели уже типизированы. Это ускоряет разработку и снижает количество ошибок.

Тестирование API с использованием OpenAPI схемы

Можно проверять ответы на соответствие схеме с помощью библиотеки jsonschema.

Пример
import json
import requests
from jsonschema import validate, ValidationError

# Загрузка схемы
with open("openapi.json") as f:
    spec = json.load(f)

# Получение схемы для ответа 200 для метода GET /items
schema = spec["paths"]["/items"]["get"]["responses"]["200"]["content"]["application/json"]["schema"]

# Запрос к API
response = requests.get("http://localhost:8000/items")
try:
    validate(instance=response.json(), schema=schema)
    print("Ответ соответствует OpenAPI схеме")
except ValidationError as e:
    print(f"Ошибка валидации: {e.message}")

Генерация OpenAPI схемы из существующих данных (на примере Flask + apispec)

Пример
from flask import Flask, jsonify
from apispec import APISpec
from apispec.ext.flask import FlaskPlugin
from apispec_webframeworks.flask import FlaskPlugin as FlaskPlugin2  # альтернатива

app = Flask(__name__)

@app.route("/api/data")
def get_data():
    return jsonify([{"id": 1, "value": "test"}])

spec = APISpec(title="Dynamic API", version="1.0.0", openapi_version="3.0.2", plugins=[FlaskPlugin2()])
with app.test_request_context():
    spec.path(view=get_data)

import json
print(json.dumps(spec.to_dict(), indent=2))

Результат: будет сгенерирован путь /api/data с ответом 200, но без указания модели (так как нет схемы). Для полноты нужно добавить схему Marshmallow.

Работа с несколькими версиями API

FastAPI позволяет иметь несколько приложений с разными префиксами.

Пример
from fastapi import FastAPI

app_v1 = FastAPI(title="API v1", version="1.0.0")
app_v2 = FastAPI(title="API v2", version="2.0.0")

@app_v1.get("/status")
def status_v1():
    return {"version": "v1"}

@app_v2.get("/status")
def status_v2():
    return {"version": "v2"}

# Монтируем с разными префиксами
from fastapi import APIRouter
router = APIRouter()
router.mount("/v1", app_v1)
router.mount("/v2", app_v2)

# Итоговое приложение
app = FastAPI()
app.include_router(router)

# Документация каждой версии доступна по /v1/docs и /v2/docs

OpenAPI (Swagger) в Python - comments

En
Open api python (python)