前面ORM模块我们已经完成了开发,接下来要做的就是对项目代码进行重构了。因为对底层数据库操作模块(db_helper.py)进行了改造,之前项目的接口代码全都跑不起来了。

  在写ORM模块时,我们已经对产品接口的分页查询、新增、修改、获取指定产品实体接口已经重构好了,还剩下删除接口未完成

 @delete('/api/product/<id:int>/')
def callback(id):
"""
删除指定记录
"""
# 编辑记录
sql = """delete from product where id=%s returning id"""
vars = (id,)
# 写入数据库
result = db_helper.write(sql, vars)
# 判断是否提交成功
if result:
return web_helper.return_msg(0, '成功')
else:
return web_helper.return_msg(-1, "删除失败")

  如果前面代码有认真学习的小伙伴看到这段代码,要改成ORM方式应该很容易实现了

  只需要将第7行到第10行替换对应的调用代码就可以了

 @delete('/api/product/<id:int>/')
def callback(id):
"""
删除指定记录
"""
# 实例化product表操作类ProductLogic
_product_logic = product_logic.ProductLogic()
result = _product_logic.delete_model(id)
# 判断是否提交成功
if result:
return web_helper.return_msg(0, '成功')
else:
return web_helper.return_msg(-1, "删除失败")

  首先是初始化产品逻辑层操作类,然后调用delete_model()这个方法就可以了

  当你习惯这种写法以后,你会发现实现各个接口会变得非常的简单与方便,开发速度比之前也提升了很多

  产品分类相关接口(product_class.py)与产品相关接口(product.py)功能差不多,具体实现我就不一一讲解了,大家可以自己试试

  产品分类的删除分类接口大家会看到它的代码与产品删除接口差不多,不过多了一个该分类是否已经被引用的一个判断,对于这个下面专门说明一下

 @delete('/api/product_class/<id:int>/')
def callback(id):
"""
删除指定记录
"""
# 判断该分类是否已经被引用,是的话不能直接删除
sql = """select count(*) as total from product where product_class_id=%s""" % (id,)
# 读取记录
result = db_helper.read(sql)
if result and result[0].get('total', -1) > 0:
return web_helper.return_msg(-1, "该分类已被引用,请清除对该分类的绑定后再来删除") # 编辑记录
sql = """delete from product_class where id=%s returning id"""
vars = (id,)
# 写入数据库
result = db_helper.write(sql, vars)
# 判断是否提交成功
if result:
return web_helper.return_msg(0, '成功')
else:
return web_helper.return_msg(-1, "删除失败")

  这段代码后半部分可以参考产品的删除接口实现,前半部分需要调用产品方法进行判断处理。

  在编写时我们会发现,我们的ORM并没有直接判断记录是否存在的方法,只有一个用于获取指定条件记录数的方法。

  一般来说,我们在开发时发现ORM没有自己想要的方法时,我们需要做以下思考:

  1.有没有替代可以实现的方法存在

  2.该功能是否是常用的功能,能否封装成公共方法,如果可以就将它封装到逻辑层基类(ORM模块)中去,让所有继承的子类都拥有这个功能

  3.如果它只是对指定表单操作时才用到,就将它封装到该逻辑层子类,方便该子类要用到时可以随时调用

  这段代码的要求是判断指定的分类是否被产品引用,抽象出来的意思就是判断指定条件的记录是否存在,对于这个功能有开发经验的小伙伴很容易判断它是很多地方都有可能要用到的通用方法,所以我们需要在ORM中增加一下这个方法。而我们ORM已经存在get_count()这个获取记录数的方法存在了,我们可以通过调用这个方法来判断记录数量是否大于0,来得出指定条件的记录是否存在这样的结果。

    def exists(self, wheres):
"""检查指定条件的记录是否存在"""
return self.get_count(wheres) > 0

  有了这个方法,我们就可以继续对产品分类删除接口进行改造了

 @delete('/api/product_class/<id:int>/')
