diff --git a/content/docs/community/component-icon-guide.md b/content/docs/community/component-icon-guide.md index 1d2bbe4e61..a8506dca02 100644 --- a/content/docs/community/component-icon-guide.md +++ b/content/docs/community/component-icon-guide.md @@ -212,6 +212,7 @@ To test if an icon works: SQLAlchemy StepZen Supabase +Tortoise ORM TypeORM Drizzle diff --git a/content/docs/get-started/orms.md b/content/docs/get-started/orms.md index e0a2bbb066..c9b3ee9b89 100644 --- a/content/docs/get-started/orms.md +++ b/content/docs/get-started/orms.md @@ -29,6 +29,8 @@ updatedOn: '2026-02-06T22:07:32.890Z' + + diff --git a/content/docs/guides/tortoise-orm.md b/content/docs/guides/tortoise-orm.md new file mode 100644 index 0000000000..d685fb6a17 --- /dev/null +++ b/content/docs/guides/tortoise-orm.md @@ -0,0 +1,210 @@ +--- +title: Connect a Tortoise ORM application to Neon +subtitle: Set up a Neon project in seconds and connect from a Python async application +summary: >- + How to connect an asynchronous Python application to a Neon project using Tortoise ORM, the asycnpg driver. +enableTableOfContents: true +updatedOn: '2026-05-04T22:07:33.000Z' +--- + + + +[Tortoise ORM](https://tortoise.github.io/) is an easy-to-use `asyncio` Object Relational Mapper (ORM) inspired by Django. It is designed specifically for asynchronous Python applications, making it a great choice for building efficient, non-blocking database interactions in frameworks like FastAPI, Starlette, or any custom async application. + +In this guide, you’ll learn how to set up a Neon project and connect to it using Tortoise ORM with the `asyncpg` driver. + +## Prerequisites + +To complete the steps in this guide, ensure that you have Python 3.10+ installed. If you do not have Python installed, refer to the [official Python website](https://www.python.org/downloads/) for installation instructions. + +To connect to Neon from Tortoise ORM: + + + +## Create a Neon project + +If you do not have one already, create a Neon project. Save your connection details, including your password. They are required when defining connection settings. + +1. Navigate to the [Projects](https://console.neon.tech/app/projects) page in the Neon Console. +2. Click **New Project**. +3. Specify your project settings and click **Create**. + +## Set up your Python project + +Create a new directory for your project and navigate into it: + +```bash +mkdir tortoise-neon-demo +cd tortoise-neon-demo +``` + +Optionally, create and activate a virtual environment to manage your dependencies: + + +Using a virtual environment allows you to manage dependencies for your project in isolation, preventing conflicts with other Python projects on your system. It also makes it easier to maintain and share your project with others. + + +```bash +python3 -m venv venv +source venv/bin/activate # macOS / Linux +# venv\Scripts\activate # Windows +``` + +## Install dependencies + +Install Tortoise ORM, the `asyncpg` Postgres driver, and `python-dotenv` to manage environment variables securely. Tortoise provides a handy extra requirement flag to install `asyncpg` automatically. + +Run the following command inside your virtual environment: + +```shell +pip install "tortoise-orm[asyncpg]" python-dotenv +``` + +## Get your connection string + +Find your database connection string by clicking the **Connect** button on your **Project Dashboard** to open the **Connect to your database** modal. Check the **Parameters only** option, which provides the individual components of the connection string that you will use to construct a connection URL. +![Connection details modal](/docs/connect/connection_details_parameters_only.png) + +Create a `.env` file in your project's root directory and add a `DATABASE_URL` variable with the following format: + +```text shouldWrap +DATABASE_URL="postgres://[PGUSER]:[PGPASSWORD]@[PGHOST]/[PGDATABASE]?ssl=true" +``` + +> Replace the placeholders `[PGUSER]`, `[PGPASSWORD]`, `[PGHOST]`, and `[PGDATABASE]` with the corresponding values from your Neon connection details. Make sure to include `ssl=true` to ensure a secure connection. + +## Create the application + +Create a file named `main.py` and add the following code. This script initializes Tortoise ORM, creates a simple `User` schema in your Neon database, inserts a record, and queries it back. + +```python +import os +from dotenv import load_dotenv +from tortoise import Tortoise, fields, run_async +from tortoise.models import Model + +load_dotenv() + +# 1. Define your models +class User(Model): + id = fields.IntField(primary_key=True) + name = fields.CharField(max_length=50) + created_at = fields.DatetimeField(auto_now_add=True) + + class Meta: + table = "demo_users" + + def __str__(self): + return self.name + +# 2. Define the main async function +async def main(): + database_url = os.getenv("DATABASE_URL") + + try: + # Initialize Tortoise ORM + print("Connecting to Neon Postgres...") + await Tortoise.init( + db_url=database_url, + modules={"models": ["__main__"]}, # Looks for models in the current file + ) + + # Generate the schema (creates tables if they don't exist) + await Tortoise.generate_schemas() + + # Insert a new user + await User.create(name="Neon User") + + # Query the database + users = await User.all() + print(f"Successfully queried the database. Users found: {len(users)}") + for user in users: + print(f"- {user.name} (Created: {user.created_at})") + + finally: + # 3. Clean up and close connections + print("Closing database connections...") + await Tortoise.close_connections() + +if __name__ == "__main__": + # run_async is a Tortoise helper that ensures connections are closed + # if the script exits unexpectedly. + run_async(main()) +``` + +In this script, the `User` class defines your database schema, mapping Python types directly to PostgreSQL columns. The `Tortoise.init()` method connects to Neon using your connection string, while `generate_schemas()` safely creates the tables if they don't already exist. For real-world applications, you would typically define models in a dedicated file (like `models.py`) and load them into the initialization config using `modules={"models": ["my_project.models"]}`. + +Run the script from your terminal: + +```bash +python main.py +``` + +You should see output indicating that the connection was successful, the table was created, and the user was successfully saved and queried. + +## Execute a transaction + +Tortoise ORM provides an `in_transaction()` async context manager to ensure database operations succeed or fail together. Create a `transactions.py` file to test this: + +```python +import os +from dotenv import load_dotenv +from tortoise import Tortoise, run_async +from tortoise.transactions import in_transaction +from main import User + +load_dotenv() + +async def transaction_demo(): + await Tortoise.init( + db_url=os.getenv("DATABASE_URL"), modules={"models": ["__main__"]} + ) + + try: + # Execute operations inside a transaction block + async with in_transaction(): + user = await User.create(name="Alice") + print(f"Created: {user.name}") + + # Simulating an error that triggers a rollback + raise ValueError("Something went wrong! Rolling back...") + + await User.create(name="Bob") # This won't happen + + except ValueError as e: + print(f"Error caught: {e}") + + # Verify Alice was not saved + count = await User.filter(name="Alice").count() + print(f"Records named 'Alice' in database: {count}") + + await Tortoise.close_connections() + + +if __name__ == "__main__": + run_async(transaction_demo()) +``` + +The `in_transaction()` block ensures an all-or-nothing execution. Because the simulated `ValueError` interrupts the process, Tortoise automatically rolls back the transaction, preventing the partial insertion of "Alice" into the database. + + + +
+**Notes for AI-assisted setup** + +- Do not install packages globally. Always use a virtual environment and run commands with `venv/bin/pip` and `venv/bin/python` (or the activated equivalent). +- Install Tortoise ORM with the `asyncpg` extra: `pip install "tortoise-orm[asyncpg]"`. +- The connection string protocol must be `postgres://` for Tortoise to utilize the `asyncpg` driver automatically. +- Always include `ssl=true` in your connection string to ensure a secure connection to Neon. +- Always ensure `await Tortoise.close_connections()` is called during application teardown or use the `tortoise.run_async()` wrapper to prevent hanging python processes. +- Do not hardcode credentials in initialization scripts. Use environment variables via `python-dotenv` and `os.getenv()`. + +
+ +## Resources + +- [Tortoise ORM documentation](https://tortoise.github.io/) +- [asyncpg documentation](https://magicstack.github.io/asyncpg/current/) + + diff --git a/content/docs/introduction.md b/content/docs/introduction.md index 66320fb816..cc8a5b89ed 100644 --- a/content/docs/introduction.md +++ b/content/docs/introduction.md @@ -83,6 +83,8 @@ Copy a setup prompt, or browse our [framework](/docs/get-started/frameworks), [l + + diff --git a/content/docs/navigation.yaml b/content/docs/navigation.yaml index e74d4f5ef2..d9401f6fb8 100644 --- a/content/docs/navigation.yaml +++ b/content/docs/navigation.yaml @@ -383,6 +383,8 @@ slug: guides/ruby-on-rails - title: SQLAlchemy slug: guides/sqlalchemy + - title: Tortoise ORM + slug: guides/tortoise-orm - title: TypeORM slug: guides/typeorm - section: Backend diff --git a/public/images/technology-logos/tortoise-orm-dark.svg b/public/images/technology-logos/tortoise-orm-dark.svg new file mode 100644 index 0000000000..860f1ef68e --- /dev/null +++ b/public/images/technology-logos/tortoise-orm-dark.svg @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/technology-logos/tortoise-orm.svg b/public/images/technology-logos/tortoise-orm.svg new file mode 100644 index 0000000000..e2e989c70e --- /dev/null +++ b/public/images/technology-logos/tortoise-orm.svg @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/prompts/tortoise-orm-prompt.md b/public/prompts/tortoise-orm-prompt.md new file mode 100644 index 0000000000..0abfd694da --- /dev/null +++ b/public/prompts/tortoise-orm-prompt.md @@ -0,0 +1,171 @@ +# 💡 AI Prompt: Connect Tortoise ORM to Neon Postgres + +**Role:** You are an expert software agent responsible for configuring the current Python project to connect to a Neon Postgres database using Tortoise ORM. + +**Purpose:** To correctly set up a virtual environment, install the required dependencies, and provide working code examples for connecting to a Neon Postgres database, executing queries, and handling transactions. + +**Scope:** +- Must be run inside an existing Python project directory. +- Assumes the user has a Neon project and access to their database connection string. +- All package installations must occur within an active Python virtual environment. + +✅ Read and understand the entire instruction set before executing. + +--- + +## 🛠️ Instructions (for AI-enabled editors) + +When this prompt is triggered, automatically configure the open Python project as follows: + +### 1. Ensure a Virtual Environment is Active + +This is a mandatory first step. +- Check for a virtual environment directory (e.g., `venv`, `.venv`) in the project. +- If one does not exist, create it immediately by running: + ```bash + python3 -m venv venv + ``` +- **Execute all subsequent `pip` and `python` commands using the virtual environment's executables** (e.g., `venv/bin/python`, `venv/bin/pip`). + +--- + +### 2. Install Dependencies + +Install the required Python packages into the virtual environment. + +```bash +venv/bin/pip install "tortoise-orm[asyncpg]" python-dotenv +``` +- **`tortoise-orm[asyncpg]`**: The core asynchronous ORM, including the high-performance `asyncpg` PostgreSQL driver. +- **`python-dotenv`**: To load the database connection string from a `.env` file securely. + +--- + +### 3. Configure Environment Variables + +1. Check for the presence of a `.env` file at the root of the project. If it doesn't exist, create one. +2. Add the `DATABASE_URL` variable to the `.env` file. **Prompt the user to replace the placeholder value** with their full connection string from the Neon console. Ensure the protocol is `postgres://` and `ssl=true` is appended. + + ```dotenv title=".env" + DATABASE_URL="postgres://:@.neon.tech/?ssl=true" + ``` + +3. Direct the user to find the values in the **Neon Console → Project → Connect** (using the "Parameters only" view). + +--- + +### 4. Create Example Connection and Transaction Scripts + +To provide a clear and testable example, create a new file named `main.py` in the project root. This script connects to the database, defines a schema, creates a table, inserts a record, and demonstrates transaction rollbacks. + +```python title="main.py" +import os +from dotenv import load_dotenv +from tortoise import Tortoise, fields, run_async +from tortoise.models import Model +from tortoise.transactions import in_transaction + +load_dotenv() + +# 1. Define your models +class User(Model): + id = fields.IntField(primary_key=True) + name = fields.CharField(max_length=50) + created_at = fields.DatetimeField(auto_now_add=True) + + class Meta: + table = "demo_users" + + def __str__(self): + return self.name + + +# 2. Define the main async function +async def main(): + database_url = os.getenv("DATABASE_URL") + + if not database_url: + raise ValueError("DATABASE_URL environment variable is not set.") + + try: + # Initialize Tortoise ORM + print("Connecting to Neon Postgres...") + await Tortoise.init( + db_url=database_url, + modules={"models": ["__main__"]}, # Looks for models in the current file + ) + + # Generate the schema (creates tables if they don't exist) + await Tortoise.generate_schemas() + + # --- Basic Operations --- + print("\n--- Basic Operations ---") + await User.create(name="Neon User") + + users = await User.all() + print(f"Successfully queried the database. Users found: {len(users)}") + for user in users: + print(f"- {user.name} (Created: {user.created_at})") + + # --- Transactions Demo --- + print("\n--- Transactions Demo ---") + try: + # Execute operations inside a transaction block + async with in_transaction(): + user = await User.create(name="Alice") + print(f"Created inside transaction: {user.name}") + + # Simulating an error that triggers a rollback + raise ValueError("Something went wrong! Rolling back...") + + await User.create(name="Bob") # This won't happen + + except ValueError as e: + print(f"Error caught: {e}") + + # Verify Alice was not saved + count = await User.filter(name="Alice").count() + print(f"Records named 'Alice' in database after rollback: {count}") + + finally: + # 3. Clean up and close connections + print("\nClosing database connections...") + await Tortoise.close_connections() + + +if __name__ == "__main__": + # run_async is a Tortoise helper that ensures connections are closed + # if the script exits unexpectedly. + run_async(main()) +``` + +--- + +## 🚀 Next Steps + +Once the file modifications are complete: + +1. Prompt the user to confirm that their Neon `DATABASE_URL` is correctly set in the `.env` file. +2. Run the example script to test the connection and transaction handling: + ```bash + venv/bin/python main.py + ``` + +--- + +## ✅ Validation Rules for AI + +Before suggesting code or making edits, ensure: +- A Python virtual environment exists and is intended for use. +- The `tortoise-orm[asyncpg]` and `python-dotenv` packages are installed in the virtual environment. +- A `.env` file is present or has been created with a valid `postgres://` connection string containing `ssl=true`. +- The example script (`main.py`) correctly load the `DATABASE_URL` from the environment using `os.getenv()`. +- The `Tortoise.close_connections()` method and `run_async()` wrapper are used to prevent hanging processes. + +--- + +## ❌ Do Not + +- **Do not install packages globally** or outside of an active Python virtual environment. +- **Do not hardcode credentials** or the connection string in any Python source code file. +- Do not output the contents of the `.env` file or the user's connection string in any response. \ No newline at end of file diff --git a/src/config/docs-icons-config.js b/src/config/docs-icons-config.js index eda0a16417..02821b4b36 100644 --- a/src/config/docs-icons-config.js +++ b/src/config/docs-icons-config.js @@ -481,6 +481,11 @@ const ICONS_CONFIG = { lightIconPath: '/images/technology-logos/tanstack.svg', darkIconPath: '/images/technology-logos/tanstack-dark.svg', }, + 'tortoise-orm': { + name: 'tortoise-orm', + lightIconPath: '/images/technology-logos/tortoise-orm.svg', + darkIconPath: '/images/technology-logos/tortoise-orm-dark.svg', + }, typeorm: { name: 'typeorm', lightIconPath: '/images/technology-logos/typeorm.svg',