Skip to content

Commit

Permalink
Merge branch 'release/0.1a4'
Browse files Browse the repository at this point in the history
  • Loading branch information
ri-gilfanov committed Apr 19, 2021
2 parents 1ed9672 + 629655a commit ef5cf13
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 112 deletions.
49 changes: 36 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,50 @@ Install

Example
-------
Install aiosqlite for work with sqlite3: ::

pip install aiosqlite

Run this code:

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_engine, sa_middleware
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_engine, sa_middleware
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import create_async_engine
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
routes = web.RouteTableDef()
class Request(Base):
__tablename__ = 'requests'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
async def main(request):
async with request.app['sa_main'].begin() as conn:
await conn.run_sync(Base.metadata.create_all)
@routes.get('/')
async def main(request):
async with request['sa_main'].begin():
# some code
request['sa_main'].add_all([Request()])
result = await request['sa_main'].execute(sa.select(Request))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
app = web.Application(middlewares=[sa_middleware()])
aiohttp_sqlalchemy.setup(app, [sa_engine('sqlite+aiosqlite:///')])
app.add_routes(routes)
web.run_app(app)
app = web.Application(middlewares=[sa_middleware()])
engine = create_async_engine('sqlite+aiosqlite:///')
aiohttp_sqlalchemy.setup(app, [sa_engine(engine)])
app.add_routes([web.get('/', main)])
web.run_app(app)
Documentation
-------------

See: https://aiohttp-sqlalchemy.readthedocs.io/

18 changes: 9 additions & 9 deletions aiohttp_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from aiohttp.abc import AbstractView
from asyncio import iscoroutinefunction
from functools import wraps
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.ext.asyncio import AsyncSession
from typing import TYPE_CHECKING

if TYPE_CHECKING:
Expand All @@ -11,24 +11,24 @@
from typing import Callable, Iterable, Tuple


__version__ = '0.1a3'
__version__ = '0.1a4'


def sa_decorator(key: str = 'sa_main'):
def wrapper(handler):
@wraps(handler)
async def wrapped(*args, **kwargs):
# Class based view decorating
if issubclass(handler, AbstractView):
if isinstance(args[0], AbstractView):
request = args[0].request
async with AsyncSession(request.app[key]) as request[key]:
return await handler(request)
return await handler(*args, **kwargs)

# Coroutine function decorating
elif iscoroutinefunction(handler):
request = args[0]
async with AsyncSession(request.app[key]) as request[key]:
return await handler(request)
return await handler(*args, **kwargs)

else:
raise TypeError('Unsupported handler type')
Expand All @@ -45,10 +45,10 @@ async def sa_middleware_(request: 'Request', handler: 'Callable') -> 'StreamResp
return sa_middleware_


def sa_engine(key: str = 'sa_main', *args, **kwargs) -> 'Tuple[str, AsyncEngine]':
return key, create_async_engine(*args, **kwargs)
def sa_engine(engine: 'AsyncEngine', key: str = 'sa_main') -> 'Tuple[AsyncEngine, str]':
return engine, key


def setup(app: 'Application', engines: 'Iterable[Tuple[str, AsyncEngine]]'):
for app_key, engine in engines:
def setup(app: 'Application', engines: 'Iterable[Tuple[AsyncEngine, str]]'):
for engine, app_key in engines:
app[app_key] = engine
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Ruslan Ilyasovich Gilfanov'

# The full version, including alpha/beta/rc tags
release = '0.1a3'
release = '0.1a4'


# -- General configuration ---------------------------------------------------
Expand Down
88 changes: 37 additions & 51 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,130 +23,116 @@ Install
pip install aiohttp-sqlalchemy


Middleware approach
-------------------
Using middlewares is preferred for most users.