def callback(id):
"""
删除指定记录
"""
# 实例化product表操作类ProductLogic
_product_logic = product_logic.ProductLogic()
# 判断该分类是否已经被引用,是的话不能直接删除
if _product_logic.exists('product_class_id=' + str(id)):
return web_helper.return_msg(-1, "该分类已被引用,请清除对该分类的绑定后再来删除") # 实例化product_class表操作类product_class_logic
_product_class_logic = product_class_logic.ProductClassLogic()
result = _product_class_logic.delete_model(id)
# 判断是否提交成功
if result:
return web_helper.return_msg(0, '成功')
else:
return web_helper.return_msg(-1, "删除失败")

  通过这个例子,大家在实际开发过程中,可以灵活的根据自己需要,来增加或改造对应的底层方法,积累你自己的底层框架代码,那么随着开发时间的增加,你开发起各种功能来就会越加得心应手了。

  细心的朋友会发现,ORM模块的缓存部分,多了一个get_model_for_cache_of_where()方法,下面我来说明一下它的用途。

  我们在开发时,除了通过主键id来获取记录实体以外,在有的数据表中,还会存在第二个主键,或多个主键的情况,我们需要通过这些主键来获取对应的记录实休,比如说管理员或用户表中的登录账号字段;订单表中的订单编码字段等。

  正常情况下,我们直接通过get_model()方法就可以读取对应的记录了,如果我们想减少数据库的查询,直接在缓存中如何使用呢?直接存取记录实体,由于这些额外的主键并没有与ORM中的编辑与删除操作关联,即在进行编辑与删除操作时不会同步更新用其他主键存储的实体内容,这样就会产生脏数据。所以我们可以换一种思路来实现,我们可以将这些额外的主键和对应的值生成缓存组合key,里面存储对应的记录实体id,也就是说在存储记录实体时,还是使用原来的主键id存储该实体,然后用额外主键和对应值生成缓存组合key中存储主键id,在获取记录实体时,先用这个组合key提取对应的id,再用这个id来获取记录实体。这个说明好像有点绕,大家自己debug一下就很容易明白其中的原理了,下面看代码:

     def get_model_for_cache_of_where(self, where):
"""
通过条件获取记录实体————条件必须是额外的主键,也就是说记录是唯一的(我们经常需要使用key、编码或指定条件来获取记录,这时可以通过当前方法来获取)
:param where: 查询条件
:return: 记录实体
"""
# 生成实体缓存key
model_cache_key = self.__table_name + encrypt_helper.md5(where)
# 通过条件从缓存中获取记录id
pk = cache_helper.get(model_cache_key)
# 如果主键id存在,则直接从缓存中读取记录
if pk:
return self.get_model_for_cache(pk) # 否则从数据库中获取
result = self.get_model(where)
if result:
# 存储条件对应的主键id值到缓存中
cache_helper.set(model_cache_key, result.get(self.__pk_name))
# 存储记录实体到缓存中
self.set_model_for_cache(result.get(self.__pk_name), result)
return result

  下面改造调用例子(请查看login.py第35行附近)

     ##############################################################
### 获取登录用户记录,并进行登录验证 ###
##############################################################
sql = """select * from manager where login_name='%s'""" % (username,)
# 从数据库中读取用户信息
manager_result = db_helper.read(sql)
# 判断用户记录是否存在
if not manager_result:
return web_helper.return_msg(-1, '账户不存在')

  我们可以改造为:

     ##############################################################
### 获取登录用户记录,并进行登录验证 ###
##############################################################
_manager_logic = manager_logic.ManagerLogic()
# 从数据库中读取用户信息
manager_result = _manager_logic.get_model_for_cache_of_where('login_name=' + string(username))
# 判断用户记录是否存在
if not manager_result:
return web_helper.return_msg(-1, '账户不存在')

  还有登录接口最底部,更新管理员最后登录时、登录ip和累加登录次数需要改造,具体代码如下:

 ##############################################################
