architect/_archive/2025-11-cleanup/platform-v2-cifra/archive/2025-11-10-restructure-v2/CODE_GENERATION_FRAMEWORKS.md

Code Generation: От описания к коду

Дата: 2025-11-10
Версия: 1.0.0
Вопрос: Как из простых элементов (поля, entity) собрать классы и модули?


БЫСТРЫЙ ОТВЕТ

Да, существует множество подходов!

Теория:
- MDA (Model-Driven Architecture) - разработка на основе моделей
- DSL (Domain-Specific Languages) - языки для конкретных предметных областей
- Метапрограммирование - код, который генерирует код

Практика (готовые решения для Python):
1. Pydantic - модели данных из описания
2. dataclasses - классы из декораторов
3. SQLAlchemy - ORM модели
4. Jinja2 - генерация кода из шаблонов
5. AST - работа с синтаксическим деревом
6. Cookiecutter - генерация проектов
7. Yeoman/Plop (аналоги для Python)

Для платформы ЦИФРА:

# Описание entity
User:
  fields:
    - name: id, type: int, primary_key: true
    - name: email, type: str, unique: true
    - name: password, type: str

# Конструктор генерирует:
# ↓
# 1. SQLAlchemy модель
# 2. Pydantic схему
# 3. FastAPI endpoints
# 4. Alembic миграцию
# 5. Тесты

Часть 1: ТЕОРИЯ (Model-Driven Development)

1.1 MDA (Model-Driven Architecture)

Концепция: Описываешь ЧТО нужно (модель), система генерирует КАК (код).

┌─────────────────────────────────────────────────────────┐
│                    META-УРОВНИ                          │
├─────────────────────────────────────────────────────────┤
│  M3: Meta-Meta Model (MOF)                              │
│      "Язык для описания языков моделирования"           │
│      ↓                                                  │
│  M2: Meta Model (UML, Ecore)                            │
│      "Язык для описания моделей"                        │
│      ↓                                                  │
│  M1: Model (ваша схема)                                 │
│      "Описание сущностей: User, Product, Order"         │
│      ↓                                                  │
│  M0: Instance (код)                                     │
│      "Сгенерированный код: models.py, api.py"           │
└─────────────────────────────────────────────────────────┘

Пример:

M2 (Meta Model):
  Entity:
    - name: string
    - fields: Field[]

  Field:
    - name: string
    - type: DataType
    - constraints: Constraint[]

M1 (Model):
  Entity "User":
    - Field "id": int, primary_key
    - Field "email": string, unique
    - Field "password": string

M0 (Generated Code):
  class User(Base):
      __tablename__ = "users"
      id = Column(Integer, primary_key=True)
      email = Column(String, unique=True)
      password = Column(String)

1.2 DSL (Domain-Specific Language)

Концепция: Специализированный язык для конкретной предметной области.

Примеры DSL:

# SQL - DSL для баз данных
SELECT name, email FROM users WHERE id = 1

# HTML - DSL для разметки
<div class="user">
  <h1>{{ user.name }}</h1>
</div>

# Ваш DSL для описания entity:
entity User:
  id: int @primary_key
  email: string @unique @email
  password: string @min_length(8)
  created_at: datetime @auto_now_add

  index:
    - email
    - created_at

  api:
    - GET /users
    - POST /users
    - GET /users/{id}

Парсер DSL → Python код:

# Из DSL выше генерируется:

# 1. SQLAlchemy модель
class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    email = Column(String, unique=True, index=True)
    password = Column(String)
    created_at = Column(DateTime, server_default=func.now(), index=True)

# 2. Pydantic схема
class UserCreate(BaseModel):
    email: EmailStr
    password: str = Field(min_length=8)

# 3. FastAPI endpoints
@app.get("/users")
async def list_users(): ...

@app.post("/users")
async def create_user(user: UserCreate): ...

@app.get("/users/{id}")
async def get_user(id: int): ...

