GINO Is Not ORM - a Python ORM on asyncpg and SQLAlchemy core.
Free software: BSD license
There’s been a lot of words about ORM a.k.a. Object-relational mapping - good or bad - as well as a lot of ORM libraries in Python. It is crucial to pick a most suitable one for your project, and for your team. GINO tries to stay in the middle between ORM and non-ORM, offering an extremely simple option.
GINO tries to define database tables with plain old Python objects - they are normal Python objects, a rollback doesn’t magically change their values. Any database operations are explicit. It is crystal clear what is done underneath each GINO API. There are no dirty models, no sessions, no magic. You have concrete control to the database, through a convenient object interface. That’s it.
GINO depends on asyncpg, which means it works only for PostgreSQL and asyncio, which means Python 3 is required - actually 3.6 required for now. Based on SQLAlchemy, gate to its ecosystem is open - feel free to use e.g. Alembic to manage your schema changes. And we specially designed a few candies for the Sanic server.
A piece of code is worth a thousand words:
from gino import Gino from sqlalchemy import Column, BigInteger, Unicode db = Gino() class User(db.Model): __tablename__ = 'users' id = Column(BigInteger(), primary_key=True) nickname = Column(Unicode(), default='noname')
This is quite similar to SQLAlchemy ORM, but it is actually SQLAlchemy core:
db = Gino() is actually a sqlalchemy.MetaData object
class User actually defines a sqlalchemy.Table at User.__table__
Other than that, User is just a normal Python object:
u = User() u.id = 7 u.id += 2 u.nickname = 'fantix'
Think as if User is defined normally (keep in imagination, not an example):
class User: def __init__(self): self.id = None self.nickname = None
However on class level, you have access to SQLAlchemy columns, which allows you to do SQLAlchemy core programming:
from sqlalchemy import select query = select([User.nickname]).where(User.id == 9)
The Gino object offers a SQLAlchemy dialect for asyncpg, allowing to execute the query in asyncpg:
import asyncpg conn = await asyncpg.connect('postgresql://localhost/gino') query, params = db.compile(query) rv = await conn.fetchval(query, *params)
Though it is possible to use GINO as a SQLAlchemy core async wrapper by using only db.Model and db.compile, it would make life much easier if GINO sugars for ORM are considered. First of all, it is preferred to bind an asyncpg.Pool to the Gino object, by creating a pool through a delegated API, following the same example above:
async with db.create_pool('postgresql://localhost/gino') as pool:
Because the models are defined with the same db object, they are automatically bound to the database pool, allowing such CRUD operations:
u1 = await User.get(9) u2 = await User.create(nickname=u1.nickname)) await u2.update(nickname='daisy') await u1.delete()
A note here: GINO has no u2.save(). Therefore u2.nickname = 'daisy' does not execute any SQL but only modify memory value - use u2.update to both run an UPDATE SQL and modify memory value. Correspondingly, u1.delete() only deletes the row in database, but leaving the object in memory untouched.
The Gino object db also offers a few more objective APIs for queries, corresponding to asyncpg APIs:
# returns all user objects with "d" in their nicknames users = await db.all(User.query.where(User.nickname.contains('d'))) # find one user object, None if not found user = await db.first(User.query.where(User.nickname == 'daisy'))
Or progressively load objects from a large query, in a transaction as required:
async with db.transaction() as (conn, tx): async for u in db.iterate(User.query, connection=conn): print(u.id, u.nickname)
There are a few tasks in GitHub issues marked as help wanted. Please feel free to take any of them and pull requests are greatly welcome.
To run tests:
python setup.py test
Credit goes to all contributors listed in the AUTHORS file. This project is inspired by asyncpgsa, peewee-async and asyncorm. asyncpg and SQLAlchemy as the dependencies did most of the heavy lifting. This package was created with Cookiecutter and the audreyr/cookiecutter-pypackage project template.
Support any primary key (Contributed by Tony Wang in #11)
Support SQLAlchemy result processor
Added rich support on JSON/JSONB
Added update and delete API
Changed API, no longer reuses asyncpg API
API changed: parameter conn renamed to optional bind
Delegated asyncpg Pool with db.create_pool
Internal enhancement and bug fixes
First release on PyPI.
Release history Release notifications | RSS feed
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.