Skip to main content

GINO Is Not ORM - a Python asyncio ORM on SQLAlchemy core.

Project description

GINO

https://img.shields.io/pypi/v/gino.svg https://img.shields.io/travis/fantix/gino/master.svg https://img.shields.io/coveralls/github/fantix/gino/master.svg Documentation Status Updates Gitter chat

GINO - GINO Is Not ORM - is a lightweight asynchronous ORM built on top of SQLAlchemy core for Python asyncio. Now (early 2018) GINO supports only one dialect asyncpg.

  • Free software: BSD license

  • Requires: Python 3.5

Documentation

Features

  • Robust SQLAlchemy-asyncpg bi-translator with no hard hack

  • Asynchronous SQLAlchemy-alike engine and connection

  • Asynchronous dialect API

  • Asynchronous-friendly CRUD objective models

  • Well-considered contextual connection and transaction management

  • Reusing native SQLAlchemy core to build queries with grammar sugars

  • Support Sanic, Tornado, aiohttp and Quart

  • Rich PostgreSQL JSONB support

Installation

pip install gino

Showcase

import asyncio
from gino import Gino

db = Gino()


class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer(), primary_key=True)
    nickname = db.Column(db.Unicode(), default='noname')


async def main():
    await db.set_bind('postgresql://localhost/gino')

    # Create tables
    await db.gino.create_all()

    # Create object, `id` is assigned by database
    u1 = await User.create(nickname='fantix')
    print(u1.id, u1.nickname)  # 1 fantix

    # Returns all user objects with "d" in their nicknames
    users = await User.query.where(User.nickname.contains('d')).gino.all()
    print(users)  # [<User object>, <User object>]

    # Find one user object, None if not found
    user = await User.query.where(User.nickname == 'daisy').gino.first()
    print(user)  # <User object> or None

    # Execute complex statement and return command status
    status, result = await User.update.values(
        nickname='No.' + db.cast(User.id, db.Unicode),
    ).where(
        User.id > 10,
    ).gino.status()
    print(status)  # UPDATE 8

    # Iterate over the results of a large query in a transaction as required
    async with db.transaction():
        async for u in User.query.order_by(User.id).gino.iterate():
            print(u.id, u.nickname)


asyncio.get_event_loop().run_until_complete(main())

About The Name

About the name GINO Is Not ORM - because I don’t really like ORM (smile). GINO does perform the Object-Relational Mapping work under the Data Mapper Pattern, but it is just not a traditional ORM. The Objects in GINO are completely stateless from database - they are pure plain Python objects in memory. Changing their attribute values does not make them “dirty” - or in a different way of thinking they are always “dirty”. Any access to database must be explicitly executed. Using GINO is more like making up SQL clauses with Models and Objects, executing them to make changes in database, or loading data from database and wrapping the results with Objects again. Objects are just row data containers, you are still dealing with SQL which is represented by Models and SQLAlchemy core grammars. Besides if you don’t like ORM at all, you can use GINO without ORM:

from gino import Gino

db = Gino()

user = db.Table(
    'users', db,
    db.Column('id', db.BigInteger(), primary_key=True),
    db.Column('nickname', db.Unicode()),
)

async def main():
    async with db.with_bind('postgresql://localhost/gino'):
        users = await db.select([user]).gino.all()
        print(users)


import asyncio

asyncio.get_event_loop().run_until_complete(main())

Contribute

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

For the tests to be run, a database has to be available (please read more in CONTRIBUTING.rst)

Meanwhile, these are also very much appreciated:

Projects using GINO

  • AintQ - asyncio task queue on PostgreSQL

  • ExchangeratesAPI - Foreign exchange rates API with currency conversion

Credits

Credit goes to all contributors listed or not 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.

Special thanks to my wife Daisy and her outsourcing company DecentFoX Studio, for offering me the opportunity to build this project. We are open for global software project outsourcing on Python, iOS and Android development. And we are hiring!

GINO is developed proudly with PyCharm.

History

GINO 0.8

This is also version 1.0 release candidate.

Migrating to GINO 0.8

1. contextvars