Часть 2: ПРАКТИКА (Готовые решения для Python)

2.1 Pydantic (Models from Schema)

Что это: Библиотека для создания моделей данных с автовалидацией.

pip install pydantic

Простой пример:

from pydantic import BaseModel, Field, EmailStr
from typing import Optional

# Описание модели
class User(BaseModel):
    id: int
    email: EmailStr
    name: str = Field(min_length=1, max_length=100)
    age: Optional[int] = Field(None, ge=0, le=150)

# Использование
user = User(
    id=1,
    email="john@example.com",
    name="John",
    age=30
)

print(user.dict())  # {'id': 1, 'email': 'john@example.com', ...}
print(user.json())  # '{"id":1,"email":"john@example.com",...}'

# Автовалидация
try:
    User(id=1, email="invalid", name="X")
except ValidationError as e:
    print(e)
    # email: value is not a valid email address
    # age: field required

Для конструктора:

from typing import Type
from pydantic import create_model

def create_entity_model(entity_name: str, fields: dict) -> Type[BaseModel]:
    """
    Создаёт Pydantic модель динамически.

    entity_name: "User"
    fields: {
        "id": (int, ...),
        "email": (str, ...),
        "age": (int, Field(ge=0, le=150))
    }
    """
    return create_model(entity_name, **fields)

# Использование
UserModel = create_entity_model("User", {
    "id": (int, ...),
    "email": (str, ...),
    "age": (int, Field(default=None, ge=0, le=150))
})

user = UserModel(id=1, email="test@example.com", age=25)
print(user)  # id=1 email='test@example.com' age=25

2.2 dataclasses (Built-in Python)

Что это: Встроенный декоратор для создания классов данных.

from dataclasses import dataclass, field
from typing import Optional

@dataclass
class User:
    id: int
    email: str
    name: str
    age: Optional[int] = None
    is_active: bool = True

# Автоматически создаются:
# - __init__()
# - __repr__()
# - __eq__()

user = User(id=1, email="john@example.com", name="John")
print(user)  # User(id=1, email='john@example.com', name='John', age=None, is_active=True)

Динамическое создание:

from dataclasses import make_dataclass

def create_dataclass_from_schema(name: str, fields: list):
    """
    fields = [
        ("id", int),
        ("email", str),
        ("age", int, field(default=0))
    ]
    """
    return make_dataclass(name, fields)

UserClass = create_dataclass_from_schema("User", [
    ("id", int),
    ("email", str),
    ("age", int, field(default=0))
])

user = UserClass(id=1, email="test@example.com")
print(user)  # User(id=1, email='test@example.com', age=0)

2.3 SQLAlchemy (Declarative ORM)

Что это: ORM с декларативным описанием моделей.

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

# Декларативное описание
class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    email = Column(String, unique=True)
    name = Column(String(100))

# Автоматически создаётся таблица
engine = create_engine("sqlite:///db.sqlite")
Base.metadata.create_all(engine)

Динамическое создание таблиц:

from sqlalchemy import Table, Column, Integer, String, MetaData

def create_table_from_schema(table_name: str, fields: dict):
    """
    fields = {
        "id": {"type": Integer, "primary_key": True},
        "email": {"type": String, "unique": True},
        "name": {"type": String(100)}
    }
    """
    metadata = MetaData()

    columns = []
    for field_name, field_config in fields.items():
        col = Column(
            field_name,
            field_config["type"],
            primary_key=field_config.get("primary_key", False),
            unique=field_config.get("unique", False)
        )
        columns.append(col)

    table = Table(table_name, metadata, *columns)
    return table

# Использование
users_table = create_table_from_schema("users", {
    "id": {"type": Integer, "primary_key": True},
    "email": {"type": String, "unique": True},
    "name": {"type": String(100)}
})

engine = create_engine("sqlite:///db.sqlite")
users_table.create(engine)

