前面 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, 多测试使用才知道怎么应用到真实项目中.
来源: https://www.cnblogs.com/EmptyFS/p/9508636.html