Skip to main content

A simple async ORM with fastapi in mind and pydantic validation.

Project description

ormar

Pypi version Pypi version Build Status Coverage CodeFactor Codacy

Overview

The ormar package is an async mini ORM for Python, with support for Postgres, MySQL, and SQLite.

The main benefit 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 model (sqlalchemy, peewee, gino etc.)

The goal was to create a simple ORM that can be used directly (as request and response models) with fastapi that bases it's data validation on pydantic.

Ormar - apart form obvious ORM in name - get it's name from ormar in swedish which means snakes, and ormar(e) in italian which means cabinet.

And what's a better name for python ORM than snakes cabinet :)

Documentation

Check out the documentation for details.

Dependencies

Ormar is built with:

Migrations

Because ormar is built on SQLAlchemy core, you can use alembic to provide database migrations.

ormar is still under development: We recommend pinning any dependencies (with i.e. ormar~=0.5.2)

Quick Start

Note: Use ipython to try this from the console, since it supports await.

import databases
import ormar
import sqlalchemy

database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()


class Album(ormar.Model):
    class Meta:
        tablename = "album"
        metadata = metadata
        database = database

    # note that type hints are optional so 
    # id = ormar.Integer(primary_key=True) 
    # is also valid
    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)


class Track(ormar.Model):
    class Meta:
        tablename = "track"
        metadata = metadata
        database = database

    id: int = ormar.Integer(primary_key=True)
    album: Optional[Album] = ormar.ForeignKey(Album)
    title: str = ormar.String(max_length=100)
    position: int = ormar.Integer()


# Create some records to work with.
malibu = await Album.objects.create(name="Malibu")
await Track.objects.create(album=malibu, title="The Bird", position=1)
await Track.objects.create(album=malibu, title="Heart don't stand a chance", position=2)
await Track.objects.create(album=malibu, title="The Waters", position=3)

# alternative creation of object divided into 2 steps
fantasies = Album(name="Fantasies")
await fantasies.save()
await Track.objects.create(album=fantasies, title="Help I'm Alive", position=1)
await Track.objects.create(album=fantasies, title="Sick Muse", position=2)


# Fetch an instance, without loading a foreign key relationship on it.
track = await Track.objects.get(title="The Bird")

# We have an album instance, but it only has the primary key populated
print(track.album)       # Album(id=1) [sparse]
print(track.album.pk)    # 1
print(track.album.name)  # None

# Load the relationship from the database
await track.album.load()
assert track.album.name == "Malibu"

# This time, fetch an instance, loading the foreign key relationship.
track = await Track.objects.select_related("album").get(title="The Bird")
assert track.album.name == "Malibu"

# By default you also get a second side of the relation 
# constructed as lowercase source model name +'s' (tracks in this case)
# you can also provide custom name with parameter related_name
album = await Album.objects.select_related("tracks").all()
assert len(album.tracks) == 3

# Fetch instances, with a filter across an FK relationship.
tracks = Track.objects.filter(album__name="Fantasies")
assert len(tracks) == 2

# Fetch instances, with a filter and operator across an FK relationship.
tracks = Track.objects.filter(album__name__iexact="fantasies")
assert len(tracks) == 2

# Limit a query
tracks = await Track.objects.limit(1).all()
assert len(tracks) == 1

Ormar Specification

QuerySet methods

  • create(**kwargs): -> Model
  • get(**kwargs): -> Model
  • get_or_create(**kwargs) -> Model
  • update(each: bool = False, **kwargs) -> int
  • update_or_create(**kwargs) -> Model
  • bulk_create(objects: List[Model]) -> None
  • bulk_update(objects: List[Model], columns: List[str] = None) -> None
  • delete(each: bool = False, **kwargs) -> int
  • all(self, **kwargs) -> List[Optional[Model]]
  • filter(**kwargs) -> QuerySet
  • exclude(**kwargs) -> QuerySet
  • select_related(related: Union[List, str]) -> QuerySet
  • prefetch_related(related: Union[List, str]) -> QuerySet
  • limit(limit_count: int) -> QuerySet
  • offset(offset: int) -> QuerySet
  • count() -> int
  • exists() -> bool
  • fields(columns: Union[List, str, set, dict]) -> QuerySet
  • exclude_fields(columns: Union[List, str, set, dict]) -> QuerySet
  • order_by(columns:Union[List, str]) -> QuerySet

Relation types

  • One to many - with ForeignKey(to: Model)
  • Many to many - with ManyToMany(to: Model, through: Model)

Model fields types

Available Model Fields (with required args - optional ones in docs):

  • String(max_length)
  • Text()
  • Boolean()
  • Integer()
  • Float()
  • Date()
  • Time()
  • DateTime()
  • JSON()
  • BigInteger()
  • Decimal(scale, precision)
  • UUID()
  • ForeignKey(to)
  • ManyToMany(to, through)

Available fields options

The following keyword arguments are supported on all field types.

  • primary_key: bool
  • nullable: bool
  • default: Any
  • server_default: Any
  • index: bool
  • unique: bool
  • choices: typing.Sequence
  • name: str

All fields are required unless one of the following is set:

  • nullable - Creates a nullable column. Sets the default to None.
  • default - Set a default value for the field.
  • server_default - Set a default value for the field on server side (like sqlalchemy's func.now()).
  • primary key with autoincrement - When a column is set to primary key and autoincrement is set on this column. Autoincrement is set by default on int primary keys.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ormar-0.5.4.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ormar-0.5.4-py3-none-any.whl (49.2 kB view details)

Uploaded Python 3

File details

Details for the file ormar-0.5.4.tar.gz.

File metadata

  • Download URL: ormar-0.5.4.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.54.0 CPython/3.9.0

File hashes

Hashes for ormar-0.5.4.tar.gz
Algorithm Hash digest
SHA256 f407be05a9cd1e9a61a958d77364dbb52f55b20be1b8ac848038a4b29135af90
MD5 a8f045cf063ea1e1e6674800be9da72f
BLAKE2b-256 0d9d4e542d86096b6a76eca61002821d2f030f5219168f3d5a6f2c55ae85f762

See more details on using hashes here.

File details

Details for the file ormar-0.5.4-py3-none-any.whl.

File metadata

  • Download URL: ormar-0.5.4-py3-none-any.whl
  • Upload date:
  • Size: 49.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.6.1 requests/2.25.0 setuptools/49.2.1 requests-toolbelt/0.9.1 tqdm/4.54.0 CPython/3.9.0

File hashes

Hashes for ormar-0.5.4-py3-none-any.whl
Algorithm Hash digest
SHA256 4b824d4250872c43140f370dd739aba5ea90eb92fb7420758e25d90c0a759f18
MD5 8f27844da1b57c90c014cf2db9dc4103
BLAKE2b-256 742e7ceab54449197ad4bb8c7ad4927ab60975e3d040cfb6049d6635a5eb5f22

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page