2.4 Jinja2 (Template Engine для генерации кода)

Что это: Шаблонизатор для генерации текста (в том числе кода).

pip install jinja2

Шаблон модели:

# templates/model.py.j2
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class {{ entity.name }}(Base):
    __tablename__ = "{{ entity.table_name }}"

    {% for field in entity.fields %}
    {{ field.name }} = Column(
        {{ field.type }},
        {% if field.primary_key %}primary_key=True,{% endif %}
        {% if field.unique %}unique=True,{% endif %}
        {% if field.nullable %}nullable=True{% else %}nullable=False{% endif %}
    )
    {% endfor %}

    def __repr__(self):
        return f"<{{ entity.name }}(id={self.id})>"

Генератор:

from jinja2 import Environment, FileSystemLoader

# Конфигурация entity
entity_config = {
    "name": "User",
    "table_name": "users",
    "fields": [
        {"name": "id", "type": "Integer", "primary_key": True, "nullable": False},
        {"name": "email", "type": "String", "unique": True, "nullable": False},
        {"name": "name", "type": "String(100)", "nullable": False},
        {"name": "age", "type": "Integer", "nullable": True}
    ]
}

# Загрузка шаблона
env = Environment(loader=FileSystemLoader("templates"))
template = env.get_template("model.py.j2")

# Генерация кода
generated_code = template.render(entity=entity_config)

# Сохранение
with open("generated/models.py", "w") as f:
    f.write(generated_code)

print(generated_code)

Результат (generated/models.py):

from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(
        Integer,
        primary_key=True,
        nullable=False
    )

    email = Column(
        String,
        unique=True,
        nullable=False
    )

    name = Column(
        String(100),
        nullable=False
    )

    age = Column(
        Integer,
        nullable=True
    )

    def __repr__(self):
        return f"<User(id={self.id})>"

2.5 AST (Abstract Syntax Tree)

Что это: Работа с синтаксическим деревом Python кода.

import ast

# Генерация класса программно
def generate_class(class_name: str, fields: list):
    """
    Создаёт Python класс через AST.

    fields = [
        {"name": "id", "type": "int"},
        {"name": "email", "type": "str"}
    ]
    """

    # Создаём __init__ метод
    init_args = [ast.arg(arg="self", annotation=None)]
    init_args += [
        ast.arg(arg=field["name"], annotation=ast.Name(id=field["type"], ctx=ast.Load()))
        for field in fields
    ]

    init_body = [
        ast.Assign(
            targets=[ast.Attribute(value=ast.Name(id="self", ctx=ast.Load()), attr=field["name"], ctx=ast.Store())],
            value=ast.Name(id=field["name"], ctx=ast.Load())
        )
        for field in fields
    ]

    init_func = ast.FunctionDef(
        name="__init__",
        args=ast.arguments(
            posonlyargs=[],
            args=init_args,
            kwonlyargs=[],
            kw_defaults=[],
            defaults=[]
        ),
        body=init_body,
        decorator_list=[],
        returns=None
    )

    # Создаём класс
    class_def = ast.ClassDef(
        name=class_name,
        bases=[],
        keywords=[],
        body=[init_func],
        decorator_list=[]
    )

    # Создаём модуль
    module = ast.Module(body=[class_def], type_ignores=[])

    # Компилируем в код
    code = compile(module, filename="<ast>", mode="exec")

    # Выполняем
    namespace = {}
    exec(code, namespace)

    return namespace[class_name]

# Использование
User = generate_class("User", [
    {"name": "id", "type": "int"},
    {"name": "email", "type": "str"}
])

user = User(id=1, email="test@example.com")
print(user.id, user.email)  # 1 test@example.com

Преобразование AST → код:

import ast
import astor  # pip install astor

# AST объект
class_def = ast.ClassDef(
    name="User",
    bases=[],
    keywords=[],
    body=[...],
    decorator_list=[]
)

