Advertisement
TC-b64

Untitled

Dec 12th, 2019
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.06 KB | None | 0 0
  1.  
  2. """
  3. Начну с того, зачем вообще придуман данный декоратор.
  4.  
  5. Как ты прописываешь обращения к СУБД в методах?
  6. Вероятнее всего, ты возьмешь соединение (при учатии пула соединений), а затем откроешь транзакцию
  7. (ведь в асинхронном приложении возможен race condition, да и в целом тебе могут понадобиться
  8. свойства транзакций (требования ACID), например атомарность (блок дейтвий будет выполнен весь, либо не выполнен вовсе))
  9. И вот, когда есть соединение, и начата транзакция, ты пишешь SQL-запрос.
  10.  
  11. В коде это выглядело бы как-то так:
  12. """
  13.  
  14.  
  15.  
  16. class DBMS:
  17.  
  18.     def __init__(...):
  19.         self.pool = ...
  20.  
  21.     async def fetch_data(self, data_id):
  22.         async with self.pool.acquire() as connect:
  23.             async with connect.transaction():
  24.                 return await connect.fetch(""" SELECT * FROM table_name WHERE data_id = $1; """, data_id)
  25.  
  26.  
  27. """
  28. В целом, всё выглядит логично.
  29. А теперь добавим еще методы...
  30. """
  31.  
  32.  
  33.     async def insert_data(self, data):
  34.         async with self.pool.acquire() as connect:
  35.             async with connect.transaction():
  36.                 await connect.execute(""" INSERT INTO table_name VALUES ($1); """, data)
  37.  
  38.     async def update_data(self, data_id, data):
  39.         async with self.pool.acquire() as connect:
  40.             async with connect.transaction():
  41.                 await connect.execute(""" SELECT * FROM table_name WHERE data_id = $1 FOR UPDATE; """, data_id)
  42.                 await connect.execute(""" UPDATE table_name SET data_column = $1 WHERE data_id = $2; """, data, data_id)
  43.  
  44.  
  45. """
  46. Тут уже видна избыточность. Мы делаем "подготовительную" часть в каждом методе.
  47. А теперь вспомним для чего были придуманы декораторы. Они как раз берут на себя действия,
  48. которые выполняются до и после какого-либо нашего кода.
  49. Выносим повторяющуюся часть в отдельное место:
  50. """
  51.  
  52.  
  53. def acquire_connection(method: Callable) -> Callable:
  54.     async def wrapper(self, *args, **kwargs) -> Any:  # не забываем про аргумент self, ведь мы оборачиваем методы
  55.         async with self._pool.acquire() as connect:  # берем соединение (именно берем, а не открываем!)
  56.             async with connect.transaction():  # начинаем транзакцию
  57.                 # выполняем код метода, передавая в него готовый _connect
  58.                 # _connect специально подчеркнут слева, чтобы ты не забыл, что его не нужно "прокидывать" извне
  59.                 return await method(self, _connect=connect, *args, **kwargs)
  60.     return wrapper
  61.  
  62.  
  63.  
  64. """
  65. Теперь посмотрим на тот же класс с декоратором:
  66. """
  67.  
  68.  
  69. from asyncpg.connection import Connection  # тайпинг проставляется для удобства в методах
  70.  
  71.  
  72. class DBMS:
  73.  
  74.     def __init__(...):
  75.         self.pool = ...
  76.  
  77.     @acquire_connection
  78.     async def fetch_data(self, data_id, _connect: Connection):
  79.         # _connect "подставляется" сам, потому что вся операция выполняется в декораторе
  80.         # необходимо его только прописывать, как ты это делаешь, например, в хэндлерах бота, куда "попадает" апдейт
  81.         return await _connect.fetch(""" SELECT * FROM table_name WHERE data_id = $1; """, data_id)
  82.  
  83.     @acquire_connection
  84.     async def insert_data(self, data, _connect: Connection):
  85.         await _connect.execute(""" INSERT INTO table_name VALUES ($1); """, data)
  86.  
  87.     @acquire_connection
  88.     async def update_data(self, data_id, data, _connect: Connection):
  89.         await _connect.execute(""" SELECT * FROM table_name WHERE data_id = $1 FOR UPDATE; """, data_id)
  90.         await _connect.execute(""" UPDATE table_name SET data_column = $1 WHERE data_id = $2; """, data, data_id)
  91.  
  92.  
  93.  
  94. """
  95. Настоятельно рекомендую передавать именованные аргументы, или хотя-бы совать _connect в конец,
  96. чтобы какой-нибудь user_id не встал вдруг на место _connect (как это было у тебя, ибо _connect ты поместил в начале,
  97. передав на его место другую вещь, сам того незаметив)
  98. """
  99.  
  100. dbms = DBMS(...)
  101.  
  102. data = await dbms.fetch_data(data_id=12345)
  103. await dbms.insert_data(data=...)
  104. await dbms.update_data(data_id=12345, data=...)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement