views.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. from operator import attrgetter, itemgetter
  2. from pathlib import Path
  3. from random import randint, sample
  4. import jinja2
  5. import orjson
  6. from aiohttp.web import Response
  7. from sqlalchemy import select
  8. from sqlalchemy.orm.attributes import flag_modified
  9. from .models import sa_fortunes, sa_worlds, Fortune, World
  10. ADDITIONAL_FORTUNE_ORM = Fortune(id=0, message='Additional fortune added at request time.')
  11. ADDITIONAL_FORTUNE_ROW = {'id': 0, 'message': 'Additional fortune added at request time.'}
  12. READ_ROW_SQL = 'SELECT "randomnumber", "id" FROM "world" WHERE id = $1'
  13. READ_SELECT_ORM = select(World.randomnumber)
  14. WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$2 WHERE id=$1'
  15. template_path = Path(__file__).parent / 'templates' / 'fortune.jinja'
  16. template = jinja2.Template(template_path.read_text())
  17. sort_fortunes_orm = attrgetter('message')
  18. sort_fortunes_raw = itemgetter('message')
  19. def get_num_queries(request):
  20. try:
  21. num_queries = int(request.match_info.get('queries', 1))
  22. except ValueError:
  23. return 1
  24. if num_queries < 1:
  25. return 1
  26. if num_queries > 500:
  27. return 500
  28. return num_queries
  29. def json_response(payload):
  30. return Response(
  31. body=orjson.dumps(payload),
  32. content_type="application/json",
  33. )
  34. async def json(request):
  35. """
  36. Test 1
  37. """
  38. return json_response({'message': 'Hello, World!'})
  39. async def single_database_query_orm(request):
  40. """
  41. Test 2 ORM
  42. """
  43. id_ = randint(1, 10000)
  44. async with request.app['db_session']() as sess:
  45. num = await sess.scalar(select(World.randomnumber).filter_by(id=id_))
  46. return json_response({'id': id_, 'randomNumber': num})
  47. async def single_database_query_raw(request):
  48. """
  49. Test 2 RAW
  50. """
  51. id_ = randint(1, 10000)
  52. async with request.app['pg'].acquire() as conn:
  53. r = await conn.fetchval('SELECT id,randomnumber FROM world WHERE id = $1', id_)
  54. return json_response({'id': id_, 'randomNumber': r})
  55. async def multiple_database_queries_orm(request):
  56. """
  57. Test 3 ORM
  58. """
  59. num_queries = get_num_queries(request)
  60. ids = [randint(1, 10000) for _ in range(num_queries)]
  61. result = []
  62. async with request.app['db_session']() as sess:
  63. for id_ in ids:
  64. num = await sess.scalar(READ_SELECT_ORM.where(World.id == id_))
  65. result.append({'id': id_, 'randomNumber': num})
  66. return json_response(result)
  67. async def multiple_database_queries_raw(request):
  68. """
  69. Test 3 RAW
  70. """
  71. num_queries = get_num_queries(request)
  72. ids = [randint(1, 10000) for _ in range(num_queries)]
  73. result = []
  74. async with request.app['pg'].acquire() as conn:
  75. stmt = await conn.prepare(READ_ROW_SQL)
  76. for id_ in ids:
  77. result.append({
  78. 'id': id_,
  79. 'randomNumber': await stmt.fetchval(id_),
  80. })
  81. return json_response(result)
  82. async def fortunes(request):
  83. """
  84. Test 4 ORM
  85. """
  86. async with request.app['db_session']() as sess:
  87. ret = await sess.execute(select(Fortune.id, Fortune.message))
  88. fortunes = ret.all()
  89. fortunes.append(ADDITIONAL_FORTUNE_ORM)
  90. fortunes.sort(key=sort_fortunes_orm)
  91. content = template.render(fortunes=fortunes)
  92. return Response(text=content, content_type='text/html')
  93. async def fortunes_raw(request):
  94. """
  95. Test 4 RAW
  96. """
  97. async with request.app['pg'].acquire() as conn:
  98. fortunes = await conn.fetch('SELECT * FROM Fortune')
  99. fortunes.append(ADDITIONAL_FORTUNE_ROW)
  100. fortunes.sort(key=sort_fortunes_raw)
  101. content = template.render(fortunes=fortunes)
  102. return Response(text=content, content_type='text/html')
  103. async def updates(request):
  104. """
  105. Test 5 ORM
  106. """
  107. num_queries = get_num_queries(request)
  108. ids = sample(range(1, 10000 + 1), num_queries)
  109. ids.sort()
  110. worlds = []
  111. async with request.app['db_session'].begin() as sess:
  112. for row_id in ids:
  113. random_number = randint(1, 10000)
  114. world = await sess.get(World, row_id, populate_existing=True)
  115. world.randomnumber = random_number
  116. # force sqlalchemy to UPDATE entry even if the value has not changed
  117. # doesn't make sense in a real application, added only for pass `tfb verify`
  118. flag_modified(world, "randomnumber")
  119. worlds.append({'id': row_id, 'randomNumber': random_number})
  120. return json_response(worlds)
  121. async def updates_raw(request):
  122. """
  123. Test 5 RAW
  124. """
  125. num_queries = get_num_queries(request)
  126. ids = sample(range(1, 10000 + 1), num_queries)
  127. ids.sort()
  128. updates = [(row_id, randint(1, 10000)) for row_id in ids]
  129. worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates]
  130. async with request.app['pg'].acquire() as conn:
  131. stmt = await conn.prepare(READ_ROW_SQL)
  132. for row_id in ids:
  133. # the result of this is the int previous random number which we don't actually use
  134. await stmt.fetchval(row_id)
  135. await conn.executemany(WRITE_ROW_SQL, updates)
  136. return json_response(worlds)
  137. async def plaintext(request):
  138. """
  139. Test 6
  140. """
  141. return Response(body=b'Hello, World!', content_type='text/plain')