# AST → Python код (строка)
code = astor.to_source(class_def)
print(code)

2.6 Cookiecutter (Project Templates)

Что это: Генератор проектов из шаблонов.

pip install cookiecutter

Создание шаблона:

my-template/
├── cookiecutter.json          # Параметры
└── {{cookiecutter.project_name}}/
    ├── models.py
    ├── api.py
    └── README.md

cookiecutter.json:

{
  "project_name": "my_project",
  "entity_name": "User",
  "fields": [
    {"name": "id", "type": "int"},
    {"name": "email", "type": "str"}
  ]
}

models.py (шаблон):

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class {{ cookiecutter.entity_name }}(Base):
    __tablename__ = "{{ cookiecutter.entity_name.lower() }}s"

    {% for field in cookiecutter.fields %}
    {{ field.name }} = Column({{ field.type | capitalize }})
    {% endfor %}

Использование:

cookiecutter my-template/

# Вводите параметры:
project_name: analytics
entity_name: Report
fields: [{"name": "id", "type": "int"}, {"name": "title", "type": "str"}]

# Создаётся проект:
analytics/
├── models.py
├── api.py
└── README.md

Часть 3: ГОТОВЫЕ LOW-CODE ФРЕЙМВОРКИ

3.1 Django REST Framework - Serializers

Концепция: Описываешь serializer → получаешь валидацию + API.

from rest_framework import serializers

class UserSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    email = serializers.EmailField()
    name = serializers.CharField(max_length=100)
    age = serializers.IntegerField(min_value=0, max_value=150, required=False)

# Автоматически:
# - Валидация
# - Сериализация (Python → JSON)
# - Десериализация (JSON → Python)

data = {"id": 1, "email": "test@example.com", "name": "John"}
serializer = UserSerializer(data=data)

if serializer.is_valid():
    print(serializer.validated_data)

3.2 Marshmallow (Schema → Validation)

pip install marshmallow
from marshmallow import Schema, fields, validate

class UserSchema(Schema):
    id = fields.Int(required=True)
    email = fields.Email(required=True)
    name = fields.Str(required=True, validate=validate.Length(min=1, max=100))
    age = fields.Int(validate=validate.Range(min=0, max=150))

schema = UserSchema()
result = schema.load({"id": 1, "email": "test@example.com", "name": "John", "age": 30})
print(result)  # {'id': 1, 'email': 'test@example.com', 'name': 'John', 'age': 30}

3.3 attrs (Classes without Boilerplate)

pip install attrs
import attr

@attr.s(auto_attribs=True)
class User:
    id: int
    email: str
    name: str
    age: int = 0

# Автоматически создаются:
# - __init__
# - __repr__
# - __eq__
# - Валидаторы

user = User(id=1, email="test@example.com", name="John")
print(user)  # User(id=1, email='test@example.com', name='John', age=0)

Часть 4: КОНСТРУКТОР ДЛЯ ПЛАТФОРМЫ ЦИФРА

4.1 Архитектура конструктора

┌─────────────────────────────────────────────────────────┐
│                    YAML ОПИСАНИЕ                        │
│  entities:                                              │
│    User:                                                │
│      fields:                                            │
│        - name: id, type: int, primary_key: true         │
│        - name: email, type: str, unique: true           │
│      api:                                               │
│        - GET /users                                     │
│        - POST /users                                    │
└────────────────────┬────────────────────────────────────┘
                     │
                     ↓
┌─────────────────────────────────────────────────────────┐
│                 PARSER (YAMLPython)                  │
│  entity_config = yaml.safe_load(...)                    │
└────────────────────┬────────────────────────────────────┘
                     │
                     ↓
