К основному содержанию
правило

tortoise-orm — правило для Cursor: модели и запросы

Правило задаёт рекомендации по работе с Tortoise ORM в Python-проектах: от управления подключениями до пакетных операций и миграций.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/tortoise-orm.mdc
Открыть на GitHub

Что делает tortoise-orm

Правило применяется к файлам **/*.py. Оно рекомендует явно открывать и закрывать подключения, задавать типы и первичные ключи моделей, а общие поля выносить в абстрактные модели. Для связанных объектов предлагаются select_related и fetch_related, чтобы не выполнять отдельный запрос для каждой записи; массовую обработку — через bulk_create и bulk_update.

Для разработки в примерах используется Tortoise.generate_schemas(), а для миграций в рабочей среде — Aerich: нужно добавить aerich.models в конфигурацию TORTOISE_ORM, затем выполнить команды инициализации и применения миграций. Это правило для Python-кода, а не готовая настройка базы данных: конфигурацию и установку Aerich придётся внести в проект самостоятельно.

Какие задачи решает

  • закрыть подключения после асинхронной работы с базой
  • задать явные первичные ключи и типы полей моделей
  • загрузить связанные записи без отдельных запросов в цикле
  • создать или обновить записи пакетно
  • подготовить миграции Aerich для рабочей среды

Примеры запросов агенту

  • Проверь в models.py, заданы ли у моделей явные первичные ключи и типы полей.
  • Перепиши загрузку категорий и товаров, чтобы не выполнять запрос для каждой категории.
  • Подскажи, как заменить generate_schemas() на миграции Aerich в рабочей среде.

Как установить tortoise-orm

Вручную: положить файлы

.cursor/rules/tortoise-orm.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о tortoise-orm

Как установить tortoise-orm в Cursor?

Добавьте файл правила по пути .cursor/rules/tortoise-orm.mdc в проекте. Cursor применяет его к файлам **/*.py.

Что делает правило tortoise-orm?

Оно подсказывает, как организовать асинхронные подключения, модели, запросы и пакетные операции с Tortoise ORM. Также рекомендует Aerich для миграций в рабочей среде.

Нужно ли использовать Aerich вместо generate_schemas()?

Для разработки правило допускает Tortoise.generate_schemas(). Для рабочей среды оно рекомендует Aerich и требует добавить aerich.models в конфигурацию TORTOISE_ORM.

Исходный текст

