[接口测试-实战]01 python3操作mysql

前言

最近跟大婶的一个课程(可以到千聊平台搜索“颠覆你的python接口自动化测试”),主要讲通过py+httplib+mysql+Django来搭建接口测试的一个实战,课程使用的是环境和我的环境有点区别,课程使用的是py2+httplib,而我使用的是py3+requests。主要跟的其实还是思想,而且我也收获良多,这里我会将学习的结果输出到简书,供应大家一起学习,当然我自认为写代码的能力和经验有限,所以可能会出一些低级的问题,希望各位指点,我们一起学习,进步。

这里我记录一下,我在跟第四节课《python操作mysql》的成果记录以及遇到的一些坑。

文章目录

  1. 课程内容简介
  2. 实践代码

课程内容简介

大婶的第四次分享《python操作mysql》主要包括三个内容,“py操作mysql实例”,“本章涉及的语法讲解”,“pycharm使用基础”,本文的主要内容还是“py操作mysql实例”,其他的部分,在后面的文章中陆续更新。

那么下面我们对实例部分做一个简单的介绍,课程主要对mysqldb进行一个简单再封装的操作,提供几个框架中会用到的一些基本功能,功能比较简单(代码中有注释),而mysqldb暂时不支持py3,所以实战中我使用的是pymysql这个模块。(不要问我为什么知道,我不告诉你)

实战代码

实战涉及到的新知识(这里只是记录一些关键字,后面会有相关的文章输出):logging模块,pymysql模块,单例设置模式

实战中我封装了两个文件,opmysql.py(数据库操作的封装),mylogging.py(logging日志的封装)

在看代码之前,我先抛出几个问题,大家可以带着这些问题看代码,印象可能会更深刻,当然很多问题都可以在代码中找到答案,毕竟,我还是很喜欢写注释的。

  1. 课程中每次使用logging的时候都需要设置输出格式信息,怎么封装
  2. 希望整个系统公用一个logging实例,怎么做
  3. 除了捕获到Exception的时候需要输入log,还有什么时候需要输出log,才能帮助我在出现bug的时候定位问题
  4. 凡是有sql的地方都有sql注入的问题,所以,在封装的过程中,我们该怎么处理sql注入的问题
# mylogging.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import logging

#在实际开发中,不会直接用logging,一般会将logging封装一下,再用

class MyLogging:
    '''
    logging的初始化操作,以类封装的形式进行
    '''
    def __init__(self):
        filename = "app.log" #日志文件的地址
        self.logger = logging.getLogger() #定义对应的程序模块名name,默认为root
        self.logger.setLevel(logging.INFO)  #必须设置,这里如果不显示设置,默认过滤掉warning之前的所有级别的信息

        sh = logging.StreamHandler() #日志输出到屏幕控制台
        sh.setLevel(logging.ERROR) #设置日志等级

        fh = logging.FileHandler(filename=filename) #向文件filename输出日志信息
        fh.setLevel(logging.INFO) #设置日志等级

        # 设置格式对象
        formatter = logging.Formatter("%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s - %(message)s") #定义日志输出格式

        # 设置handler的格式对象
        sh.setFormatter(formatter)
        fh.setFormatter(formatter)

        # 将handler增加到logger中
        self.logger.addHandler(sh)
        self.logger.addHandler(fh)

# python的模块是天然的单例模式,因为模块在第一次导入的时候,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码
# 因此,我们只需要把相关的文件和数据定义在一个模块中,就可以获得一个单例对象了

# 日志操作对象logger,这里设置为单例,则凡是需要使用logging对象的地方都共同这个logger,节省内存开销
mylogger =  MyLogging().logger

if __name__=="__main__":
    #test
    mylogger.debug("debug")
    mylogger.info("info")
    mylogger.warning("warning")
    mylogger.error("error")
    mylogger.critical("critical")