┌─────────────────────────────────────────────────────────┐
│                CODE GENERATOR                           │
│  ┌────────────────────────────────────────────────┐     │
│  │ 1. SQLAlchemy Model (Jinja2)                   │     │
│  │    templates/sqlalchemy_model.py.j2            │     │
│  ├────────────────────────────────────────────────┤     │
│  │ 2. Pydantic Schema (create_model)              │     │
│  │    UserCreate, UserResponse                    │     │
│  ├────────────────────────────────────────────────┤     │
│  │ 3. FastAPI Endpoints (Jinja2)                  │     │
│  │    templates/fastapi_crud.py.j2                │     │
│  ├────────────────────────────────────────────────┤     │
│  │ 4. Alembic Migration (Jinja2)                  │     │
│  │    templates/alembic_migration.py.j2           │     │
│  ├────────────────────────────────────────────────┤     │
│  │ 5. Tests (Jinja2)                              │     │
│  │    templates/test_entity.py.j2                 │     │
│  └────────────────────────────────────────────────┘     │
└────────────────────┬────────────────────────────────────┘
                     │
                     ↓
┌─────────────────────────────────────────────────────────┐
│                 GENERATED CODE                          │
│  ├── models/user.py          (SQLAlchemy)               │
│  ├── schemas/user.py         (Pydantic)                 │
│  ├── api/users.py            (FastAPI CRUD)             │
│  ├── migrations/001_user.py  (Alembic)                  │
│  └── tests/test_user.py      (pytest)                   │
└─────────────────────────────────────────────────────────┘

4.2 Формат описания (YAML DSL)

Файл: schemas/User.yaml

entity:
  name: User
  table_name: users
  description: "Пользователь системы"

fields:
  - name: id
    type: integer
    primary_key: true
    auto_increment: true

  - name: email
    type: string
    max_length: 255
    unique: true
    nullable: false
    validators:
      - email

  - name: username
    type: string
    max_length: 100
    unique: true
    nullable: false
    validators:
      - min_length: 3
      - alphanumeric

  - name: password_hash
    type: string
    max_length: 255
    nullable: false

  - name: is_active
    type: boolean
    default: true

  - name: created_at
    type: datetime
    auto_now_add: true

  - name: updated_at
    type: datetime
    auto_now: true

indexes:
  - fields: [email]
    unique: true
  - fields: [username]
    unique: true
  - fields: [created_at]

relationships:
  - name: posts
    entity: Post
    type: one_to_many
    foreign_key: user_id

api:
  endpoints:
    - method: GET
      path: /users
      description: "Список пользователей"
      auth: true

    - method: POST
      path: /users
      description: "Создать пользователя"
      auth: false

    - method: GET
      path: /users/{id}
      description: "Получить пользователя"
      auth: true

    - method: PUT
      path: /users/{id}
      description: "Обновить пользователя"
      auth: true

    - method: DELETE
      path: /users/{id}
      description: "Удалить пользователя"
      auth: true
      admin_only: true

permissions:
  create: ["anonymous"]
  read: ["authenticated"]
  update: ["owner", "admin"]
  delete: ["admin"]

4.3 Генератор (Python код)

generator.py:

import yaml
from jinja2 import Environment, FileSystemLoader
from pathlib import Path
from pydantic import create_model