tortoise-orm.mdc · CC0-1.0
---
description: Definitive guidelines for using Tortoise ORM effectively, focusing on async patterns, robust model design, efficient querying, and production-ready migrations.
globs: **/*.py
---
# tortoise-orm Best Practices

Tortoise ORM is an excellent choice for `asyncio`-native Python applications. Adhere to these guidelines for maintainable, performant, and secure database interactions.

## 1. Async Lifecycle Management

Always initialize and close connections explicitly. Failing to do so leads to resource leaks and unpredictable behavior.

❌ BAD: Dangling connections
```python
from tortoise import Tortoise

async def main():
    await Tortoise.init(db_url='sqlite://:memory:', modules={'models': ['app.models']})
    # ... application logic ...
    # No close_connections()
```

✅ GOOD: Proper async context management
```python
from tortoise import Tortoise, run_async

async def init_db():
    await Tortoise.init(
        db_url='sqlite://:memory:',
        modules={'models': ['app.models']}
    )
    await Tortoise.generate_schemas() # For development only

async def close_db():
    await Tortoise.close_connections()

# Use in your application entry point:
async def run_app():
    await init_db()
    try:
        # Your application code here
        pass
    finally:
        await close_db()

# Or, for simple scripts:
run_async(run_app())
```

## 2. Production Migrations with Aerich

`Tortoise.generate_schemas()` is for development convenience only. For production, use Aerich, the official migration tool.

❌ BAD: Using `generate_schemas` in production
```python
# app.py
await Tortoise.init(...)
await Tortoise.generate_schemas() # 🚨 DANGER: Do NOT use in production!
```

✅ GOOD: Aerich for schema evolution
1.  **Install Aerich:** `pip install "aerich[toml]"`
2.  **Configure `TORTOISE_ORM` to include `aerich.models`:**
    ```python
    # config.py
    TORTOISE_ORM = {
        "connections": {"default": "postgres://user:pass@host:port/db"},
        "apps": {
            "models": {
                "models": ["app.models", "aerich.models"], # Crucial: include aerich.models
                "default_connection": "default",
            },
        },
    }
    ```
3.  **Initialize Aerich:** `aerich init -t config.TORTOISE_ORM`
4.  **Generate initial migration:** `aerich init-db`
5.  **Generate subsequent migrations:** `aerich migrate`
6.  **Apply migrations:** `aerich upgrade`

## 3. Robust Model Design

Define models clearly with explicit types, primary keys, and `Meta` options.

### 3.1 Base Model & Primary Keys
Every model must inherit from `tortoise.models.Model` and define a primary key. Use recommended primary key types.

❌ BAD: Implicit primary key, missing type hints
```python
from tortoise.models import Model
from tortoise import fields

class User(Model):
    name = fields.TextField() # 'id' will be auto-generated, but less explicit
```

✅ GOOD: Explicit primary key, type hints
```python
from tortoise.models import Model
from tortoise import fields
from uuid import UUID

class User(Model):
    id: UUID = fields.UUIDField(primary_key=True) # Explicit UUID PK
    name: str = fields.CharField(max_length=255, unique=True)
    created_at: fields.DatetimeField = fields.DatetimeField(auto_now_add=True)
    updated_at: fields.DatetimeField = fields.DatetimeField(auto_now=True)

    class Meta:
        table = "users" # Explicit table name
```

### 3.2 Abstract Models for Reusability
Use abstract models for common fields to avoid repetition.

```python
from tortoise.models import Model
from tortoise import fields

class TimestampMixin(Model):
    created_at = fields.DatetimeField(null=True, auto_now_add=True)
    updated_at = fields.DatetimeField(null=True, auto_now=True)

    class Meta:
        abstract = True # This model will not create a table

class Product(TimestampMixin):
    id: int = fields.IntField(primary_key=True)
    name: str = fields.CharField(max_length=100)
    price: float = fields.DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        table = "products"
```

### 3.3 Model Discovery (`__models__`)
For explicit model discovery, define `__models__` in your model modules.

```python
# app/models.py
from tortoise.models import Model
from tortoise import fields

class Category(Model):
    id: int = fields.IntField(primary_key=True)
    name: str = fields.CharField(max_length=50, unique=True)

class Item(Model):
    id: int = fields.IntField(primary_key=True)
    name: str = fields.CharField(max_length=100)
    category: fields.ForeignKeyRelation["Category"] = fields.ForeignKeyField('models.Category', related_name='items')

__models__ = [
    "app.models.Category",
    "app.models.Item",
]
```

## 4. Efficient Querying (Avoid N+1)

Always eager-load related objects to prevent the N+1 query problem.

❌ BAD: N+1 queries
```python
# Fetching each category's items individually
categories = await Category.all()
for category in categories:
    print(f"Category: {category.name}")
    # This triggers a new query for each category
    items = await category.items.all()
    for item in items:
        print(f"  Item: {item.name}")
```

✅ GOOD: Eager loading with `select_related` or `fetch_related`
```python
# Using select_related for forward relations (e.g., Item -> Category)
items_with_categories = await Item.all().select_related('category')
for item in items_with_categories:
    print(f"Item: {item.name}, Category: {item.category.name}") # category is pre-fetched

# Using fetch_related for reverse relations (e.g., Category -> items)
categories_with_items = await Category.all()
await categories_with_items.fetch_related('items') # Fetches all items for all categories in one query
for category in categories_with_items:
    print(f"Category: {category.name}")
    for item in category.items:
        print(f"  Item: {item.name}")
```

## 5. Batch Operations

Use `bulk_create` and `bulk_update` for inserting/updating multiple records efficiently.

❌ BAD: Individual saves in a loop
```python
# Inefficient for many objects
for data in list_of_data:
    await MyModel.create(**data)
```

✅ GOOD: Batch operations
```python
from datetime import datetime

# Efficient for many objects
objects_to_create = [MyModel(**data) for data in list_of_data]
await MyModel.bulk_create(objects_to_create)

# Similarly for updates:
objects_to_update = await MyModel.filter(status="pending")
for obj in objects_to_update:
    obj.status = "processed"
    obj.updated_at = datetime.now()
await MyModel.bulk_update(objects_to_update, fields=["status", "updated_at"])
```

## 6. Transactions for Atomicity

Ensure data consistency with atomic transactions for related operations.

```python
from tortoise.transactions import in_transaction

class Account(Model): # Assume Account model exists
    id: int = fields.IntField(primary_key=True)
    balance: float = fields.DecimalField(max_digits=10, decimal_places=2)

async def transfer_funds(from_account_id: int, to_account_id: int, amount: float):
    async with in_transaction() as connection:
        from_account = await Account.get(id=from_account_id)
        to_account = await Account.get(id=to_account_id)

        if from_account.balance < amount:
            raise ValueError("Insufficient funds")

        from_account.balance -= amount
        to_account.balance += amount

        await from_account.save(update_fields=["balance"], using_db=connection)
        await to_account.save(update_fields=["balance"], using_db=connection)
    # Transaction commits automatically on success, rolls back on error
```

## 7. Version Pinning

Pin your `tortoise-orm` version in `requirements.txt` to a stable minor release (e.g., `0.25.x`) to avoid unexpected breaking changes.

❌ BAD: Loose dependency
```
tortoise-orm
```

✅ GOOD: Pinned dependency
```
tortoise-orm==0.25.10 # Pin to a specific patch version
# or
tortoise-orm~=0.25.0 # Pin to a minor version range
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Все правила IDE по теме «Бэкенд и API» →
async-python-patterns Seth Hobson Скилл помогает писать асинхронный Python-код с asyncio, async/await и конкурентными задачами. Пригодится для API и приложений с большим числом I/O-операций. CX Codex 18 055установок python-pro VoltAgent Community Субагент пишет типобезопасный Python-код для веб API, системных утилит и сложных приложений. Пригодится для async-задач и проектов с высокими требованиями к типам. 11 689установок typespec-m365-copilot официальный github Правило подсказывает, как описывать агентов и API-плагины Microsoft 365 Copilot на TypeSpec. Срабатывает при работе с файлами .tsp. ★ 40 478звёзд репо typescript-mcp-server официальный github Правило подсказывает Copilot, как писать MCP-серверы на TypeScript SDK: классы, схемы на zod, транспорты и типовые ошибки. ★ 40 478звёзд репо springboot официальный github Правило задаёт Copilot стиль кода для Spring Boot: внедрение зависимостей, конфигурация, логирование и проверка сборки. ★ 40 478звёзд репо springboot-4-migration официальный github Инструкция для GitHub Copilot по переводу проектов со Spring Boot 3.x на 4.0. Помогает обновить зависимости в Gradle Kotlin DSL, каталоги версий и имена стартеров. ★ 40 478звёзд репо ruby-on-rails официальный github Задаёт для GitHub Copilot стандарты архитектуры Rails: тонкие контроллеры, сервисные объекты и строгие миграции. Помогает писать предсказуемый бэкенд на Ruby. ★ 40 478звёзд репо quarkus официальный github Задаёт стандарты разработки на Quarkus для GitHub Copilot при работе с Java и сборщиками. Направляет генерацию сервисов, конфигураций и тестов. ★ 40 478звёзд репо

Из того же набора240

Весь набор →
trpc Задаёт стандарты построения API на tRPC в файлах TypeScript. Помогает генерировать модульные роутеры, валидацию через Zod и корректные middleware. ★ 3 580звёзд репо trio Правило задаёт рекомендации по асинхронному Python-коду с Trio: управление задачами, отмена, ввод-вывод, типы и тесты. Подключите его, если проект использует Trio. ★ 3 580звёзд репо transformers Правило для Cursor задаёт стиль работы с библиотекой 🤗 Transformers: модульные модели, типизация, Trainer вместо ручных циклов обучения. ★ 3 580звёзд репо tqdm Файл правил для Cursor, который задаёт агенту единый стиль работы с tqdm: импорты, контекстные менеджеры, вывод в консоль и метрики в постфиксе. ★ 3 580звёзд репо tornado Правило для Cursor задаёт стиль кода в Tornado: только async/await для ввода-вывода, полные аннотации типов и один обработчик ошибок на всё приложение. ★ 3 580звёзд репо tkinter Направляет разработку GUI на Python: диктует использование ttk и ttkbootstrap, классовую архитектуру экранов и сетку grid. ★ 3 580звёзд репо tinygrad Правило задаёт практики для tinygrad: ленивые вычисления, сбор параметров, профилирование и работу с графом операций. ★ 3 580звёзд репо three-js Правило для Cursor задаёт стандарты разработки на three.js: обработка ошибок WebGL, ленивая загрузка ассетов, оптимизация цикла рендера и освобождение ресурсов. ★ 3 580звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой