Tortoise ORM 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 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.
- Navigate to the Projects page in the Neon Console.
- Click New Project.
- Specify your project settings and click Create.
Set up your Python project
Create a new directory for your project and navigate into it:
mkdir tortoise-neon-demo cd tortoise-neon-demoOptionally, create and activate a virtual environment to manage your dependencies:
Why use a virtual environment?
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.
python3 -m venv venv source venv/bin/activate # macOS / Linux # venv\Scripts\activate # WindowsInstall dependencies
Install Tortoise ORM, the
asyncpgPostgres driver, andpython-dotenvto manage environment variables securely. Tortoise provides a handy extra requirement flag to installasyncpgautomatically.Run the following command inside your virtual environment:
pip install "tortoise-orm[asyncpg]" python-dotenvGet 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.

Create a
.envfile in your project's root directory and add aDATABASE_URLvariable with the following format: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 includessl=trueto ensure a secure connection.Create the application
Create a file named
main.pyand add the following code. This script initializes Tortoise ORM, creates a simpleUserschema in your Neon database, inserts a record, and queries it back.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
Userclass defines your database schema, mapping Python types directly to PostgreSQL columns. TheTortoise.init()method connects to Neon using your connection string, whilegenerate_schemas()safely creates the tables if they don't already exist. For real-world applications, you would typically define models in a dedicated file (likemodels.py) and load them into the initialization config usingmodules={"models": ["my_project.models"]}.Run the script from your terminal:
python main.pyYou 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 atransactions.pyfile to test this: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 simulatedValueErrorinterrupts 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/pipandvenv/bin/python(or the activated equivalent). - Install Tortoise ORM with the
asyncpgextra:pip install "tortoise-orm[asyncpg]". - The connection string protocol must be
postgres://for Tortoise to utilize theasyncpgdriver automatically. - Always include
ssl=truein your connection string to ensure a secure connection to Neon. - Always ensure
await Tortoise.close_connections()is called during application teardown or use thetortoise.run_async()wrapper to prevent hanging python processes. - Do not hardcode credentials in initialization scripts. Use environment variables via
python-dotenvandos.getenv().
Resources
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. For paid plan support options, see Support.