class CodeGenerator:
    def __init__(self, templates_dir: str, output_dir: str):
        self.env = Environment(loader=FileSystemLoader(templates_dir))
        self.output_dir = Path(output_dir)

    def generate_from_yaml(self, yaml_file: str):
        """Генерирует код из YAML описания"""

        # 1. Загрузка YAML
        with open(yaml_file) as f:
            config = yaml.safe_load(f)

        entity_name = config["entity"]["name"]

        # 2. Генерация SQLAlchemy модели
        self._generate_sqlalchemy_model(config)

        # 3. Генерация Pydantic схем
        self._generate_pydantic_schemas(config)

        # 4. Генерация FastAPI endpoints
        self._generate_fastapi_endpoints(config)

        # 5. Генерация Alembic миграции
        self._generate_alembic_migration(config)

        # 6. Генерация тестов
        self._generate_tests(config)

        print(f"✅ Generated code for {entity_name}")

    def _generate_sqlalchemy_model(self, config):
        """Генерирует SQLAlchemy модель"""
        template = self.env.get_template("sqlalchemy_model.py.j2")
        code = template.render(entity=config)

        output_file = self.output_dir / "models" / f"{config['entity']['name'].lower()}.py"
        output_file.parent.mkdir(parents=True, exist_ok=True)
        output_file.write_text(code)

    def _generate_pydantic_schemas(self, config):
        """Генерирует Pydantic схемы"""
        template = self.env.get_template("pydantic_schemas.py.j2")
        code = template.render(entity=config)

        output_file = self.output_dir / "schemas" / f"{config['entity']['name'].lower()}.py"
        output_file.parent.mkdir(parents=True, exist_ok=True)
        output_file.write_text(code)

    def _generate_fastapi_endpoints(self, config):
        """Генерирует FastAPI CRUD endpoints"""
        template = self.env.get_template("fastapi_crud.py.j2")
        code = template.render(entity=config)

        output_file = self.output_dir / "api" / f"{config['entity']['name'].lower()}s.py"
        output_file.parent.mkdir(parents=True, exist_ok=True)
        output_file.write_text(code)

    def _generate_alembic_migration(self, config):
        """Генерирует Alembic миграцию"""
        template = self.env.get_template("alembic_migration.py.j2")
        code = template.render(entity=config)

        import datetime
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = f"{timestamp}_create_{config['entity']['table_name']}.py"

        output_file = self.output_dir / "migrations" / "versions" / filename
        output_file.parent.mkdir(parents=True, exist_ok=True)
        output_file.write_text(code)

    def _generate_tests(self, config):
        """Генерирует pytest тесты"""
        template = self.env.get_template("test_entity.py.j2")
        code = template.render(entity=config)

        output_file = self.output_dir / "tests" / f"test_{config['entity']['name'].lower()}.py"
        output_file.parent.mkdir(parents=True, exist_ok=True)
        output_file.write_text(code)


# Использование
if __name__ == "__main__":
    generator = CodeGenerator(
        templates_dir="templates",
        output_dir="generated"
    )

    generator.generate_from_yaml("schemas/User.yaml")

4.4 Шаблоны Jinja2

templates/sqlalchemy_model.py.j2:

from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime

Base = declarative_base()

class {{ entity.name }}(Base):
    """{{ entity.description }}"""
    __tablename__ = "{{ entity.table_name }}"

    {% for field in fields %}
    {{ field.name }} = Column(
        {% if field.type == "integer" %}Integer{% endif %}
        {% if field.type == "string" %}String({{ field.max_length | default(255) }}){% endif %}
        {% if field.type == "boolean" %}Boolean{% endif %}
        {% if field.type == "datetime" %}DateTime{% endif %},
        {% if field.primary_key %}primary_key=True,{% endif %}
        {% if field.auto_increment %}autoincrement=True,{% endif %}
        {% if field.unique %}unique=True,{% endif %}
        {% if field.nullable == false %}nullable=False,{% endif %}
        {% if field.default is defined %}default={{ field.default }},{% endif %}
        {% if field.auto_now_add %}default=datetime.utcnow,{% endif %}
        {% if field.auto_now %}onupdate=datetime.utcnow,{% endif %}
    )
    {% endfor %}

    {% for rel in relationships %}
    {{ rel.name }} = relationship("{{ rel.entity }}", back_populates="{{ entity.name.lower() }}")
    {% endfor %}

    def __repr__(self):
        return f"<{{ entity.name }}(id={self.id})>"

templates/fastapi_crud.py.j2:

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List

from ..database import get_db
from ..models.{{ entity.name.lower() }} import {{ entity.name }}
from ..schemas.{{ entity.name.lower() }} import {{ entity.name }}Create, {{ entity.name }}Response

