美文网首页
python对于mysql数据库操作类的相关方法及感悟

python对于mysql数据库操作类的相关方法及感悟

作者: PeterPZ | 来源:发表于2019-06-25 17:06 被阅读0次

    概述

    本着磨刀不误砍柴工、高内聚低耦合的代码原则,带着心中向往的行云流水的敲代码向往,秉承绝不走回头路、我的代码我最屌的原则记录了此文。

    对于数据库数据的操作,力要写一个万能、通用且高效率的数据库操作类。
    话不多说,直接上代码:

    import pymysql
    
    
    class MysqlHelper:
        def __init__(self):
            self._user = db_user
            self._password = db_pwd
            self._charset = 'utf8'
            self._port = port
            self._host = db_host
            self._db_name = db_name
            self._conn = self.connect_mysql()
            if self._conn:
                self._cursor = self._conn.cursor()
    
        def connect_mysql(self):
            """
            连接数据库
            :return:
            """
            conn = pymysql.connect(host=self._host,
                                   user=self._user,
                                   passwd=self._password,
                                   db=self._db_name,
                                   port=self._port,
                                   cursorclass=pymysql.cursors.DictCursor,
                                   charset=self._charset,
                                   )
            return conn
    
        def close(self):
            """
            关闭数据库连接
            :return:
            """
            self._cursor.close()
            self._conn.close()
    
        def execute(self, *args, params=None):
            """
            执行多条sql
            :param params:
            :param args:
            :return:
            """
            if params is None:
                params = []
            effect = 0
            for sql in args:
                num = self._cursor.execute(sql, params)
                effect += num
            self._conn.commit()
            self.close()
            return effect
    
        def select_multi(self, *args, params=None):
            """
            查询语句,可以执行多条查询
            :param args:
            :param params:
            :return: 返回元祖res:结果,num查询出行数
            """
            if params is None:
                params = []
            i = 1
            res = {}
            for sql in args:
                num = self._cursor.execute(sql, params)
                sql_results = self._cursor.fetchall()
                res['result%s' % i] = sql_results
                res['effect%s' % i] = num
                i += 1
            self.close()
            return res
    
        def select(self, sql, act='all', params=None):
            """
            查询语句方法
            :param act:
            :param sql:
            :param params:
            :return: 返回字典res:结果,num查询出行数
            """
            global res
            if params:
                pass
            else:
                params = []
            if act == 'all':
                num = self._cursor.execute(sql)
                sql_results = self._cursor.fetchall()
                res = {
                    'result': sql_results,
                    'effect': num
                }
            self.close()
            return res
    
    

    好了,以上代码就是一个基本上可以直接使用的数据库操作类了,简述来说呢就是select_multi方法用来执行多条查询sql语句,select方法来执行单条语句(区别不大,但是各有用处),execute方法来执行多条操作语句。

    之所以写了一个select_multi方法和execute通用多条语句就是用来仿写数据库中的事务操作,减少了每条语句对于数据库的连接、访问、关闭的这一系列操作,提高了效率。
    但是对于cursor().execute()方法又是比较特殊的。

    一、问题所在

    先粘一段源代码:

    def execute(self, query, args=None):
            """Execute a query
    
            :param str query: Query to execute.
    
            :param args: parameters used with query. (optional)
            :type args: tuple, list or dict
    
            :return: Number of affected rows
            :rtype: int
    
            If args is a list or tuple, %s can be used as a placeholder in the query.
            If args is a dict, %(name)s can be used as a placeholder in the query.
            """
            while self.nextset():
                pass
    
            query = self.mogrify(query, args)
    
            result = self._query(query)
            self._executed = query
            return result
    

    可以看出来execute方法里面有两个参数而对于args是非必传,查看过文档,在调用时的param参数在使用时是用来避免sql注入而存在的,这样就能避免了恶意攻击数据库等行为。但是了解过这点之后,在某些sql语句中例如

    DATE_FORMAT(CURRENT_DATE,'%Y-%m-%d')
    

    日期函数中的%Y却又会报错,网上很多方法都是将单%变成双%避免被认定为占位符而存在。

    报错截图.jpg

    二、水落石出

    对于Python中的三引号,将单%变为双%在更新之后又不好用了,会直接认定为是字符串%%,好坑......
    于是再次观察源代码在execute()中的mogrify()方法

        def mogrify(self, query, args=None):
            """
            Returns the exact string that is sent to the database by calling the
            execute() method.
    
            This method follows the extension to the DB API 2.0 followed by Psycopg.
            """
            conn = self._get_db()
            if PY2:  # Use bytes on Python 2 always
                query = self._ensure_bytes(query, encoding=conn.encoding)
    
            if args is not None:
                query = query % self._escape_args(args, conn)
    
            return query
    

    传入了上层函数传来的args参数、而在函数中对于args进行了一次判定,而对于避免sql注入而传入的param参数,尽管为空,但是是一个空列表而非None对象,所以会卡在self._escape_args(args, conn)方法上,结果清晰明了了。
    那么,改进!

        def select(self, sql, act='all'):
            """
            查询语句方法
            :param act:
            :param sql:
            :param params:
            :return: 返回字典res:结果,num查询出行数
            """
            global res
            if act == 'all':
                num = self._cursor.execute(sql)
                sql_results = self._cursor.fetchall()
                res = {
                    'result': sql_results,
                    'effect': num
                }
            self.close()
            return res
    

    这样就通用的多了,舒服。当然,也可以自己加很多种操作方法,那就取决于你啦。玩的开心。

    相关文章

      网友评论

          本文标题:python对于mysql数据库操作类的相关方法及感悟

          本文链接:https://www.haomeiwen.com/subject/plqxcctx.html