Skip to content

Commit

Permalink
Merge branch 'release/0.7.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ri-gilfanov committed Jun 7, 2021
2 parents 165e54a + 04f7989 commit 2cb6860
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 111 deletions.
5 changes: 3 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Copy and paste this code in a file and run:
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
metadata = sa.MetaData()
Expand All @@ -63,7 +63,8 @@ Copy and paste this code in a file and run:
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
aiohttp_sqlalchemy.setup(app, [sa_bind(engine)])
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
app.add_routes([web.get('/', main)])
web.run_app(app)
25 changes: 14 additions & 11 deletions aiohttp_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,37 @@

if TYPE_CHECKING:
from aiohttp.web import Application
from typing import Iterable, Union, Tuple
from typing import Callable, Iterable, Union, Tuple

TSABinding = Tuple[Union[AsyncEngine, sessionmaker], str, bool]
TSessionFactory = Callable[..., AsyncSession]
TEngineOrFactory = Union[AsyncEngine, TSessionFactory]
TSABinding = Tuple[TSessionFactory, str, bool]


__version__ = '0.6.0'
__version__ = '0.7.0'

__all__ = ['DuplicateAppKeyError', 'DuplicateRequestKeyError', 'SABaseView',
'sa_bind', 'sa_decorator', 'sa_middleware', 'SAView', 'setup',]


def sa_bind(
arg: 'Union[AsyncEngine, sessionmaker]',
key: str = DEFAULT_KEY,
*,
middleware: bool = True,
) -> 'TSABinding':
""" AsyncEngine wrapper for binding in setup function. """
arg: 'TEngineOrFactory', key: str = DEFAULT_KEY, *,
middleware: bool = True) -> 'TSABinding':
""" Session factory wrapper for binding in setup function. """

if isinstance(arg, AsyncEngine):
arg = sessionmaker(arg, AsyncSession)

return arg, key, middleware


def setup(app: 'Application', bindings: 'Iterable[TSABinding]'):
""" Setup function for binding SQLAlchemy engines. """
for arg, key, middleware in bindings:
for Session, key, middleware in bindings:
if key in app:
raise DuplicateAppKeyError(key)

app[key] = arg
app[key] = Session

if middleware:
app.middlewares.append(sa_middleware(key))
10 changes: 3 additions & 7 deletions aiohttp_sqlalchemy/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,9 @@ async def wrapped(*args, **kwargs) -> 'StreamResponse':
if key in request:
raise DuplicateRequestKeyError(key)

arg = request.config_dict.get(key)
if isinstance(arg, AsyncEngine):
async with AsyncSession(arg) as request[key]:
return await handler(*args, **kwargs)
elif isinstance(arg, sessionmaker):
async with arg() as request[key]:
return await handler(*args, **kwargs)
Session = request.config_dict.get(key)
async with Session() as request[key]:
return await handler(*args, **kwargs)

return wrapped
return wrapper
10 changes: 3 additions & 7 deletions aiohttp_sqlalchemy/middlewares.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ async def sa_middleware_(request: 'Request', handler: 'Callable')\
if key in request:
raise DuplicateRequestKeyError(key)

arg = request.config_dict.get(key)
if isinstance(arg, AsyncEngine):
async with AsyncSession(arg) as request[key]:
return await handler(request)
elif isinstance(arg, sessionmaker):
async with arg() as request[key]:
return await handler(request)
Session = request.config_dict.get(key)
async with Session() as request[key]:
return await handler(request)

return sa_middleware_
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.6.0'
release = '0.7.0'


# -- General configuration ---------------------------------------------------
Expand Down
98 changes: 57 additions & 41 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,43 +40,44 @@ Copy and paste this code in a file and run:

.. code-block:: python
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_bind, sa_middleware
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import create_async_engine
from aiohttp import web
import aiohttp_sqlalchemy
from aiohttp_sqlalchemy import sa_bind, sa_middleware
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
class MyModel(Base):
__tablename__ = 'my_table'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
class MyModel(Base):
__tablename__ = 'my_table'
id = sa.Column(sa.Integer, primary_key=True)
timestamp = sa.Column(sa.DateTime(), default=datetime.now)
async def main(request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def main(request):
async with request['sa_main'].bind.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async with request['sa_main'].begin():
request['sa_main'].add_all([MyModel()])
result = await request['sa_main'].execute(sa.select(MyModel))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
async with request['sa_main'].begin():
request['sa_main'].add_all([MyModel()])
result = await request['sa_main'].execute(sa.select(MyModel))
data = {r.id: r.timestamp.isoformat() for r in result.scalars()}
return web.json_response(data)
app = web.Application()
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
aiohttp_sqlalchemy.setup(app, [sa_bind(engine)])
engine = create_async_engine('sqlite+aiosqlite:///')
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_bind(Session)])
app.add_routes([web.get('/', main)])
web.run_app(app)
app.add_routes([web.get('/', main)])
web.run_app(app)
SQLAlchemy and Asyncio
Expand All @@ -93,10 +94,14 @@ Binding multiple engines
second_engine = create_async_engine('mysql+aiomysql://user:password@host/database')
third_engine = create_async_engine('sqlite+aiosqlite:///')
MainSession = orm.sessionmaker(main_engine, AsyncSession)
SecondSession = orm.sessionmaker(second_engine, AsyncSession)
ThirdSession = orm.sessionmaker(third_engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [
sa_bind(main_engine),
sa_bind(second_engine, 'sa_second'),
sa_bind(third_engine, 'sa_third'),
sa_bind(MainSession),
sa_bind(SecondSession, 'sa_second'),
sa_bind(ThirdSession, 'sa_third'),
])
Expand All @@ -114,11 +119,9 @@ Class based views
async with self.sa_session('sa_second').begin():
# some your code
main_engine = create_async_engine('sqlite+aiosqlite:///')
second_engine = create_async_engine('sqlite+aiosqlite:///')
aiohttp_sqlalchemy.setup(app, [
sa_bind(main_engine),
sa_bind(second_engine, 'sa_second'),
sa_bind(MainSession),
sa_bind(SecondSession, 'sa_second'),
])
Expand All @@ -132,18 +135,17 @@ Decorating handlers