router = APIRouter(prefix="/{{ entity.table_name }}", tags=["{{ entity.table_name }}"])

{% for endpoint in api.endpoints %}
{% if endpoint.method == "GET" and "{id}" not in endpoint.path %}
@router.get("", response_model=List[{{ entity.name }}Response])
async def list_{{ entity.table_name }}(
    skip: int = 0,
    limit: int = 100,
    db: Session = Depends(get_db)
):
    """{{ endpoint.description }}"""
    items = db.query({{ entity.name }}).offset(skip).limit(limit).all()
    return items
{% endif %}

{% if endpoint.method == "POST" %}
@router.post("", response_model={{ entity.name }}Response, status_code=201)
async def create_{{ entity.name.lower() }}(
    data: {{ entity.name }}Create,
    db: Session = Depends(get_db)
):
    """{{ endpoint.description }}"""
    item = {{ entity.name }}(**data.dict())
    db.add(item)
    db.commit()
    db.refresh(item)
    return item
{% endif %}

{% if endpoint.method == "GET" and "{id}" in endpoint.path %}
@router.get("/{id}", response_model={{ entity.name }}Response)
async def get_{{ entity.name.lower() }}(
    id: int,
    db: Session = Depends(get_db)
):
    """{{ endpoint.description }}"""
    item = db.query({{ entity.name }}).filter({{ entity.name }}.id == id).first()
    if not item:
        raise HTTPException(status_code=404, detail="{{ entity.name }} not found")
    return item
{% endif %}

{% if endpoint.method == "PUT" %}
@router.put("/{id}", response_model={{ entity.name }}Response)
async def update_{{ entity.name.lower() }}(
    id: int,
    data: {{ entity.name }}Create,
    db: Session = Depends(get_db)
):
    """{{ endpoint.description }}"""
    item = db.query({{ entity.name }}).filter({{ entity.name }}.id == id).first()
    if not item:
        raise HTTPException(status_code=404, detail="{{ entity.name }} not found")

    for key, value in data.dict().items():
        setattr(item, key, value)

    db.commit()
    db.refresh(item)
    return item
{% endif %}

{% if endpoint.method == "DELETE" %}
@router.delete("/{id}", status_code=204)
async def delete_{{ entity.name.lower() }}(
    id: int,
    db: Session = Depends(get_db)
):
    """{{ endpoint.description }}"""
    item = db.query({{ entity.name }}).filter({{ entity.name }}.id == id).first()
    if not item:
        raise HTTPException(status_code=404, detail="{{ entity.name }} not found")

    db.delete(item)
    db.commit()
    return None
{% endif %}
{% endfor %}

4.5 Использование конструктора

Шаг 1: Описываете entity (YAML)

# schemas/Product.yaml
entity:
  name: Product
  table_name: products

fields:
  - name: id
    type: integer
    primary_key: true

  - name: name
    type: string
    max_length: 200

  - name: price
    type: integer

api:
  endpoints:
    - method: GET
      path: /products
    - method: POST
      path: /products

Шаг 2: Запускаете генератор

python generator.py schemas/Product.yaml

Шаг 3: Получаете готовый код

generated/
├── models/product.py        # SQLAlchemy модель
├── schemas/product.py       # Pydantic схемы
├── api/products.py          # FastAPI CRUD
├── migrations/..._create_products.py
└── tests/test_product.py

Шаг 4: Подключаете к приложению

# main.py
from fastapi import FastAPI
from generated.api.products import router as products_router

app = FastAPI()
app.include_router(products_router)

Готово! API работает:

GET  /products     - Список
POST /products     - Создать
GET  /products/1   - Получить
PUT  /products/1   - Обновить
DELETE /products/1 - Удалить

Часть 5: ИНТЕГРАЦИЯ С AI (Claude)

5.1 AI-powered конструктор