We introduced aiocontextvars 0.2.0 which is revamped to be compatible with PEP-567 without manual interference by a few simple implicit patches. To upgrade to GINO 0.8, please remove the enable_inherit() or disable_inherit() calls, because they are the default behavior now thus no longer exist. However, you’ll need to confirm that the event loop in use is always created after importing gino or aiocontextvars, or the patch won’t work correctly.

There is nothing to worry about in Python 3.7.

2. none_as_none

When GINO tries to load a row with all NULL values into an instance, it will now by default return None instead of an instance with all None attributes. To recover the default behavior of 0.7, please specify none_as_none(False) in affected model loader.

This is especially applicable to relationship sub-loaders - if the sub-loader found it all NULL, no instance will be set to parent instance. For example:

child = await Child.load(parent=Parent).query.gino.first()

If child.parent_id is NULL in database, then the child instance won’t be called with any setattr(child, 'parent', ...) at all. (If you need child.parent == None in this case, consider setting default value parent = None in child model.)

Please note, it is deprecated to disable none_as_none, and disabling will be removed in GINO 1.0.

0.8.7 (TBD)

  • Improved error handling when attribute names collide (Contributed by Reskov in #637 #638)

  • Fixed with_bind usability in aiohttp extension (#518)

0.8.6 (2020-02-10)

  • Fixed JSONPathType bind processor for asyncpg (#609)

  • Allowed for primary keys with different names from the database columns (Contributed by Tiago Requeijo in #599 #600)

  • Allowed to use simple callable instead of property for one-2-many loading (Contributed by Olexiy in #629)

  • Added distinct() to Alias (#628)

0.8.5 (2019-11-19)

  • Improved support for __tablename__ in declared_attr (Contributed by Roald Storm in #592)

0.8.4 (2019-11-09)

  • Better loader support for models in subqueries (#573 #585)

  • Allowed __tablename__ to be a declared_attr (#579 #582)

  • Fixed Sanic 19.9.0 compatibility (#569)

  • Added one() and one_or_none() (Contributed by Ilaï Deutel in #577)

  • Improved Starleet extension compatibility (Contributed by Jim O’Brien in #538)

  • Fixed Starlette connection release during exceptions issue (Contributed by qulaz in #533)

  • Fixed server event compatibility with Sanic 19.6.2 (Contributed by Julio Lacerda in #520)

  • Fixed Grammar (Contributed by Simeon J Morgan in #504)

0.8.3 (2019-06-06)

  • Fixed deprecated warnings in asyncpg dialect and aiohttp (#425)

  • Customizable db attribute name in aiohttp app instance (#457)

  • Added Starlette support (#486)

0.8.2 (2019-03-07)

  • Added exception for unknown JSON properties (#406 #408)

  • Supported Quart 0.7 (#411)

  • Accepted kwargs for db init in extensions (#407 #427)

  • Added custom config parameter to aiohttp middleware (Contributed by Michał Dziewulski in #440)

  • Added NullPool (#437 #441)

  • Unpinned dependency versions (#447)

  • Added support for SQLAlchemy 1.3 (#433 #451)

0.8.1 (2018-12-08)

  • Alias supported Label (#365)

  • Docs update (#308, 4c59ad, #401 by Pascal van Kooten)

  • Version requirement for SQLAlchemy is updated to >=1.2 (#378 #382)

  • Added option for SSL in aiohttp extension (Contributed by Martin Zaťko in #387 #393) * And all other extensions (#395)

  • Supported Tornado 5 (#396, also thanks to Vladimir Goncharov)

  • Fixed custom JSON/JSONB type support (#402 #403)

(Most fixes done by Tony Wang)

0.8.0 (2018-10-16)

  • Welcome Tony Wang to the maintenance team (#335)

  • Allowed custom column names (#261 #297)

  • Allowed column instance in model.load() (Contributed by Jekel in #323)

  • [Breaking] Upgraded to aiocontextvars 0.2.0 (#333)

  • Fixed bug that the same empty stack is shared between sub-tasks (#313 #334)

  • [Breaking] Made none_as_none() the default behavior (#351)

  • Bug fixes and docs update

GINO 0.7

This is also version 1.0 beta 3.

0.7.7 (2018-12-08)

  • Backported fix for custom JSON/JSONB type support (#402 #403)

0.7.6 (2018-08-26)

  • Updated library support (Contributed by Tony Wang in #275 #309)

  • Added none_as_none() (#281 #282)

  • Added ARRAY alias in asyncpg dialect module (Contributed by Mykyta Holubakha in #289)

  • Added Model.lookup() to prevent updating whole table without primary key (#287 #288)

  • Added DB_ECHO in extension options (Contributed by Mykyta Holubakha in #298)

  • Fixed broken JSON/JSONB result processor since 0.5.8 (Contributed by Tony Wang in #291 #305)

  • Removed bad rollback after a failing commit (Contributed by Tony Wang in #302 #304)

  • Fixed to raise UninitializedError if bind is None (Contributed by Tony Wang in #307 #310)

0.7.5 (2018-07-26)

  • Added friendly error message when using abstract models by mistake (#224)

  • Supported Python 3.7 (Contributed by Tony Wang in #265)

  • Updated documentation

  • Fixed a bug in TupleLoader (Contributed by Pavol Vargovcik in #279 #280)

0.7.4 (2018-06-10)

  • Added aiocontextvars as required dependency required for Python 3.5 and 3.6 (#228)

  • Added Quart support (#213)

  • Fixed Tornado options parsing (#231)

  • Improved coding style and test coverage

0.7.3 (2018-05-19)

  • Fix for failing binary type (#225)

0.7.2 (2018-05-15)

  • Added prepared statement support (#14)

  • Added dsn in extension config (Contributed by Yurii Shtrikker in #215)

0.7.1 (2018-05-03)

  • Added support for inline model constraints (Contributed by Kinware in #198)

  • Added docs and tests for using SSL (#202)

  • Added declared_attr (#204)

  • Allowed ModelLoader passively load partial model (#216)

0.7.0 (2018-04-18)

  • Added Python 3.5 support (#187)

  • Added support to use dict as ident for Model.get (#192)

  • Added result loader (partial relationship support) (#13)

  • Added documentation on relationship and transaction (#146)

GINO 0.6

This is also version 1.0 beta 2.

Migrating to GINO 0.6

1. Task Local

We created a new Python package aiocontextvars from previous local.py. If you made use of the task local features, you should install this package.

Previous gino.enable_task_local() and gino.disable_task_local() are replaced by aiocontextvars.enable_inherit() and aiocontextvars.disable_inherit(). However in GINO 0.5 they controls the whole task local feature switch, while aiocontextvars by default offers task local even without enable_inherit(), which controls whether the local storage should be passed between chained tasks. When enabled, it behaves the same as enabled in 0.5, but you cannot completely turn off the task local feature while aiocontextvars is installed.

There is no gino.get_local() and gino.reset_local() relevant in aiocontextvars. The similar thing is aiocontextvars.ContextVar instance through its get(), set() and delete() methods.

Previous gino.is_local_root() is now not aiocontextvars.Context.current().inherited.

2. Engine

GINO 0.6 hides asyncpg.Pool behind the new SQLAlchemy-alike gino.GinoEngine. Instead of doing this in 0.5:

async with db.create_pool('postgresql://...') as pool:
    # your code here

You should change it to this in 0.6:

async with db.with_bind('postgresql://...') as engine:
    # your code here

This equals to:

engine = await gino.create_engine('postgresql://...')
db.bind = engine
try:
    # your code here
finally:
    db.bind = None
    await engine.close()

Or:

engine = await db.set_bind('postgresql://...')
try:
    # your code here
finally:
    await db.pop_bind().close()

Or even this:

db = await gino.Gino('postgresql://...')
try:
    # your code here
finally:
    await db.pop_bind().close()

Choose whichever suits you the best.

Obviously GinoEngine doesn’t provide asyncpg.Pool methods directly any longer, but you can get the underlying asyncpg.Pool object through engine.raw_pool property.

GinoPool.get_current_connection() is now changed to current_connection property on GinoEngine instances to support multiple engines.

GinoPool.execution_option is gone, instead update_execution_options() on GinoEngine instance is available.

GinoPool().metadata is gone, dialect is still available.

GinoPool.release() is removed in GinoEngine and Gino, the release() method on GinoConnection object should be used instead.

These methods exist both in 0.5 GinoPool and 0.6 GinoEngine: close(), acquire(), all(), first(), scalar(), status().

3. GinoConnection

Similarly, GinoConnection in 0.6 is no longer a subclass of asyncpg.Connection, instead it has a asyncpg.Connection instance, accessable through GinoConnection.raw_connection property.

GinoConnection.metadata is deleted in 0.6, while dialect remained.

GinoConnection.execution_options() is changed from a mutable dict in 0.5 to a method returning a copy of current connection with the new options, the same as SQLAlchemy behavior.

GinoConnection.release() is still present, but its default behavior has been changed to permanently release this connection. You should add argument permanent=False to remain its previous behavior.

And all(), first(), scalar(), status(), iterate(), transaction() remained in 0.6.

4. Query API

All five query APIs all(), first(), scalar(), status(), iterate() now accept the same parameters as SQLAlchemy execute(), meaning they accept raw SQL text, or multiple sets of parameters for “executemany”. Please note, if the parameters are recognized as “executemany”, none of the methods will return anything. Meanwhile, they no longer accept the parameter bind if they did. Just use the API on the GinoEngine or GinoConnection object instead.

5. Transaction

Transaction interface is rewritten. Now in 0.6, a GinoTransaction object is provided consistently from all 3 methods:

async with db.transaction() as tx:
    # within transaction

async with engine.transaction() as tx:
    # within transaction

async with engine.acquire() as conn:
    async with conn.transaction() as tx:
        # within transaction

And different usage with await:

tx = await db.transaction()
try:
    # within transaction
    await tx.commit()
except:
    await tx.rollback()
    raise

The GinoConnection object is available at tx.connection, while underlying transaction object from database driver is available at tx.transaction - for asyncpg it is an asyncpg.transaction.Transaction object.

0.6.6 (2018-05-18)

  • Backported a fix for failing binary type (#225)

0.6.5 (2018-04-18)

  • Abandoned 0.6.4 and keep 0.6.x stable

  • Backported doc for transaction

0.6.4 (2018-04-16)

Abandoned version, please use 0.7.0 instead.

0.6.3 (2018-04-08)

  • Added aiohttp support

  • Added support for calling create() on model instances (Contributed by Kinware in #178 #180)

  • Fixed get() by string, and misc environment issues (Contributed by Tony Wang in #191 193 #183 #184)

0.6.2 (2018-03-24)

  • Fixed SQLAlchemy prefetch issue (#141)

  • Fixed issue that mixin class on Model not working (#174)

  • Added more documentation (Thanks Olaf Conradi for reviewing)

0.6.1 (2018-03-18)

  • Fixed create and drop for Enum type (#160)

  • A bit more documentation (#159)

0.6.0 (2018-03-14)

  • [Breaking] API Refactored, Pool replaced with Engine

    • New API Engine replaced asyncpg Pool (#59)

    • Supported different dialects, theoretically

    • Used aiocontextvars instead of builtin task local (#89)

  • [Breaking] Fixed query API with multiparams (executemany) to return correctly (#20)

  • [Breaking] The query methods no longer accept the parameter bind

  • [Breaking] Gino no longer exposes postgresql types

  • Added echo on engine (#142)

  • Added tests to cover 80% of code

  • Added gino extension on SchemaItem for create_all and so on (#76 #106)

  • Added gino extension on model classes for create() or drop()

  • Added _update_request_cls on CRUDModel (#147)

  • Rewrote the documentation (#146)

GINO 0.5

This is also version 1.0 beta 1.

0.5.8 (2018-02-14)

  • Preparing for 0.6.0 which will be a breaking release

  • Fixed wrong value of Enum in creation (Contributed by Sergey Kovalev in #126)

0.5.7 (2017-11-24)

This is an emergency fix for 0.5.6.

  • Fixed broken lazy connection (Contributed by Ádám Barancsuk in #114)

  • Added Model.outerjoin

0.5.6 (2017-11-23)

  • Changed to use unnamed statement when possible (#80 #90)

  • Added more example (Contributed by Kentoseth in #109)

  • Added Model.join and made Model selectable (Contributed by Ádám Barancsuk in #112 #113)

0.5.5 (2017-10-18)

  • Ensured clean connection if transaction acquire fails (Contributed by Vladimir Goncharov in #87)

  • Added ability to reset local storage (#84)

  • Fixed bug in JSON property update

  • Added update chaining feature

0.5.4 (2017-10-04)

  • Updated example (Contributed by Kinware in #75)

  • Added Model.insert (Contributed by Neal Wang in #63)

  • Fixed issue that non-lazy acquiring fails dirty (#79)

0.5.3 (2017-09-23)

  • Fixed no module named cutils error (Contributed by Vladimir Goncharov in #73)

0.5.2 (2017-09-10)

  • Added missing driver name on dialect (#67)

  • Fixed dialect to support native decimal type (#67)

0.5.1 (2017-09-09)

This is an emergency fix for 0.5.0.

  • Reverted the extension, back to pure Python (#60)

  • Used SQLAlchemy RowProxy

  • Added first_or_404

  • Fixed bug that GinoPool cannot be inherited

0.5.0 (2017-09-03)

  • [Breaking] Internal refactor: extracted and isolated a few modules, partially rewritten

    • Extracted CRUD operations

    • Core operations are moved to dialect and execution context

    • Removed guess_model, switched to explicit execution options

    • Turned timeout parameter to an execution option

    • Extracted pool, connection and api from asyncpg_delegate

  • Added support for SQLAlchemy execution options, and a few custom options

  • [Breaking] Made Model.select return rows by default (#39)

  • Moved get_or_404 to extensions (#38)

  • Added iterator on model classes (#43)

  • Added Tornado extension (Contributed by Vladimir Goncharov)

  • Added Model.to_dict (#47)

  • Added an extension module to update asyncpg.Record with processed results

Early Development Releases

Considered as alpha releases.

0.4.1 (2017-08-20)

  • Support select on model instance

0.4.0 (2017-08-15)

  • Made get_or_404 more friendly when Sanic is missing (Contributed by Neal Wang in #23 #31)

  • Delegated sqlalchemy.__all__ (Contributed by Neal Wang in #10 #33)

  • [Breaking] Rewrote JSON/JSONB support (#29)

  • Added lazy parameter on db.acquire (Contributed by Binghan Li in #32)

  • Added Sanic integration (Contributed by Binghan Li, Tony Wang in #30 #32 #34)

  • Fixed iterate API to be compatible with asyncpg (#32)

  • Unified exceptions

  • [Breaking] Changed update API (#29)

  • Bug fixes

0.3.0 (2017-08-07)

  • Supported __table_args__ (#12)

  • Introduced task local to manage connection in context (#19)

  • Added query.gino extension for in-place execution

  • Refreshed README (#3)

  • Adopted PEP 487 (Contributed by Tony Wang in #17 #27)

  • Used weakref on __model__ of table and query (Contributed by Tony Wang)

  • Delegated asyncpg timeout parameter (Contributed by Neal Wang in #16 #22)

0.2.3 (2017-08-04)

  • Supported any primary key (Contributed by Tony Wang in #11)

0.2.2 (2017-08-02)

  • Supported SQLAlchemy result processor

  • Added rich support on JSON/JSONB

  • Bug fixes

0.2.1 (2017-07-28)

  • Added update and delete API

0.2.0 (2017-07-28)

  • Changed API, no longer reuses asyncpg API

0.1.1 (2017-07-25)

  • Added db.bind

  • API changed: parameter conn renamed to optional bind

  • Delegated asyncpg Pool with db.create_pool

  • Internal enhancement and bug fixes

0.1.0 (2017-07-21)

  • First release on PyPI.

Project details


Download files

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

Source Distribution

gino-0.8.7.tar.gz (219.0 kB view hashes)

Uploaded Source

Built Distribution

gino-0.8.7-py3-none-any.whl (61.2 kB view hashes)

Uploaded Python 3

Supported by

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