### 更新用户信息到数据库 ###
##############################################################
# 更新当前管理员最后登录时间、Ip与登录次数(字段说明,请看数据字典)
sql = """update manager set last_login_time=%s, last_login_ip=%s, login_count=login_count+1 where id=%s"""
# 组合更新值
vars = ('now()', ip, manager_id,)
# 写入数据库
db_helper.write(sql, vars)

  我们可以更改为:

     ##############################################################
### 更新用户信息到数据库 ###
##############################################################
# 更新当前管理员最后登录时间、Ip与登录次数(字段说明,请看数据字典)
fields = {
'last_login_time': 'now()',
'last_login_ip': string(ip),
'login_count': 'login_count+1',
}
# 写入数据库
_manager_logic.edit_model(manager_id, fields)

  对于字段值,如果为字符串、具体时间、json等类型的,也就是说需要用单撇号括起来的,我们就需要调用string_helper模块的string方法进行转换,它可以为变量增加单撇号,如果直接赋字符串值,生成的sql语句是没有单撇号的,这里要注意一下

  如果是数值类型,直接写值就可以了,当然直接赋字符串值也没有关系,因为生成sql是不会自动添加单撇号的

  如果要赋postgresql系统变量,如now(),直接像上面这样写就可以了

  如果字段是数值型,要让它进行计算,直接像上面这样写也行,可以是多个字段用加号连起来。当然你也可以将字段时读出来进行计算后再赋值提交也没有问题

  具体操作需要大家自己多debug,多测试使用才知道怎么应用到真实项目中。

  本文对应的源码下载

版权声明:本文原创发表于 博客园,作者为 AllEmpty 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。

python开发QQ群:669058475(本群已满)、733466321(可以加2群)    作者博客:http://www.cnblogs.com/EmptyFS/

我的第一个python web开发框架(33)——接口代码重构的更多相关文章

  1. 我的第一个python web开发框架(41)——总结

    我的第一个python web开发框架系列博文从17年6.7月份开始写(存了近十章稿留到9月份才开始发布),到今天结束,一年多时间,想想真不容易啊. 整个过程断断续续,中间有段时间由于工作繁忙停了好长 ...

  2. 我的第一个python web开发框架(14)——后台管理系统登录功能

    接下来正式进入网站的功能开发.要完成后台管理系统登录功能,通过查看登录页面,我们可以了解到,我们需要编写验证码图片获取接口和登录处理接口,然后在登录页面的HTML上编写AJAX. 在进行接口开发之前, ...

  3. 我的第一个python web开发框架(21)——小结

    这个小网站终于成功上线,小白除了收获一笔不多的费用外,还得到女神小美的赞赏,心中满满的成就感.这一天下班后,他请老菜一起下馆子,兑现请吃饭的承诺,顺便让老菜点评一下. 小白:老大,在你的指导下终于完成 ...

  4. 我的第一个python web开发框架(1)——前言

    由于之前经验不是很丰富,写的C#系统太过复杂,所以一直想重写,但学的越多越觉得自己懂的越少,越觉的底气不足.所以一直不敢动手,在内心深处对自己讲,要静下心来认真学习,继续沉淀沉淀.这两年多以来找各种机 ...

  5. 我的第一个python web开发框架(3)——怎么开始?

    小白与小美公司经过几次接触商谈,好不容易将外包签订了下来,准备开始大干一场.不过小白由于没有太多的项目经验,学过python懂得python的基本语法,在公司跟着大家做过简单功能,另外还会一些HTML ...

  6. 我的第一个python web开发框架(22)——一个安全小事故

    在周末的一个早上,小白还在做着美梦,就收到了小美的连环追魂call,电话一直响个不停. 小白打着哈欠拿起电话:早上好美女. 小美:出事了出事了,我们公司网站一早访问是一片空白,什么内容都没有了,你赶急 ...

  7. 我的第一个python web开发框架(15)——公司介绍编辑功能

    完成登录以后,就会进入后台管理系统的主界面,因为这个是小项目,所以导航菜单全部固化在HTML中,不能修改.一般后台还会有一个欢迎页或关键数据展示的主页面,小项目也没有多大的必要,所以登录后直接进入公司 ...

  8. 我的第一个python web开发框架(16)——产品分类管理

    产品分类管理的html页面之前忘记做了,这次附件里补上. 好了先上图 从页面效果图来看,我们需要开发列表获取接口.添加接口.单条记录获取接口.编辑接口和删除接口 对于产品分类列表,我们将使用jqgri ...

  9. 我的第一个python web开发框架(40)——后台日志与异常处理

    后台权限和底层框架的改造终于完成了,小白也终于可以放下紧悬着的心,可以轻松一下了.这不他为了感谢老菜,又找老菜聊了起来. 小白:多谢老大的帮忙,系统终于改造完成了,可以好好放松一下了. 老菜:呵呵,对 ...