Single middleware
-----------------
'''''''''''''''''

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_engine, sa_middleware
routes = web.RouteTableDef()
@routes.get('/')
async def main(request):
async with request['sa_main'].begin():
# some code
app = web.Application(middlewares=[sa_middleware()])
aiohttp_sqlalchemy.setup(app, [sa_engine('sqlite+aiosqlite:///')])
app.add_routes(routes)
web.run_app(app)
Multiple middlewares
--------------------
''''''''''''''''''''

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_engine, sa_middleware
routes = web.RouteTableDef()
@routes.get('/')
async def main(request):
async with request['sa_primary'].begin():
async with request['sa_main'].begin():
# some code
async with request['sa_secondary'].begin():
# some code
middlewares = [sa_middleware('sa_primary'), sa_middleware('sa_secondary')]
app = web.Application(middlewares=middlewares)
app = web.Application(middlewares=[sa_middleware(), sa_middleware('sa_secondary')])
aiohttp_sqlalchemy.setup(app, [
sa_engine('sa_primary', 'sqlite+aiosqlite:///'),
sa_engine('sa_secondary', 'sqlite+aiosqlite:///'),
])
app.add_routes(routes)
web.run_app(app)
Decorators instead middlewares
------------------------------
Decorator approach
------------------
But you can use decorators instead of middlewares.

Decorated coroutine function
''''''''''''''''''''''''''''

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_decorator, sa_engine, sa_middleware
routes = web.RouteTableDef()
@routes.get('/')
@sa_decorator()
async def main(request):
async with request['sa_main'].begin():
# some code
@routes.view('/other_url')
@sa_decorator()
app = web.Application()
aiohttp_sqlalchemy.setup(app, [sa_engine('sqlite+aiosqlite:///')])
Decorated class based view
''''''''''''''''''''''''''

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_decorator, sa_engine, sa_middleware
class ClassBasedView(web.View):
@sa_decorator()
async def get(self):
async with request['sa_main'].begin():
# some code
app = web.Application()
aiohttp_sqlalchemy.setup(app, [sa_engine('sqlite+aiosqlite:///')])
app.add_routes(routes)
web.run_app(app)
Hybrid approach
---------------
And you can combine the middleware approach and the decorator approach.

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_decorator, sa_engine, sa_middleware
from sqlalchemy.ext.asyncio import create_async_engine
routes = web.RouteTableDef()
@routes.get('/')
@sa_decorator('sa_secondary')
async def main(request):
async with request['sa_main'].begin():
# some code
@routes.view('/url_2')
class ClassBasedView(web.View):
async def get(self):
async with request['sa_secondary'].begin():
# some code
# some your code
@routes.get('/url_3')
@sa_decorator('sa_tertiary')
async def main(request):
async with request['sa_tertiary'].begin():
# some code
middlewares = [sa_middleware(), sa_middleware('sa_secondary')]
app = web.Application(middlewares=middlewares)
app = web.Application(middlewares=[
sa_middleware(),
])
aiohttp_sqlalchemy.setup(app, [
sa_engine('sqlite+aiosqlite:///'),
sa_engine('sa_secondary', 'sqlite+aiosqlite:///'),
sa_engine('sa_tertiary', 'sqlite+aiosqlite:///'),
sa_engine(create_async_engine('sqlite+aiosqlite:///')),
sa_engine(create_async_engine('sqlite+aiosqlite:///'), 'sa_secondary'),
])
aiohttp_sqlalchemy.setup(app, [sa_engine('sqlite+aiosqlite:///')])
app.add_routes(routes)
web.run_app(app)
Indices and tables
==================
------------------

* :ref:`genindex`
* :ref:`modindex`
Expand Down
Empty file added examples/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions examples/hybrid_approach.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_decorator, sa_engine, sa_middleware
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import create_async_engine
from random import choice


metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)


class Request(Base):
__tablename__ = 'requests'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)


@sa_decorator('sa_secondary')
async def main(request):
async with request.app['sa_main'].begin() as conn:
await conn.run_sync(Base.metadata.create_all)

async with request.app['sa_secondary'].begin() as conn:
await conn.run_sync(Base.metadata.create_all)

session = choice(['sa_main', 'sa_secondary'])

async with request[session].begin():
request[session].add_all([Request()])

async with request['sa_main'].begin():
result = await request['sa_main'].execute(sa.select(Request))
main_result = {r.id: r.timestamp.isoformat() for r in result.scalars()}

async with request['sa_secondary'].begin():
result = await request['sa_secondary'].execute(sa.select(Request))
secondary_result = {r.id: r.timestamp.isoformat() for r in result.scalars()}

data = {
'main': main_result,
'secondary': secondary_result,
}
return web.json_response(data)


app = web.Application(middlewares=[
sa_middleware(),
])
aiohttp_sqlalchemy.setup(app, [
sa_engine(create_async_engine('sqlite+aiosqlite:///')),
sa_engine(create_async_engine('sqlite+aiosqlite:///'), 'sa_secondary'),
])
app.add_routes([web.get('/', main)])
web.run_app(app)

0 comments on commit ef5cf13

Please sign in to comment.