Ormar
python async orm with fastapi in mind and pydantic validation
Install / Use
/learn @ormar-orm/OrmarREADME
ormar
<p> <a href="https://pypi.org/project/ormar"> <img src="https://img.shields.io/pypi/v/ormar.svg" alt="Pypi version"> </a> <a href="https://pypi.org/project/ormar"> <img src="https://img.shields.io/pypi/pyversions/ormar.svg" alt="Pypi version"> </a> <a href="https://github.com/collerek/ormar/actions/workflows/test-package.yml"> <img src="https://github.com/collerek/ormar/actions/workflows/test-package.yml/badge.svg?branch=master" alt="Test package" height="20"> </a> <a href="https://codecov.io/gh/collerek/ormar"> <img src="https://codecov.io/gh/collerek/ormar/branch/master/graph/badge.svg" alt="Coverage"> </a> <a href="https://qlty.sh/gh/collerek/projects/ormar"><img src="https://qlty.sh/gh/collerek/projects/ormar/maintainability.svg" alt="Maintainability" /></a> <a href="https://pepy.tech/project/ormar"> <img src="https://pepy.tech/badge/ormar"></a> </p>Overview
The ormar package is an async mini ORM for Python, with support for Postgres,
MySQL, and SQLite.
The main benefits of using ormar are:
- getting an async ORM that can be used with async frameworks (fastapi, starlette etc.)
- getting just one model to maintain - you don't have to maintain pydantic and other orm models (sqlalchemy, peewee, gino etc.)
The goal was to create a simple ORM that can be used directly (as request and response models) with [fastapi][fastapi] that bases it's data validation on pydantic.
Ormar - apart from the obvious "ORM" in name - gets its name from ormar in Swedish which means snakes, and ormar in Croatian which means cabinet.
And what's a better name for python ORM than snakes cabinet :)
If you like ormar remember to star the repository in github!
The bigger community we build, the easier it will be to catch bugs and attract contributors ;)
Documentation
Check out the [documentation][documentation] for details.
Note that for brevity most of the documentation snippets omit the creation of the database and scheduling the execution of functions for asynchronous run.
If you want more real life examples than in the documentation you can see the [tests][tests] folder, since they actually have to create and connect to a database in most of the tests.
Yet remember that those are - well - tests and not all solutions are suitable to be used in real life applications.
Part of the fastapi ecosystem
As part of the fastapi ecosystem ormar is supported in libraries that somehow work with databases.
As of now ormar is supported by:
If you maintain or use a different library and would like it to support ormar let us know how we can help.
Dependencies
Ormar is built with:
- [
sqlalchemy core][sqlalchemy-core] for query building. - [
sqlalchemy async][sqlalchemy-async] for cross-database async support. - [
pydantic][pydantic] for data validation.
License
ormar is built as open-sorce software and will remain completely free (MIT license).
As I write open-source code to solve everyday problems in my work or to promote and build strong python community you can say thank you and buy me a coffee or sponsor me with a monthly amount to help ensure my work remains free and maintained.
<a aria-label="Sponsor collerek" href="https://github.com/sponsors/collerek" style="text-decoration: none; color: #c9d1d9 !important;"> <div style=" background-color: #21262d; border-color: #30363d; box-shadow: 0 0 transparent, 0 0 transparent; color: #c9d1d9 !important; border: 1px solid; border-radius: 6px; cursor: pointer; display: inline-block; font-size: 14px; padding: 10px; line-height: 0px; height: 40px; "> <span style="color: #c9d1d9 !important;">Sponsor - Github Sponsors</span> </div> </a>Migrating from sqlalchemy and existing databases
If you currently use sqlalchemy and would like to switch to ormar check out the auto-translation
tool that can help you with translating existing sqlalchemy orm models so you do not have to do it manually.
Beta versions available at github: sqlalchemy-to-ormar
or simply pip install sqlalchemy-to-ormar
sqlalchemy-to-ormar can be used in pair with sqlacodegen to auto-map/ generate ormar models from existing database, even if you don't use sqlalchemy for your project.
Migrations & Database creation
Because ormar is built on SQLAlchemy core, you can use [alembic][alembic] to provide
database migrations (and you really should for production code).
For tests and basic applications the sqlalchemy is more than enough:
# note this is just a partial snippet full working example below
# 1. Imports
import sqlalchemy
import ormar
from ormar import DatabaseConnection
# 2. Initialization
DATABASE_URL = "sqlite+aiosqlite:///db.sqlite"
base_ormar_config = ormar.OrmarConfig(
metadata=sqlalchemy.MetaData(),
database=DatabaseConnection(DATABASE_URL),
)
# Define models here
# 3. Database creation and tables creation
engine = sqlalchemy.create_engine(DATABASE_URL.replace('+aiosqlite', ''))
base_ormar_config.metadata.create_all(engine)
For a sample configuration of alembic and more information regarding migrations and database creation visit [migrations][migrations] documentation section.
Package versions
ormar is still under development:
We recommend pinning any dependencies (with i.e. ormar~=0.9.1)
ormar also follows the release numeration that breaking changes bump the major number,
while other changes and fixes bump minor number, so with the latter you should be safe to
update, yet always read the [releases][releases] docs before.
example: (0.5.2 -> 0.6.0 - breaking, 0.5.2 -> 0.5.3 - non breaking).
Asynchronous Python
Note that ormar is an asynchronous ORM, which means that you have to await the calls to
the methods, that are scheduled for execution in an event loop. Python has a builtin module
[asyncio][asyncio] that allows you to do just that.
Note that most "normal" python interpreters do not allow execution of await
outside of a function (because you actually schedule this function for delayed execution
and don't get the result immediately).
In a modern web framework (like fastapi), the framework will handle this for you, but if
you plan to do this on your own you need to perform this manually like described in the
quick start below.
Quick Start
Note that you can find the same script in examples folder on github.
from typing import Optional
import ormar
import pydantic
import sqlalchemy
DATABASE_URL = "sqlite+aiosqlite:///db.sqlite"
# note that this step is optional -> all ormar cares is an individual
# OrmarConfig for each of the models, but this way you do not
# have to repeat the same parameters if you use only one database
base_ormar_config = ormar.OrmarConfig(
database=ormar.DatabaseConnection(DATABASE_URL),
metadata=sqlalchemy.MetaData(),
)
# Note that all type hints are optional
# below is a perfectly valid model declaration
# class Author(ormar.Model):
# ormar_config = base_ormar_config.copy(tablename="authors")
#
# id = ormar.Integer(primary_key=True) # <= notice no field types
# name = ormar.String(max_length=100)
class Author(ormar.Model):
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Book(ormar.Model):
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
author: Optional[Author] = ormar.ForeignKey(Author)
title: str = ormar.String(max_length=100)
year: int = ormar.Integer(nullable=True)
# note - normally import should be at the beginning of the file
import asyncio
async def setup_database():
"""Create all tables in the database."""
if not base_ormar_config.database.is_connected:
await base_ormar_config.database.connect()
async with base_ormar_config.database.engine.begin() as conn:
await conn.run_sync(base_ormar_config.metadata.drop_all)
await conn.run_sync(base_ormar_config.metadata.create_all)
if base_ormar_config.database.is_connected:
await base_ormar_config.database.disconnect()
# create the database
# note that in production you should use migrations
# note that this is not required if you connect to existing database
# just to be sure we clear the db before
asyncio.run(setup_database())
# all functions below are divided into functionality categories
# note how all functions are defined with async - hence can use await AND needs to
# be awaited on their own
async def create():
# Create some records to work with through QuerySet.create method.
# Note that queryset is exposed on each Model's class as objects
tolkien = await Author.objects.create(name="J.R.R. Tolkien")
await Book.objects.create(author=tolkien, title="The Hobbit", year=1937)
await Book.objects.create(author=tolkien, title="The Lord of the Rings", year=1955)
await Book.objects.create(author=tolkien, title="The Silmarillion", year=1977)
# alternative creation of object divided into 2 steps
sapkowski = Author(name="Andrzej Sapkowski")
# do some stuff
await sapkowski.save()
# or save() after initialization
await Book(author=sapkowski, title="The Witcher", year=1990).save()
await Book(author=sapkowski, title="The Tower of Fools", year=2002).save()
# to read more about inserting data into the database
# visit: https://collerek.github.io/ormar/queries/create/
async def read():
# Fetch an instance, without loading a foreign key relationship on it.
# Django style
book = await Book.objects.get(title="The Hobbit")
# or python style
book = await Book.
