现代的编程语言在进行数据库操作时候,往往都是使用ORM组件(ObjectRelationalMapping)而不是直接链接到数据库中然后自己写SQL,ORM可以帮我们厘清数据关系、自动进行内置的Sql注入防护等等,总之好处多多。但是往往有点不太自由束手束脚不太放得开。
在早期的PHP写Web应用时候基本都是自己拼Sql语句的,当然带来大量的注入式漏洞。时下在Golang语言中,其语言内置了数据库的Sql包,可以让用户直接通过非ORM、非查询构建器方法来与数据库交互。即使在一些Golang大型项目中,写Sql语句也是很常见的。
而现在最流行,用户量最大的Python语言中没有任何内置的支持数据库交互的东西,这也给初学者进行数据库操作增加麻烦。本文中虫虫就给大家介绍如何在Python中通过适配器直接编写Sql语句,从而达到间接学习数据库和Sql语言的目的。
本文主要目的在于学习,可以实现在Python中不依赖查询生成器和ORM熟练的拼写和处理Sql,并能将这些逻辑抽象打包,最后可以实现MVC三层架构,实现数据库操作和业务逻辑(以及表现层)分开。
从一个简单的例子开始:
#user/domain.py
dataclassclassUser: id:int=None dt_cated:datetime=None username:str=None email:str=None mobile:Optional[str]=NoneUser是可能看到的名为SQL表的字面定义user。这包括喜欢包含在所有SQL表中的一些列:
id(主键)。
dt_cated日期时间值,通常默认为NOW()。
所有字段默认为None,这可以姐可以执行SELECT不包含表中所有列的查询,同时仍然能够将行转换为User对象。所有列都是NOTNULL除了mobile,代码中利用Optional键入提示以强调该字段允许NULL。
创建数据库
#user/pository.pyfromabcimportABC,abstractmethodclassUserRepository(ABC):
abstractmethoddefnew(self,new_row:User)-int:"""Cateanewcordandturnsthenewidvalue."""passabstractmethoddefget_by_id(self,id_val:int)-User:"""GetasinglecordbyIDvalue."""pass现在可以创建的存储库,创建的存储库是一个抽象类,可以确保任何未来的实现都需要遵循规则。
为了方便,我们直接使用psycopg3来实现PostgSQL链接,其他数据库类型都可以随时扩展:
#external/postgs.pyimportpsycopgfrompsycopg.rowsimportclass_rowfromconfigimportget_configcfg=get_config()conn=psycopg.connect(dbname=cfg.database_name,user=cfg.database_username,password=cfg.database_password,host=cfg.database_host,)defnew_cursor(name=None,row_factory=None):ifnameisNone:name=""ifrow_factoryisNone:turnconn.cursor(name=name)turnconn.cursor(name=name,row_factory=class_row(row_factory))
首先,根据配置设置连接,连接到数据。
结果会返回psycopg游标的函数。psycopg3包含一个自定义行工厂,这个功能非常有用。
#user/pository.pyfromexternal.postgsimportconn,new_cursorclassUserPostgSQL(UserRepository):defnew(self,new_row:User)-int:withnew_cursor()ascur:cur.execute("""INSERTINTOuser(username,email)VALUES(%s,%s)ONCONFLICT(email)DONOTHINGRETURNINGid;""",(new_row.username,new_row.email),)new_id=cur.fetchone()ifnew_idandlen(new_id):conn.