# opmysql.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'''
定义对mysql数据库基本操作的封装
1.单条数据的操作:insert,update,delete
2.查询数据表的一条数据
3.查询数据表的所有数据
'''

import  pymysql
from mylogging import mylogger

class OpMysql:
    '''mysql操作类,基于pymysql的封装'''
    def __init__(self,host="127.0.0.1",username="root",password="",databasename="dsn"):
        '''
        初始化opmysql对象
        :param host:数据库服务器地址
        :param username:数据库用户名
        :param password:用户的密码
        :param databasename:要操作的库名称
        '''
        try:
            self.conn = pymysql.connect(
                host = host,
                user = username,
                password = password,
                database = databasename,
                charset = 'utf8', # 如果sql语句中存在中文字符的时候,需要在这里指定charset的参数,否则中文显示乱码
                cursorclass = pymysql.cursors.DictCursor # pymysql默认select获取的数据是元祖类型,如果想要字典类型的数据,可以在这里统一设置
            )
            self.cur = self.conn.cursor() #创建游标
        except pymysql.Error as e:
            mylogger.info("[connection_message]-host:%s;user:%s;password:%s;database:%s"%(host,username,password,databasename))
            mylogger.exception(e)

    def op_sql(self,query,params=None):
        '''
        单条数据的操作,insert,update,delete
        :param query:包含%s的sql字符串,当params=None的时候,不包含%s
        :param params:一个元祖,默认为None
        :return:如果执行过程没有crash,返回True,反之返回False
        '''
        try:
            self.cur.execute(query,params)
            self.conn.commit()
            return True
        except BaseException as e:
            self.conn.rollback() #如果这里是执行的执行存储过程的sql命令,那么可能会存在rollback的情况,所以这里应该考虑到
            mylogger.info("[sql_str_message]-%s"%self.cur.mogrify(query,params))
            mylogger.exception(e)
            return False

    def select_one(self,query,params=None):
        '''
        查询数据表的单条数据
        :param query: 包含%s的sql字符串,当params=None的时候,不包含%s
        :param params: 一个元祖,默认为None
        :return: 如果执行未crash,并以包含dict的列表的方式返回select的结果,否则返回错误代码001
        '''
        try:
            self.cur.execute(query,params)
            self.cur.scroll(0,"absolute") #光标回到初始位置,感觉自己的这句有点多余
            return self.cur.fetchone()
        except BaseException as e:
            mylogger.info("[sql_str_message]-%s" % self.cur.mogrify(query, params))
            mylogger.exception(e)
            return "001" #错误代码001

    def select_all(self,query,params=None):
        '''
        查询数据表的单条数据
        :param query:包含%s的sql字符串,当params=None的时候,不包含%s
        :param params:一个元祖,默认为None
        :return:如果执行未crash,并以包含dict的列表的方式返回select的结果,否则返回错误代码001
        '''
        try:
            self.cur.execute(query,params)
            self.cur.scroll(0,"absolute") #光标回到初始位置,感觉这里得这句有点多余
            return self.cur.fetchall()
        except BaseException as e:
            mylogger.info("[sql_str_message]-%s" % self.cur.mogrify(query, params))
            mylogger.exception(e)
            return "001" #错误代码001

    def insert_many(self,query,params):
        '''
        向数据表中插入多条数据
        :param query:包含%s的sql字符串,当params=None的时候,不包含%s
        :param params:一个内容为元祖的列表
        :return:如果执行过程没有crash,返回True,反之返回False
        '''
        try:
            self.cur.executemany(query,params)
            self.conn.commit()
            return True
        except BaseException as e:
            self.conn.rollback()
            mylogger.info("[sql_str_message]-%s" % self.cur.mogrify(query, params))
            mylogger.exception(e)
            return False

    def __del__(self):
        '''
        当该对象的引用计数为0的时候,python解释器会自动执行__dell__方法,自动释放游标和链接
        '''
        self.cur.close()
        self.conn.close()

if __name__ == "__main__":
    # test
    opmysql = OpMysql()
    print(opmysql.select_one("SELECT * FROM user WHERE id = %s;",(1,)))
    opmysql.select_one("SELECT * FROM user WHERE idd = %s;",("12",))

填坑指南

怎么样,看完上面的代码,你找到前面问题的答案没有,如果没有,继续看下去,我会给你参考链接,拿走学习吧。

【问】课程中每次使用logging的时候都需要设置输出格式信息,怎么封装
【答案】代码中有答案,同时可以参考这里

【问】希望整个系统公用一个logging实例,怎么做
【答案】单例模式(Singleton Pattern),是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在,当你希望在整个系统中,某个类只能出现一个实例的时候,单例对象就能派上用处,比如我们实践代码中的mylogging类就可以采用单例模式的方式来处理,节约内存资源。python中实现单例模式的方式有很多种,这里使用模块的方式来获得一个单例对象,具体实现方式,代码中有,自己找,当然你也可以参考这里

【问】除了捕获到Exception的时候需要输入log,还有什么时候需要输出log,才能帮助我在出现bug的时候定位问题
【答案】在错误日志里面除了有exception的具体内容而外,还应该打印sql信息,函数入参等,在报错的时候,除了提供的出错模块,行数,类型等信息之外,如果能够知道是在传递入什么内容之后执行出错的,可以更好的定位问题并分析原因。

【问】凡是有sql的地方都有sql注入的问题,所以,在封装的过程中,我们该怎么处理sql注入的问题 **
【答案】pymysql模块的execute/callproc方法的参数化语句自动防注入,所以我们在进行sql拼接的时候尽量使用pymysql的execute和callproc方法的参数化语句,具体可以查看我之前的笔记做了解《python3使用pymysql操作mysql》

这些都是课程里面没有覆盖到的地方,也是我自己在实践中的疑问和解决结果

好啦,暂时分享到这里,自己文笔真的太有限了,看来在未来,要好好研究一下写作了,不过,只要你们看得懂就行。

    原文作者:亭子青年
    原文地址: https://www.jianshu.com/p/a11a6c34e2c1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