我的第一个python web开发框架(28)——定制ORM(四)
在数据库操作时,新增记录也是必不可少的,接下来我们应用字典的特性来组合sql语句
先上产品新增接口代码
@post('/api/product/')
def callback():
"""
新增记录
"""
name = web_helper.get_form('name', '产品名称')
code = web_helper.get_form('code', '产品编码')
product_class_id = convert_helper.to_int0(web_helper.get_form('product_class_id', '产品分类'))
standard = web_helper.get_form('standard', '产品规格')
quality_guarantee_period = web_helper.get_form('quality_guarantee_period', '保质期')
place_of_origin = web_helper.get_form('place_of_origin', '产地')
front_cover_img = web_helper.get_form('front_cover_img', '封面图片')
content = web_helper.get_form('content', '产品描述', is_check_special_char=False)
# 防sql注入攻击处理
content = string_helper.filter_str(content, "'")
# 防xss攻击处理
content = string_helper.clear_xss(content)
is_enable = convert_helper.to_int0(web_helper.get_form('is_enable', '是否启用')) # 添加记录(使用returning这个函数能返回指定的字段值,这里要求返回新添加记录的自增id值)
sql = """insert into product (name, code, product_class_id, standard, quality_guarantee_period,
place_of_origin, front_cover_img, content, is_enable)
values (%s, %s, %s, %s, %s, %s, %s, %s, %s) returning id"""
vars = (name, code, product_class_id, standard, quality_guarantee_period, place_of_origin, front_cover_img, content, is_enable)
# 写入数据库
result = db_helper.write(sql, vars)
# 判断是否提交成功
if result and result[0].get('id'):
return web_helper.return_msg(0, '成功')
else:
return web_helper.return_msg(-1, "提交失败")
在21行到24行就是sql语句的拼接,使用这种方法,我们经常会因为多写或少写%s和变量,导致sql执行出错。
在python中,我们最常用的就是字典,从新增记录的语句中,可以看到字段与值是一一对应的,那么我们就可以考虑使用字典的方式来进行存储字段与值,在ORM中对其进行处理,然后拼接生成对应的sql语句。
操作步骤如下几步:
1.将新增记录时的字段名与值,使用字典方式存储起来
2.将字典做为参数传给ORM新增记录方法
3.在进行下一步之前,大家首先要了解python字符串替的有多种方法,例如:方法一:’Hello %s' % ('world',) ;方法二:'Hello %(name)s' % {'name': 'world'} ;等,还有很多其他方法这里不一一列举。第一种只能按照指定的顺序进行替换,数量必须一致,不然会出错;而第二种字典类型替换时,字典位置不需要考虑,字典值超出替换内容,会自动忽略,它最大的好处,我觉得是可读性,让这个字符串变的更加容易理解。所以下面我们将会使用字典替换方法。
4.新增记录方法接收到参数以后,使用for循环,将字段名提取出来,生成sql插入字段名数组和字典替换数组,即:insert into table_name (字段名,字段名...) values (值,值...),这里需要将字典中的字段名提取出来组合成“字段名,字段名...”这样的串,值也是一样,为了让组合后的字符串更容易调试与理解,我们将它生成字典替换格式%(name1)s,%(name2)s...,最好再使用字典直接替换(当然你直接将值连接起来也行,你可以根据自己的需要进行调整)。
例如,我们需要添加产品名称、产品编码、产品分类id,可以它们用字典存储起来做为参数
fields = {
'name': "'名称'",
'code': "'201808031245678'",
'product_class_id': 1,
}
然后可以通过for循环,将字典参数进行处理,提取出来存储到list中
key_list = []
value_list = []
for key in fields.keys():
key_list.append(key)
value_list.append('%(' + key + ')s')
执行后,key_list与value_list的值分别为:
key_list = ['name', 'product_class_id', 'code']
value_list = ['%(name)s', '%(product_class_id)s', '%(code)s']
然后我们设置一个sql字符串拼接字典,将表名、字段名字符串与值字符串存储进去,在存储前使用join方式进行拼接,生成用逗号分隔的字符串
parameter = {
'table_name': self.__table_name,
'key_list': ','.join(key_list),
'value_list': ','.join(value_list)
}
执行后生成的值为:
parameter = {'key_list': 'name,product_class_id,code', 'value_list': '%(name)s,%(product_class_id)s,%(code)s', 'table_name': 'product'}
然后将它们与新增sql合成
sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning id " % parameter
执行后sql值为:
sql = 'insert into product (name,product_class_id,code) values (%(name)s,%(product_class_id)s,%(code)s) '
最后将它与最开始提交的字典参数进行合成
sql = sql % fields
生成最终可执行的sql语句
sql = "insert into product (name,product_class_id,code) values ('名称',1,'201808031245678') "
完整代码
def add_model(self, fields, returning=''):
"""新增数据库记录"""
### 拼接sql语句 ###
# 初始化变量
key_list = []
value_list = []
# 将传入的字典参数进行处理,把字段名生成sql插入字段名数组和字典替换数组
# PS:字符串使用字典替换参数时,格式是%(name)s,这里会生成对应的字串
# 比如:
# 传入的字典为: {'id': 1, 'name': '名称'}
# 那么生成的key_list为:'id','name'
# 而value_list为:'%(id)s,%(name)s'
# 最终而value_list为字符串对应名称位置会被替换成相应的值
for key in fields.keys():
key_list.append(key)
value_list.append('%(' + key + ')s')
# 设置sql拼接字典,并将数组(lit)使用join方式进行拼接,生成用逗号分隔的字符串
parameter = {
'table_name': self.__table_name,
'pk_name': self.__pk_name,
'key_list': ','.join(key_list),
'value_list': ','.join(value_list)
}
# 如果有指定返回参数,则添加
if returning:
parameter['returning'] = ', ' + returning
else:
parameter['returning'] = '' # 生成可以使用字典替换的字符串
sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning %(pk_name)s %(returning)s" % parameter
# 将生成好的字符串替字典参数值,生成最终可执行的sql语句
sql = sql % fields with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
# 执行sql语句
result = db.execute(sql)
if result:
db.commit()
return result[0]
return {}
代码大家多debug一下,就可以查看到每一行代码的执行情况以及sql拼接生成情况了。
这里的sql语句额外添加了postgresql的returning函数,在默认情况下会返回数据插入成功后的主键值给主程序。我们在进行新增操作时,经常要获取新增记录的自增id,通过return id就可以将它返回给我们,如果你还需要返回其他字段值,可以在这个新增方式的returning参数中添加想要返回的字段名,如果想返回整条记录,输入*号就可以了。
写到这里,可以看到查询与执行,都使用类似的with方法,我们可以将它们提取出来重构成独立的函数,为后续的修改和其他查询去除重复代码,改造后代码如下:
#!/usr/bin/env python
# coding=utf-8 from common import db_helper class LogicBase():
"""逻辑层基础类""" def __init__(self, db, is_output_sql, table_name, column_name_list='*', pk_name='id'):
"""类初始化"""
# 数据库参数
self.__db = db
# 是否输出执行的Sql语句到日志中
self.__is_output_sql = is_output_sql
# 表名称
self.__table_name = str(table_name).lower()
# 查询的列字段名称,*表示查询全部字段,多于1个字段时用逗号进行分隔,除了字段名外,也可以是表达式
self.__column_name_list = str(column_name_list).lower()
# 主健名称
self.__pk_name = str(pk_name).lower() #####################################################################
### 执行Sql ### def get_model(self, wheres):
"""通过条件获取一条记录"""
# 如果有条件,则自动添加where
if wheres:
wheres = ' where ' + wheres # 合成sql语句
sql = "select %(column_name_list)s from %(table_name)s %(wheres)s" % \
{'column_name_list': self.__column_name_list, 'table_name': self.__table_name, 'wheres': wheres}
# 初化化数据库链接
result = self.select(sql)
if result:
return result[0]
return {} def get_model_for_pk(self, pk, wheres=''):
"""通过主键值获取数据库记录实体"""
if not pk:
return {}
# 组装查询条件
wheres = '%s = %s' % (self.__pk_name, str(pk)) return self.get_model(wheres) def add_model(self, fields, returning=''):
"""新增数据库记录"""
### 拼接sql语句 ###
# 初始化变量
key_list = []
value_list = []
# 将传入的字典参数进行处理,把字段名生成sql插入字段名数组和字典替换数组
# PS:字符串使用字典替换参数时,格式是%(name)s,这里会生成对应的字串
# 比如:
# 传入的字典为: {'id': 1, 'name': '名称'}
# 那么生成的key_list为:'id','name'
# 而value_list为:'%(id)s,%(name)s'
# 最终而value_list为字符串对应名称位置会被替换成相应的值
for key in fields.keys():
key_list.append(key)
value_list.append('%(' + key + ')s')
# 设置sql拼接字典,并将数组(lit)使用join方式进行拼接,生成用逗号分隔的字符串
parameter = {
'table_name': self.__table_name,
'pk_name': self.__pk_name,
'key_list': ','.join(key_list),
'value_list': ','.join(value_list)
}
# 如果有指定返回参数,则添加
if returning:
parameter['returning'] = ', ' + returning
else:
parameter['returning'] = '' # 生成可以使用字典替换的字符串
sql = "insert into %(table_name)s (%(key_list)s) values (%(value_list)s) returning %(pk_name)s %(returning)s" % parameter
# 将生成好的字符串替字典参数值,生成最终可执行的sql语句
sql = sql % fields result = self.execute(sql)
if result:
return result[0]
return {} def select(self, sql):
"""执行sql查询语句(select)"""
with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
# 执行sql语句
result = db.execute(sql)
if not result:
result = []
return result def execute(self, sql):
"""执行sql语句,并提交事务"""
with db_helper.PgHelper(self.__db, self.__is_output_sql) as db:
# 执行sql语句
result = db.execute(sql)
if result:
db.commit()
else:
result = []
return result #####################################################################
上单元测试代码:
#!/usr/bin/evn python
# coding=utf-8 import unittest
from common.string_helper import string
from logic import product_logic class DbHelperTest(unittest.TestCase):
"""数据库操作包测试类""" def setUp(self):
"""初始化测试环境"""
print('------ini------') def tearDown(self):
"""清理测试环境"""
print('------clear------') def test(self):
##############################################
# 只需要看这里,其他代码是测试用例的模板代码 #
##############################################
# 实例化product表操作类ProductLogic
_product_logic = product_logic.ProductLogic()
# 执行get_model_for_pk()方法,获取记录实体
model = _product_logic.get_model_for_pk(2)
print(model) # 测试新增记录
fields = {
'name': string('名称'),
'code': string(''),
'product_class_id': 1,
}
result = _product_logic.add_model(fields, '*')
print(result) ############################################## if __name__ == '__main__':
unittest.main()
PS:单元测试中,通过from common.string_helper import string导入了string这个方法,它是用来给字符串添加单撇号字符(')用的,因为在更新数据库时,字符串类型需要用单撇号括起来的,而Python中直接使用单撇字符只是标识它为字符串而已,需要用\'或双引号+单撇字符+双引号方式,才能得到我们想要的结果,为了方便操作,所以增加了string这个方法,帮我们进行转换。
输出结果:
------ini------
{'content': '产品描述', 'name': '饼干', 'place_of_origin': '广东广州', 'add_time': datetime.datetime(2018, 7, 25, 23, 10, 4), 'code': '', 'is_enable': 1, 'front_cover_img': 'http://xxx.com/xxx.jpg', 'product_class_id': 1, 'quality_guarantee_period': '2018年12月', 'id': 2, 'standard': '500g'}
{'content': '', 'name': '名称', 'place_of_origin': '', 'add_time': datetime.datetime(2018, 8, 3, 16, 51, 3), 'code': '', 'is_enable': 0, 'front_cover_img': '', 'product_class_id': 1, 'quality_guarantee_period': '', 'id': 15, 'standard': ''}
------clear------
前面的接口也可以改造为
@post('/api/product/')
def callback():
"""
新增记录
"""
name = web_helper.get_form('name', '产品名称')
code = web_helper.get_form('code', '产品编码')
product_class_id = convert_helper.to_int0(web_helper.get_form('product_class_id', '产品分类'))
standard = web_helper.get_form('standard', '产品规格')
quality_guarantee_period = web_helper.get_form('quality_guarantee_period', '保质期')
place_of_origin = web_helper.get_form('place_of_origin', '产地')
front_cover_img = web_helper.get_form('front_cover_img', '封面图片')
content = web_helper.get_form('content', '产品描述', is_check_special_char=False)
# 防sql注入攻击处理
content = string_helper.filter_str(content, "'")
# 防xss攻击处理
content = string_helper.clear_xss(content)
is_enable = convert_helper.to_int0(web_helper.get_form('is_enable', '是否启用')) # 设置新增参数
fields = {
'name': string(name),
'code': string(code),
'product_class_id': product_class_id,
'standard': string(standard),
'quality_guarantee_period': string(quality_guarantee_period),
'place_of_origin': string(place_of_origin),
'front_cover_img': string(front_cover_img),
'content': string(content),
'is_enable': is_enable,
}
# 实例化product表操作类ProductLogic
_product_logic = product_logic.ProductLogic()
# 新增记录
result = _product_logic.add_model(fields)
# 判断是否提交成功
if result:
return web_helper.return_msg(0, '成功')
else:
return web_helper.return_msg(-1, "提交失败")
代码行数比之前是增加了,但可读性好了很多
版权声明:本文原创发表于 博客园,作者为 AllEmpty 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
python开发QQ群:669058475(本群已满)、733466321(可以加2群) 作者博客:http://www.cnblogs.com/EmptyFS/
我的第一个python web开发框架(28)——定制ORM(四)的更多相关文章
- 我的第一个python web开发框架(41)——总结
我的第一个python web开发框架系列博文从17年6.7月份开始写(存了近十章稿留到9月份才开始发布),到今天结束,一年多时间,想想真不容易啊. 整个过程断断续续,中间有段时间由于工作繁忙停了好长 ...
- 我的第一个python web开发框架(14)——后台管理系统登录功能
接下来正式进入网站的功能开发.要完成后台管理系统登录功能,通过查看登录页面,我们可以了解到,我们需要编写验证码图片获取接口和登录处理接口,然后在登录页面的HTML上编写AJAX. 在进行接口开发之前, ...
- 我的第一个python web开发框架(1)——前言
由于之前经验不是很丰富,写的C#系统太过复杂,所以一直想重写,但学的越多越觉得自己懂的越少,越觉的底气不足.所以一直不敢动手,在内心深处对自己讲,要静下心来认真学习,继续沉淀沉淀.这两年多以来找各种机 ...
- 我的第一个python web开发框架(3)——怎么开始?
小白与小美公司经过几次接触商谈,好不容易将外包签订了下来,准备开始大干一场.不过小白由于没有太多的项目经验,学过python懂得python的基本语法,在公司跟着大家做过简单功能,另外还会一些HTML ...
- 我的第一个python web开发框架(22)——一个安全小事故
在周末的一个早上,小白还在做着美梦,就收到了小美的连环追魂call,电话一直响个不停. 小白打着哈欠拿起电话:早上好美女. 小美:出事了出事了,我们公司网站一早访问是一片空白,什么内容都没有了,你赶急 ...
- 我的第一个python web开发框架(2)——一个简单的小外包
第一部分说明 第一部分大概有20来章,主要讲的是一些开发常识.开发前中后期准备内容.开发环境与服务器部署环境安装设置.python基础框架结构与功能等内容,代码会比较简单. 本系列会以故事的方式,向大 ...
- 我的第一个python web开发框架(6)——第一个Hello World
小白中午听完老菜讲的那些话后一直在思考,可想来想去还是一头雾水,晕晕呼呼的一知半解,到最后还是想不明白,心想:老大讲的太高深了,只能听懂一半半,看来只能先记下来,将明白的先做,不明白的等以后遇到再学. ...
- 我的第一个python web开发框架(7)——本地部署前端访问服务器
PS:本系列内容进度节奏会放的很慢,每次知识点都尽量少一点,这样大家接触的知识点少了,会更容易理解,因为少即是多.另外,对于后面代码部分,虽然尽量不用那些复杂的封装和类,但它并不表示看了就能全部记住, ...
- 我的第一个python web开发框架(10)——工具函数包说明(一)
PS:原先是想直接进入功能开发,要用到什么函数时再创建,这样也容易熟悉每个函数的由来和使用方法,但考虑到这样操作,到时会经常在不同文件间切换,不好描述,容易造成混乱,所以还是使用函数库这种方式来说明. ...
随机推荐
- 边缘计算 VS 云计算,谁才是未来?
计算是互联网中一个永恒的话题,设备的所有运行都可以看成是 0 和 1 的运算.在计算中近些年有两个越来越响亮的技术:云计算和边缘计算.现如今是云计算方兴未艾,边缘计算已经有了燎原之势,本文将对这两种技 ...
- Flink生成Parquet格式文件实战
1.概述 在流数据应用场景中,往往会通过Flink消费Kafka中的数据,然后将这些数据进行结构化到HDFS上,再通过Hive加载这些文件供后续业务分析.今天笔者为大家分析如何使用Flink消费Kaf ...
- web进修之—Hibernate起步(1)(2)
想开始写博客了,尝试了CSDN和cnblog之后还是觉得cnblog更加简洁.专注(不过cnblog不支持搬家),所以把刚刚写的两篇学习博客链接放在这儿,这样这个系列也算是完整了: web进修之—Hi ...
- Eclipse查看JDK源码(非常详细)
Eclipse查看源码的方式其实很简单,打开项目,然后按着ctrl,然后把鼠标光标移动到你想查看的方法或者对象上,这时会出现一条下划线,然后点击鼠标左键就可以进入那个方法或者对象了.但是有的情况下会出 ...
- java中变量的存储位置
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符 ...
- 【Angular专题】——(2)【译】Angular中的ForwardRef
原文地址:https://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html 作者:Christoph ...
- 第62章 EntityFramework支持 - Identity Server 4 中文文档(v1.0.0)
为IdentityServer中的配置和操作数据扩展点提供了基于EntityFramework的实现.EntityFramework的使用允许任何EF支持的数据库与此库一起使用. 这个库的仓库位于这里 ...
- InterLocked学习笔记
在进行多线程编程的时候特别重要的一点就是多线程的同步,什么是同步呢?字面意思就是使多个不在同一线程执行的代码统一到一个线程中执行,但是对执行中的线程过程却无法控制,这就造成了多个线程可能同时操作同一个 ...
- Entity Framework 框架
微软官方提供的ORM技术的实现就是EF(Entity Framework)框架.EF的模式有三种分别是:Database First 数据库先行 ,Model First 模型先行 , Code F ...
- 杭电ACM2005--第几天?
第几天? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...