From a5b75e03c00d09e1b1fe54f1051a74217f93ed0a Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Fri, 22 Jul 2016 14:56:23 +0200 Subject: [PATCH] Refactor demo (#981) * Refactor demo code * Update docs --- demos/polls/aiohttpdemo_polls/db.py | 64 +++++++++++++++---------- demos/polls/aiohttpdemo_polls/main.py | 14 +++--- demos/polls/aiohttpdemo_polls/routes.py | 15 +++--- demos/polls/aiohttpdemo_polls/utils.py | 14 ------ demos/polls/aiohttpdemo_polls/views.py | 36 +++++++------- docs/tutorial.rst | 46 +++++++++++------- 6 files changed, 102 insertions(+), 87 deletions(-) diff --git a/demos/polls/aiohttpdemo_polls/db.py b/demos/polls/aiohttpdemo_polls/db.py index 7d56b830b78..311ee8e206f 100644 --- a/demos/polls/aiohttpdemo_polls/db.py +++ b/demos/polls/aiohttpdemo_polls/db.py @@ -1,3 +1,4 @@ +import aiopg.sa import sqlalchemy as sa @@ -34,32 +35,43 @@ class RecordNotFound(Exception): """Requested record in database was not found""" -async def get_question(postgres, question_id): - async with postgres.acquire() as conn: - cursor = await conn.execute( - question.select() - .where(question.c.id == question_id)) - question_record = await cursor.first() - if not question_record: - msg = "Question with id: {} does not exists" - raise RecordNotFound(msg.format(question_id)) - cursor = await conn.execute( - choice.select() - .where(choice.c.question_id == question_id) - .order_by(choice.c.id)) - choice_recoreds = await cursor.fetchall() +async def init_postgres(conf, loop): + engine = await aiopg.sa.create_engine( + database=conf['database'], + user=conf['user'], + password=conf['password'], + host=conf['host'], + port=conf['port'], + minsize=conf['minsize'], + maxsize=conf['maxsize'], + loop=loop) + return engine + + +async def get_question(conn, question_id): + result = await conn.execute( + question.select() + .where(question.c.id == question_id)) + question_record = await result.first() + if not question_record: + msg = "Question with id: {} does not exists" + raise RecordNotFound(msg.format(question_id)) + result = await conn.execute( + choice.select() + .where(choice.c.question_id == question_id) + .order_by(choice.c.id)) + choice_recoreds = await result.fetchall() return question_record, choice_recoreds -async def vote(postgres, question_id, choice_id): - async with postgres.acquire() as conn: - resp = await conn.execute( - choice.update() - .returning(*choice.c) - .where(choice.c.question_id == question_id) - .where(choice.c.id == choice_id) - .values(votes=choice.c.votes + 1)) - record = await resp.fetchone() - if not record: - msg = "Question with id: {} or choice id: {} does not exists" - raise RecordNotFound(msg.format(question_id), choice_id) +async def vote(conn, question_id, choice_id): + result = await conn.execute( + choice.update() + .returning(*choice.c) + .where(choice.c.question_id == question_id) + .where(choice.c.id == choice_id) + .values(votes=choice.c.votes+1)) + record = await result.fetchone() + if not record: + msg = "Question with id: {} or choice id: {} does not exists" + raise RecordNotFound(msg.format(question_id), choice_id) diff --git a/demos/polls/aiohttpdemo_polls/main.py b/demos/polls/aiohttpdemo_polls/main.py index 52f4991a3b8..91469d989ab 100644 --- a/demos/polls/aiohttpdemo_polls/main.py +++ b/demos/polls/aiohttpdemo_polls/main.py @@ -15,6 +15,11 @@ PROJ_ROOT = pathlib.Path(__file__).parent.parent +async def close_pg(app): + app['db'].close() + await app['db'].wait_closed() + + async def init(loop): # setup application and extensions app = web.Application(loop=loop) @@ -24,16 +29,13 @@ async def init(loop): conf = load_config(str(PROJ_ROOT / 'config' / 'polls.yaml')) # create connection to the database - pg = await init_postgres(conf['postgres'], loop) - - async def close_pg(app): - pg.close() - await pg.wait_closed() + db = await init_postgres(conf['postgres'], loop) + app['db'] = db app.on_cleanup.append(close_pg) # setup views and routes - handler = SiteHandler(pg) + handler = SiteHandler(db) setup_routes(app, handler, PROJ_ROOT) setup_middlewares(app) diff --git a/demos/polls/aiohttpdemo_polls/routes.py b/demos/polls/aiohttpdemo_polls/routes.py index 1749f60337c..0b66e336821 100644 --- a/demos/polls/aiohttpdemo_polls/routes.py +++ b/demos/polls/aiohttpdemo_polls/routes.py @@ -1,11 +1,12 @@ +from .views import index, poll, results, vote -def setup_routes(app, handler, project_root): - add_route = app.router.add_route - add_route('GET', '/', handler.index) - add_route('GET', '/poll/{question_id}', handler.poll, name='poll') - add_route('GET', '/poll/{question_id}/results', - handler.results, name='results') - add_route('POST', '/poll/{question_id}/vote', handler.vote, name='vote') + +def setup_routes(app, project_root): + app.router.add_route('GET', '/', index) + app.router.add_route('GET', '/poll/{question_id}', poll, name='poll') + app.router.add_route('GET', '/poll/{question_id}/results', + results, name='results') + app.router.add_route('POST', '/poll/{question_id}/vote', vote, name='vote') app.router.add_static('/static/', path=str(project_root / 'static'), name='static') diff --git a/demos/polls/aiohttpdemo_polls/utils.py b/demos/polls/aiohttpdemo_polls/utils.py index 691bc1d05a6..683968ef472 100644 --- a/demos/polls/aiohttpdemo_polls/utils.py +++ b/demos/polls/aiohttpdemo_polls/utils.py @@ -1,5 +1,4 @@ import yaml -import aiopg.sa def load_config(fname): @@ -7,16 +6,3 @@ def load_config(fname): data = yaml.load(f) # TODO: add config validation return data - - -async def init_postgres(conf, loop): - engine = await aiopg.sa.create_engine( - database=conf['database'], - user=conf['user'], - password=conf['password'], - host=conf['host'], - port=conf['port'], - minsize=conf['minsize'], - maxsize=conf['maxsize'], - loop=loop) - return engine diff --git a/demos/polls/aiohttpdemo_polls/views.py b/demos/polls/aiohttpdemo_polls/views.py index 4399d1c549f..20bbb9f387b 100644 --- a/demos/polls/aiohttpdemo_polls/views.py +++ b/demos/polls/aiohttpdemo_polls/views.py @@ -3,24 +3,22 @@ from . import db -class SiteHandler: +@aiohttp_jinja2.template('index.html') +async def index(request): + async with request['db'].acquire() as conn: + cursor = await conn.execute(db.question.select()) + records = await cursor.fetchall() - def __init__(self, pg): - self.postgres = pg - - @aiohttp_jinja2.template('index.html') - async def index(self, request): - async with self.postgres.acquire() as conn: - cursor = await conn.execute(db.question.select()) - records = await cursor.fetchall() questions = [dict(q) for q in records] return {'questions': questions} - @aiohttp_jinja2.template('detail.html') - async def poll(self, request): + +@aiohttp_jinja2.template('detail.html') +async def poll(request): + async with request['db'].acquire() as conn: question_id = request.match_info['question_id'] try: - question, choices = await db.get_question(self.postgres, + question, choices = await db.get_question(conn, question_id) except db.RecordNotFound as e: raise web.HTTPNotFound(text=str(e)) @@ -29,12 +27,14 @@ async def poll(self, request): 'choices': choices } - @aiohttp_jinja2.template('results.html') - async def results(self, request): + +@aiohttp_jinja2.template('results.html') +async def results(request): + async with request['db'].acquire() as conn: question_id = request.match_info['question_id'] try: - question, choices = await db.get_question(self.postgres, + question, choices = await db.get_question(conn, question_id) except db.RecordNotFound as e: raise web.HTTPNotFound(text=str(e)) @@ -44,7 +44,9 @@ async def results(self, request): 'choices': choices } - async def vote(self, request): + +async def vote(request): + async with request['db'].acquire() as conn: question_id = int(request.match_info['question_id']) data = await request.post() try: @@ -53,7 +55,7 @@ async def vote(self, request): raise web.HTTPBadRequest( text='You have not specified choice value') from e try: - await db.vote(self.postgres, question_id, choice_id) + await db.vote(conn, question_id, choice_id) except db.RecordNotFound as e: raise web.HTTPNotFound(text=str(e)) router = request.app.router diff --git a/docs/tutorial.rst b/docs/tutorial.rst index a81df4d3f80..8cf12a7a3b6 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -144,6 +144,10 @@ and second table is choice table: | question_id | +---------------+ +TBD: aiopg.sa.create_engine and pushing it into app's storage + +TBD: graceful cleanup + .. _aiohttp-tutorial-views: @@ -156,16 +160,17 @@ next Python code inside file (``polls/aiohttpdemo_polls/views.py``):: from aiohttp import web - class SiteHandler: - async def index(self, request): - return web.Response(text='Hello Aiohttp!') + async def index(self, request): + return web.Response(text='Hello Aiohttp!') This is the simplest view possible in Aiohttp. Now we should add ``index`` view to ``polls/aiohttpdemo_polls/routes.py``:: - def setup_routes(app, handler, project_root): - add_route = app.router.add_route - add_route('GET', '/', handler.index) + from .views import index + + + def setup_routes(app, project_root): + app.router.add_route('GET', '/', index) Now if we open browser we can see:: @@ -181,17 +186,18 @@ Templates Let's add more useful views:: @aiohttp_jinja2.template('detail.html') - async def poll(self, request): - question_id = request.match_info['question_id'] - try: - question, choices = await db.get_question(self.postgres, - question_id) - except db.RecordNotFound as e: - raise web.HTTPNotFound(text=str(e)) - return { - 'question': question, - 'choices': choices - } + async def poll(request): + async with request['db'].acquire() as conn: + question_id = request.match_info['question_id'] + try: + question, choices = await db.get_question(conn, + question_id) + except db.RecordNotFound as e: + raise web.HTTPNotFound(text=str(e)) + return { + 'question': question, + 'choices': choices + } Templates are very convinient way forweb page writing. We return a dict with page content, ``aiohttp_jinja2.template`` decorator @@ -235,3 +241,9 @@ Fortunatelly it can be done easy by single call:: where ``project_root`` is the path to root folder. + + +Middlewares +----------- + +TBD