Идея: Claude анализирует текстовый запрос → генерирует YAML → конструктор генерирует код.

Пользователь: "Создай сущность Product с полями name, price, stock"
    
Claude API: Анализ  YAML
    
{
  entity: {name: "Product", table_name: "products"},
  fields: [
    {name: "id", type: "integer", primary_key: true},
    {name: "name", type: "string", max_length: 200},
    {name: "price", type: "integer"},
    {name: "stock", type: "integer", default: 0}
  ],
  api: {endpoints: [...]}
}
    
Code Generator: YAML  Python код
    
Готовые файлы: models.py, api.py, schemas.py

Реализация:

import anthropic
from generator import CodeGenerator

async def ai_powered_generator(user_request: str):
    """
    AI-powered конструктор:
    Текстовый запрос → Claude генерирует YAML → Генератор создаёт код
    """

    # 1. Claude генерирует YAML
    claude = anthropic.Anthropic(api_key="...")

    prompt = f"""
    Создай YAML описание entity для следующего запроса:
    "{user_request}"

    Формат YAML:
    entity:
      name: "EntityName"
      table_name: "table_name"
    fields:
      - name: id, type: integer, primary_key: true
      - name: ..., type: ..., ...
    api:
      endpoints:
        - method: GET, path: /items
        - method: POST, path: /items

    Верни ТОЛЬКО YAML, без комментариев.
    """

    response = await claude.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=2000,
        messages=[{"role": "user", "content": prompt}]
    )

    yaml_content = response.content[0].text

    # 2. Сохраняем YAML
    yaml_file = "schemas/generated.yaml"
    with open(yaml_file, "w") as f:
        f.write(yaml_content)

    # 3. Генерируем код
    generator = CodeGenerator(
        templates_dir="templates",
        output_dir="generated"
    )
    generator.generate_from_yaml(yaml_file)

    return {
        "yaml": yaml_content,
        "files_generated": [
            "models/entity.py",
            "schemas/entity.py",
            "api/entities.py"
        ]
    }

# Использование
result = await ai_powered_generator(
    "Создай сущность User с email, password, is_active"
)

Часть 6: РЕКОМЕНДАЦИЯ ДЛЯ ПЛАТФОРМЫ ЦИФРА

Стек для конструктора:

YAML (описание entity)
    ↓
Claude API (генерация YAML из текста)
    ↓
Python Parser (PyYAML)
    ↓
Code Generator:
    ├── Jinja2 (шаблоны)
    ├── Pydantic (динамические модели)
    └── AST (если нужна гибкость)
    ↓
Generated Code:
    ├── SQLAlchemy models
    ├── Pydantic schemas
    ├── FastAPI endpoints
    ├── Alembic migrations
    └── Tests

Преимущества:

  1. Скорость: Описал entity → получил готовый CRUD за 5 секунд
  2. Консистентность: Весь код по одному стандарту
  3. DRY: Описываешь один раз, генерируется всё
  4. AI-first: Claude помогает создавать YAML
  5. Гибкость: Можно кастомизировать шаблоны

ИТОГО

Вопрос: Существует ли что-то для описания entity → генерация кода?

Ответ: ДА! Множество решений:

Теория:
- MDA (Model-Driven Architecture)
- DSL (Domain-Specific Languages)
- Метапрограммирование

Практика (Python):
- Pydantic - модели с валидацией
- dataclasses - классы данных
- SQLAlchemy - ORM модели
- Jinja2 - генерация кода из шаблонов
- AST - работа с синтаксическим деревом
- Cookiecutter - генерация проектов

Для платформы ЦИФРА:

Описание (YAML) + Claude API + Jinja2 = Готовый код

Время разработки конструктора: 3-5 дней
Экономия времени после: Каждая новая entity = 5 секунд вместо 1 часа!


Версия: 1.0.0
Статус: Готово к обсуждению
Следующий шаг: Начать разработку конструктора?