.. code-block:: python
@sa_decorator('sa_fourth')
@sa_decorator('sa_optional')
async def handler(request):
# some your code
class Handler(SAView):
@sa_decorator('sa_fourth')
@sa_decorator('sa_optional')
async def get(self):
# some your code
engine = create_async_engine('sqlite+aiosqlite:///')
aiohttp_sqlalchemy.setup(app, [
sa_bind(engine, 'sa_fourth', middleware=False),
sa_bind(Session, 'sa_optional', middleware=False),
])
Expand All @@ -162,7 +164,8 @@ Nested apps
app = web.Application()
engine = create_async_engine('sqlite+aiosqlite:///')
aiohttp_sqlalchemy.setup(app, [sa_engine(engine)])
Session = orm.sessionmaker(engine, AsyncSession)
aiohttp_sqlalchemy.setup(app, [sa_engine(Session)])
subapp = web.Application()
subapp.add_routes([web.get('', main)])
Expand All @@ -172,6 +175,19 @@ Nested apps
Change log
----------
Version 0.7.0
^^^^^^^^^^^^^
Changed
"""""""
Usage ``sqlalchemy.orm.sessionmaker`` object is recomended as a first argument
of ``aiohttp_sqlalchemy.sa_bind()``. See examples on documetation.

Removed
"""""""
Removed support of ``request.config_dict.get('sa_main')`` and
``request.app['sa_main']`` expressions. Use a ``request['sa_main'].bind``
expression.

Version 0.6.0
^^^^^^^^^^^^^
Added
Expand All @@ -186,9 +202,9 @@ sessionmaker]`` in ``sa_bind()`` signature.

Deprecated
""""""""""
Expressions ``request.config_dict.get('sa_main')`` and
``request.app['sa_main']`` is deprecated. Use expression
``request['sa_main'].bind``.
Deprecated support of ``request.config_dict.get('sa_main')`` and
``request.app['sa_main']`` expressions. Use a ``request['sa_main'].bind``
expression.

Removed
"""""""
Expand Down
21 changes: 12 additions & 9 deletions examples/hybrid_approach.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from random import choice


Expand All @@ -18,15 +18,15 @@ class Request(Base):
timestamp = sa.Column(sa.DateTime(), default=datetime.now)


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

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

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

async with request[session].begin():
request[session].add_all([Request()])
Expand All @@ -35,8 +35,8 @@ async def main(request):
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))
async with request['sa_second'].begin():
result = await request['sa_second'].execute(sa.select(Request))
secondary_result = {r.id: r.timestamp.isoformat() for r in result.scalars()}

data = {
Expand All @@ -49,11 +49,14 @@ async def main(request):
app = web.Application()

main_engine = create_async_engine('sqlite+aiosqlite:///')
secondary_engine = create_async_engine('sqlite+aiosqlite:///')
second_engine = create_async_engine('sqlite+aiosqlite:///')

MainSession = orm.sessionmaker(main_engine, AsyncSession)
SecondSession = orm.sessionmaker(second_engine, AsyncSession)

aiohttp_sqlalchemy.setup(app, [
sa_bind(main_engine),
sa_bind(secondary_engine, 'sa_secondary', middleware=False),
sa_bind(MainSession),
sa_bind(SecondSession, 'sa_second', middleware=False),
])

app.add_routes([web.get('/', main)])
Expand Down
21 changes: 12 additions & 9 deletions examples/multiple_decorated_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import datetime
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from random import choice


Expand All @@ -19,15 +19,15 @@ class Request(Base):


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

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

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

async with request[session].begin():
request[session].add_all([Request()])
Expand All @@ -36,8 +36,8 @@ async def main(request):
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))
async with request['sa_second'].begin():
result = await request['sa_second'].execute(sa.select(Request))
secondary_result = {r.id: r.timestamp.isoformat() for r in result.scalars()}

data = {
Expand All @@ -50,11 +50,14 @@ async def main(request):
app = web.Application()

main_engine = create_async_engine('sqlite+aiosqlite:///')
secondary_engine = create_async_engine('sqlite+aiosqlite:///')
second_engine = create_async_engine('sqlite+aiosqlite:///')

MainSession = orm.sessionmaker(main_engine, AsyncSession)
SecondSession = orm.sessionmaker(second_engine, AsyncSession)

aiohttp_sqlalchemy.setup(app, [
sa_bind(main_engine, middleware=False),
sa_bind(secondary_engine, 'sa_secondary', middleware=False),
sa_bind(MainSession, middleware=False),
sa_bind(SecondSession, 'sa_second', middleware=False),
])

app.add_routes([web.get('/', main)])
Expand Down

0 comments on commit 2cb6860

Please sign in to comment.