随机推荐

  1. python3+ selenium3开发环境搭建-手把手教你安装python(详细)

    环境搭建 基于python3和selenium3做自动化测试,俗话说:工欲善其事必先利其器:没有金刚钻就不揽那瓷器活,磨刀不误砍柴工,因此你必须会搭建基本的开发环境,掌握python基本的语法和一个I ...

  2. laravel的消息队列剖析

    laravel的消息队列剖析 这篇来自于看到朋友转的58沈剑的一篇文章:1分钟实现"延迟消息"功能 在实际工作中也不止遇见过一次这个问题,我在想着以前是怎么处理的呢?我记得当初在上 ...

  3. java代码之美(7)---guava之Bimap

    guava之Bimap bimap的作用很清晰:它是一个一一映射,可以通过key得到value,也可以通过value得到key. 一.概述 1.bimap和普通HashMap区别 (1)在Java集合 ...

  4. 再见了Server对象,拥抱IHostingEnvironment服务对象(.net core)

    一.绝对路径 1.获取应用程序运行当前目录Directory.GetCurrentDirectory(). System.IO命名空间中存在Directory类,提供了获取应用程序运行当前目录的静态方 ...

  5. RestTemplate的逆袭之路,从发送请求到负载均衡

    上篇文章我们详细的介绍了RestTemplate发送请求的问题,熟悉Spring的小伙伴可能会发现:RestTemplate不就是Spring提供的一个发送请求的工具吗?它什么时候具有了实现客户端负载 ...

  6. nginx部署~dotnetCore+mvc网站502

    这个不是nginx的问题,也不是dotnet core的问题,也不是mvc的问题,更不是防火墙的问题! 原因在于这个SeLinux 把它关了就可以了 setsebool -P httpd_can_ne ...

  7. PE知识复习之PE的重定位表

    PE知识复习之PE的重定位表 一丶何为重定位 重定位的意思就是修正偏移的意思.  如一个地址位 0x401234 ,Imagebase = 0x400000 . 那么RVA就是 1234.  如果Im ...

  8. Magicodes.NET框架之路——V0.0.0.5 Beta版发布

    最近写代码的时间实在不多,而且今年又打算业余学习下Unity3D以及NodeJs(用于开发游戏后台),因此完善框架的时间更不多了.不过我会一直坚持下去的,同时我也希望有兴趣的同学可以加入Push你的代 ...

  9. java内置线程池ThreadPoolExecutor源码学习记录

    背景 公司业务性能优化,使用java自带的Executors.newFixedThreadPool()方法生成线程池.但是其内部定义的LinkedBlockingQueue容量是Integer.MAX ...

  10. C#工具:利用HttpClient调用WebApi

    可以利用HttpClient来进行Web Api的调用.由于WebA Api的调用本质上就是一次普通的发送请求与接收响应的过程, 所有HttpClient其实可以作为一般意义上发送HTTP请求的工具. ...