python?-?sqlachemy另類用法思路詳解
這里只是給出一個(gè)思路,或許對(duì)于未來(lái)解決問(wèn)題有一些參考意義。
仿 JAP 的寫法
這種寫法很像 java 環(huán)境中的 JPA,如果引入模版引擎,則可以大幅增強(qiáng)實(shí)用性。
但是,在 python 環(huán)境中,這不符合主流的 ORM 框架。
潛在風(fēng)險(xiǎn):代碼檢測(cè)的時(shí)候,可能會(huì)被誤判,因?yàn)槲覀兌x了一大堆空的函數(shù)。
# 注解式事務(wù) start --------------------------------------------- @update(sql='UPDATE `t_temp` SET `desc`= :desc WHERE (`id`= :id) LIMIT 1') def modify(params: dict = None) -> int: pass @query(sql='SELECT * FROM `t_temp` WHERE (`id`= :id) LIMIT 1', result_type=dict) def queryById(params: dict = None) -> list: pass @query(sql='SELECT * FROM `t_temp` WHERE (`id`= :id) LIMIT 1', result_type=dict) def queryById2(id: int) -> list: pass @transactional() def test_annotation(): ret = modify({'id': 18, 'desc': 'OR 1=1'}) print(ret) result = queryById2(18) print(result)
代碼封裝
import inspect import logger_factory import typing from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.engine import Result, CursorResult logger = logger_factory.get_logger() # 定義數(shù)據(jù)庫(kù)連接字符串 DATABASE_URI = 'mysql+pymysql://{username}:{password}@{host}:{port}/{dbname}?charset=utf8mb4' # 替換為你的數(shù)據(jù)庫(kù)用戶名、密碼、主機(jī)、端口和數(shù)據(jù)庫(kù)名 USERNAME = 'root' PASSWORD = 'root' HOST = 'localhost' PORT = '3306' DBNAME = 'med' # 創(chuàng)建數(shù)據(jù)庫(kù)引擎,使用連接池 engine = create_engine( DATABASE_URI.format( username=USERNAME, password=PASSWORD, host=HOST, port=PORT, dbname=DBNAME ), echo=False, # 如果設(shè)置為True,SQLAlchemy將打印所有執(zhí)行的SQL語(yǔ)句,通常用于調(diào)試 pool_size=10, # 連接池大小 max_overflow=20, # 超過(guò)連接池大小外最多創(chuàng)建的連接數(shù) pool_timeout=30, # 連接池中沒(méi)有線程可用時(shí),在拋出異常前等待的時(shí)間 pool_recycle=3600 # 多少秒之后對(duì)連接進(jìn)行一次回收(重置) ) # do a test with engine.connect() as con: rs = con.execute(text('SELECT 1')) rs.fetchone() logger.debug('create engine succeed!') # session-maker Session = sessionmaker(bind=engine) # thread safe session-maker DBSession = scoped_session(Session) # with Session() as session: # # 獲取數(shù)據(jù)庫(kù)連接 # connection = session.connection() # savepoint = connection.begin_nested() # print(savepoint) def getEffectRows(result: Result) -> int: r""" 獲取受影響行數(shù) 這里有點(diǎn)問(wèn)題:源碼部分 rowcount 是一個(gè) callable,但實(shí)際應(yīng)該是 int; 這里繞一點(diǎn),確保不會(huì)出問(wèn)題,如果返回 -1,說(shuō)明出現(xiàn)了意料之外的情況 :param result: 結(jié)果集 :return: 受影響行數(shù) """ if isinstance(result, CursorResult): effect_row = result.rowcount if isinstance(effect_row, int): return effect_row if callable(effect_row): return effect_row() return -1 def resultAsDict(result: Result) -> list: r""" 將查詢結(jié)果轉(zhuǎn)換為 dict-list :param result: 結(jié)果集 :return: dict 列表 """ keys = result.keys() ret = list() for item in result.fetchall(): ret.append(dict(zip(keys, item))) return ret def execute(sql: str, params: dict = None) -> Result: r""" 執(zhí)行一條查詢語(yǔ)句 :param sql: 查詢語(yǔ)句 :param params: 參數(shù) :return: 結(jié)果集 """ if sql is None: raise ValueError('sql cannot be None') logger.debug('execute sql: ' + sql) logger.debug('parameter : ' + str(params)) return DBSession().execute(text(sql), params) def executeQuery(sql: str, params: dict = None, result_type: type = tuple) -> typing.Sequence: r""" 執(zhí)行一個(gè)查詢 :param sql: sql :param params: dict :param result_type: 結(jié)果集類型,可選:tuple、dict :return: 序列 """ result = execute(sql, params) if result_type == dict: return resultAsDict(result) pass # default return_type tuple-list return result.fetchall() def executeUpdate(sql: str, params: dict = None) -> int: r""" 執(zhí)行一個(gè)查詢 :param sql: sql 執(zhí)行語(yǔ)句 :param params: dict 查詢參數(shù) :return: 受影響行數(shù) """ result = execute(sql, params) return getEffectRows(result) def transactional(rollback: type = Exception): r""" 注解式事務(wù) 用法類似于 spring 環(huán)境下的 @Transactional 注解 注意: 事務(wù)控制在 session 級(jí)別,不能兼容事務(wù)嵌套的場(chǎng)景(理想狀態(tài)下,應(yīng)當(dāng)通過(guò) save-point 實(shí)現(xiàn)) 推薦: 如果遇到很復(fù)雜的事務(wù)嵌套,顯式調(diào)用 session,手動(dòng)控制事務(wù) :param rollback: 指定觸發(fā)回滾的異常類型 :return: 裝飾器函數(shù) """ def decorator(func): def call(*args, **kwargs): session = None try: session = DBSession() ret = func(*args, **kwargs) session.commit() return ret except rollback as e: if session: session.rollback() logger.exception(f'transaction exception, rollback: {str(e)}') raise finally: if session: session.close() return call return decorator pass def update(sql: str = None): r""" 注解式查詢,E.G.:: @update(sql='UPDATE `t_temp` SET `desc`= :desc WHERE (`id`= :id) LIMIT 1') def modify(params: dict = None) -> int: pass :param sql: 要執(zhí)行的 sql :return: decorator """ def decorator(func): def call(*args, **kwargs): result = execute(sql, args[0]) return getEffectRows(result) return call return decorator pass def query(sql: str = None, result_type: type = tuple): r""" 注解式查詢,E.G.:: E.G.:: @query(sql='SELECT * FROM `t_temp` WHERE (`id`= :id) LIMIT 1', result_type=dict) def queryById2(id: int) -> list: pass :param sql: 要執(zhí)行的 sql :param result_type: 結(jié)果集類型,可選:tuple、dict :return: decorator """ def decorator(func): def call(*args, **kwargs): if sql is None: raise ValueError('sql cannot be None') first = args[0] if isinstance(first, dict): result = DBSession().execute(text(sql), args) else: names = inspect.signature(func).parameters.values() params = dict() for idx, name in enumerate(names): params[name.name] = args[idx] print(params) result = DBSession().execute(text(sql), params) if result_type == dict: keys = result.keys() ret = list() for item in result.fetchall(): ret.append(dict(zip(keys, item))) return ret # default return_type tuple pass return result.fetchall() return call return decorator pass @transactional() def test_transaction(): r""" 測(cè)試注解式事務(wù) :return: None """ session = DBSession() session.execute(text("UPDATE `t_temp` SET `desc`= :desc WHERE (`id`= :id) LIMIT 1"), {'id': 18, 'desc': 'OR 1=3'}) session.execute(text("UPDATE `t_temp` SET `desc`= :desc WHERE (`id`= :id) LIMIT 1"), {'id': 18, 'desc': 'OR 1=4'}) # raise exception raise SyntaxError('Syntax error') @transactional() def test_api(): r""" 測(cè)試封裝過(guò)的函數(shù) :return: None """ execute("UPDATE `t_temp` SET `desc`= :desc WHERE (`id`= :id) LIMIT 1", {'id': 18, 'desc': 'OR 1=1'}) execute("UPDATE `t_temp` SET `desc`= :desc WHERE (`id`= :id) LIMIT 1", {'id': 18, 'desc': 'OR 1=2'}) # raise exception raise SyntaxError('Syntax error')
到此這篇關(guān)于python - sqlachemy另類用法的文章就介紹到這了,更多相關(guān)python sqlachemy另類用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pytorch DataLoader的num_workers參數(shù)與設(shè)置大小詳解
這篇文章主要介紹了pytorch DataLoader的num_workers參數(shù)與設(shè)置大小詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05python使用Streamlit庫(kù)制作Web可視化頁(yè)面
一談到Web頁(yè)面,可能大家首先想到就是HTML,CSS或JavaScript。 本次小F就給大家介紹一下如何用Python制作一個(gè)數(shù)據(jù)可視化網(wǎng)頁(yè),使用到的是Streamlit庫(kù)。輕松的將一個(gè)Excel數(shù)據(jù)文件轉(zhuǎn)換為一個(gè)Web頁(yè)面,提供給所有人在線查看。2021-05-05Python 比較兩個(gè)數(shù)組的元素的異同方法
下面小編就為大家?guī)?lái)一篇Python 比較兩個(gè)數(shù)組的元素的異同方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08神經(jīng)網(wǎng)絡(luò)相關(guān)之基礎(chǔ)概念的講解
今天小編就為大家分享一篇關(guān)于神經(jīng)網(wǎng)絡(luò)相關(guān)之基礎(chǔ)概念的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12Python實(shí)現(xiàn)自動(dòng)化處理PDF文件的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何使用Python完成簡(jiǎn)單的PDF文件處理操作,如PDF文件的批量合并、拆分、加密以及添加水印等,需要的可以參考一下2022-09-09關(guān)于python的縮進(jìn)規(guī)則的知識(shí)點(diǎn)詳解
在本篇文章里小編給大家整理了關(guān)于python的縮進(jìn)規(guī)則的知識(shí)點(diǎn)詳解,有興趣的朋友們可以學(xué)習(xí)下。2020-06-06