Перейти к содержанию

CRUD Auto — автогенерация REST API

Каждая зарегистрированная модель автоматически получает полный набор REST-эндпоинтов. Ноль строк кода — только определение модели.

Генерируемые эндпоинты

Для модели с __route__ = "products". Все авто-роуты монтируются под общим префиксом /auto (исключает конфликт с кастомными роутерами).

Метод URL Описание
POST /auto/products/search Поиск с фильтрами и пагинацией
POST /auto/products Создание записи
POST /auto/products/{id} Чтение записи по ID (тело: {fields:[...]})
PUT /auto/products/{id} Обновление записи
PUT /auto/products/bulk Массовое обновление ({ids, values})
DELETE /auto/products/{id} Удаление записи
DELETE /auto/products/bulk Массовое удаление (тело: [ids])
GET /auto/products/fields Список полей модели
GET /auto/products/search_many2many Чтение M2M-связей записи
POST /auto/products/default_values Значения по умолчанию

Безопасность фильтров

Имена полей в filter и параметр sort в search_many2many валидируются по реальным (не private) полям модели. Неизвестное или приватное поле → 400 #FIELDS_NOT_FOUND; идентификаторы дополнительно экранируются в SQL-билдере. Схемы create/update не содержат private-полей (password_hash и т.п.).

Как это работает

graph LR
    M[DotModel] --> SR[SchemaRegistry]
    SR --> PS[Pydantic Schemas]
    PS --> CR[CRUDRouterGenerator]
    CR --> R[FastAPI Routers]

    style M fill:#e3f2fd
    style R fill:#e8f5e9
  1. SchemaRegistry анализирует все поля модели
  2. Генерирует Pydantic-схемы для валидации (create, update, read, search)
  3. CRUDRouterGenerator создаёт FastAPI-роутеры с этими схемами
  4. Роутеры подключаются к приложению при старте

Search API

POST /auto/products/search
Content-Type: application/json

{
    "filter": [
        ["active", "=", true],
        ["price", ">", 100]
    ],
    "fields": ["id", "name", "price", "category_id"],
    "order": "price",
    "sort": "desc",
    "start": 0,
    "end": 20,
    "limit": 20
}

Ответ:

{
    "data": [
        {"id": 5, "name": "Widget Pro", "price": 299, "category_id": 2},
        {"id": 3, "name": "Widget Basic", "price": 199, "category_id": 1}
    ],
    "total": 42
}

Create API

POST /auto/products
Content-Type: application/json

{
    "name": "New Product",
    "price": 199.99,
    "category_id": 1,
    "active": true
}

Ответ:

{
    "data": {"id": 43}
}

Read API

POST /auto/products/43
Content-Type: application/json

{
    "fields": ["id", "name", "price", "category_id"]
}

Ответ:

{
    "data": {
        "id": 43,
        "name": "New Product",
        "price": 199.99,
        "category_id": {"id": 1, "name": "Electronics"}
    }
}

Update API

PUT /auto/products/43
Content-Type: application/json

{
    "price": 249.99,
    "name": "Updated Product"
}

Кастомные роутеры

Для модулей, которым нужна дополнительная логика, создавай обычные FastAPI-роутеры:

backend/base/crm/chat/routers/messages.py
from fastapi import APIRouter, Request

router_private = APIRouter(
    prefix="/chats",
    tags=["Chat Messages"],
)

@router_private.post("/{chat_id}/messages")
async def send_message(req: Request, chat_id: int, body: MessageCreate):
    """Кастомный эндпоинт с бизнес-логикой."""
    env = req.app.state.env

    # Проверка прав
    member = await ChatMember.check_membership(chat_id, user_id)
    ChatMember.check_permissions(member, ChatPermissions(can_write=True))

    # Создание через ORM
    msg_id = await env.models.chat_message.create(
        env.models.chat_message(
            chat_id=chat_id,
            author_user_id=user_id,
            body=body.body,
        )
    )

    # WebSocket уведомление
    await env.apps.chat.chat_manager.send_to_chat(chat_id, {...})

    return {"data": {"id": msg_id}}

CRUD Auto + кастомные роутеры

Модель может одновременно иметь авто-CRUD роуты и кастомные роутеры. Авто-CRUD покрывает базовые операции, а кастомные — бизнес-логику (чат, транзакции, уведомления).