Package: aerie
AerieA wrapper around SQLAlchemy made to support asynchronous workloads.Aerie- is anavariel(or winged elf) from Baldur's Gate II game.InstallationInstallaerieusing PIP or poetry:pipinstallaerie[postgresql]# orpoetryaddaerie[postgresql]For SQLite use "sqlite" extra. To install all drivers use "full" extra.Featuresfull async/await supportplain SQL with bound paramsSQLAlchemy query builders supportSQLAlchemy ORM supportpaginationTODOsimplify column definition:sa.Column(sa.Integer)->models.IntergerField()integrate with Alembic CLIQuick startSee example application inexamples/directory of this repository.UsageAs this library is based onSQLAlchemy, it is strictly recommended
getting yourself familiar with it.A general usage is:create an instance of Aeriedefine ORM modelscreate tables in the database (or, preferably, use Alembic migrations)obtain a session and perform database queriesAerie instanceCreate an instance of Aerie class and pass a connection string to it:fromaerieimportAeriedb=Aerie('sqlite+aiosqlite:///tmp/database.sqlite2')# ordb=Aerie('postgresql+asyncpg://postgres:postgres@localhost/aerie')You need appropriate driver installed. Add "aiosqlite" for SQLite support, or add "asyncpg" for PostreSQL support.Raw SQL queriesAt this step Aerie is ready to work. Create a new transaction and execute any query you need.fromsqlalchemy.sqlimporttext# assuming "users" table existssql=text('select * from users where user_id = :user_id')rows=awaitdb.query(sql,{'user_id':1}).all()Full listingexamples/raw_sql.pyUsing query builderSure, you are not limited to plain SQL. SQLAlchemy query builders also supported (because Aerie is a tiny layer on top
of the SQLAlchemy)fromsqlalchemy.sqlimporttextimportsqlalchemyassafromaerieimportmetadatausers=sa.Table('users',metadata,sa.Column(sa.Integer,name='id',primary_key=True),sa.Column(sa.String,name='name'),)# create tablesawaitdb.schema.create_tables()stmt=select(users).where(users.c.id==2)rows=awaitdb.query(stmt).all()Full listingexamples/tables.pyUsing ORM models and sessionsAnother option to low-level query builder are ORM models. Aerie providesaerie.Modelclass that you should extend to
create your model.fromaerieimportBaseimportsqlalchemyassaclassUser(Base):__tablename__='users'id=sa.Column(sa.Integer,primary_key=True)name=sa.Column(sa.String)# create tablesawaitdb.schema.create_tables()asyncwithdb.session()assession:session.add_all([User(id=1,name='One'),User(id=2,name='Two'),User(id=3,name='Three'),])awaitsession.flush()# get first user in the row setuser=awaitsession.query(User).first()Make sure the module with models is imported before you create tables.
Otherwise they will not be added to themetadataand, thus, not created.Full listingexamples/orm.pyPaginationAerie's DbSession ships with pagination utilities out of the box. When you need to paginate a query just
callDbSession.paginatemethod.asyncwithdb.session()assession:page=awaitsession.query(User).paginate(page=1,page_size=10)foruserinpage:print(user)print('Next page:%s'%page.next_page)print('Previous page:%s'%page.previous_page)print('Displaying items:%s-%s'%(page.start_index,page.end_index))The page object has more helper attributes:PropertyTypeDescriptiontotal_pagesintTotal pages in the row set.has_nextboolTest if the next page is available.has_previousboolTest if the previous page is available.has_otherboolTest if there are another pages except current one.next_pageintNext page number. Always returns an integer. If there is no more pages the current page number returned.previous_pageintPrevious page number. Always returns an integer. If there is no previous page, the number 1 returned.start_indexintThe 1-based index of the first item on this page.end_indexintThe 1-based index of the last item on this page.total_rowsintTotal rows in result set.Alembic migrationsAlembic usage is well documented in the official
docs:Using Asyncio with AlembicNote, you need to useaerie.metadatawhen you configuretarget_metadataoption:# migrations/env.pyfromaerieimportmetadatatarget_metadata=metadataAlso, don't forget to import all your models in Alembic'senv.pyfile so their structure is fully loaded and no models
forgotten.Shared instancesYou can configure Aerie to populate Aerie.instances class-level variable, so you can access database instances from
anywhere of your code. For that, just passnameargument to Aerie constructor.# migrations/env.pyfromaerieimportAeriedb=Aerie(name='shared',...)# other filedb=Aerie.get_instance('shared')Note, instances without name cannot be shared.