python で database クラスを作ってみる (修正版)

python で database クラスを作ってみる (Part 5) で作ったクラスでは、python3.6 では動かなかったので、少し改修して python3.6 で動作するようにしました。

動作確認下環境:
1)
CentOS Linux release 7.5
mysql Ver 15.1 Distrib 5.5.56-MariaDB
Python 2.7.5
Python 3.6.5

2)
CentOS release 6.10
mysql Ver 14.14 Distrib 5.1.73
Python 2.6.6
Python 3.6.5

Python の MySQL インターフェースに mysqlclient を使用します。

# pip install mysqlclient
# pip3.6 install mysqlclient

以前の SimpleDB クラスは、Python 3.6 でエラーになってしまうので手直ししたものを再掲します。

SimpleDBクラスのソース: simpledb.py
(ファイル名を simpledb.py_.txt から “simpledb.py” に変更してください。)

Python で例外が発生した場所を取得する

python でプログラミング中、try ~ except で例外を補足しますが、どのコードでその例外が発生したかを特定する方法がわからなかったので調べました。

https://docs.python.org/ja/2.7/library/sys.html

sys.exc_info() で返されるレースバックオブジェクトに設定された現在の行番号 tb_lineno で、例外が発生したコードの行番号がわかります。

import sys, os

try:
    # リスト インデックス範囲外
    for link in navi[0]:
        ・・・(省略)・・・
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print "%s, %s, %s" % (exc_type, fname, exc_tb.tb_lineno)

出力例:

<type 'exceptions.IndexError'>, 'test.py', 131

python で database クラスを作ってみる (Part 4)

今回はテーブルのデータの更新と削除をやります。

データを更新するため、SimpleDB クラスに update 関数を定義します。
引数には table: テーブル名、更新データを辞書型、conditions: 条件を渡します。
関数内では、引数を基にして “UPDATE table SET field = value, …” という SQL 文を組み立てます。
UPDATE が成功すると、影響のあった行数を返し、失敗した場合は、None を返します。

    def update(self, table, data, conditions):
        sql = "UPDATE %s" % table
        bind = []

        if data is not None and len(data) > 0:
            # Build fields
            delim = ""
            sql += " SET "
            for field, value in data.iteritems():
                sql += "%s `%s` = " % (delim, field)
                sql += "%s"
                bind.append(value)
                delim = ","

        if conditions is not None and len(conditions) > 0:
            sql += " WHERE "
            delim = ""

            for field, value in conditions.iteritems():
                if value is None:
                    sql += "%s `%s` IS NULL" % (delim, field)
                else:
                    sql += "%s `%s` = " % (delim, field)
                    sql += "%s"
                    bind.append(value)
                delim = " AND"

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            self.__connection.commit()
            # Return rows affected.
            return self.__cursor.rowcount
        return None
    ## End def update

次に、このテーブルからデータを削除するため、delete 関数を定義します。
引数には table: テーブル名、conditions: 条件を指定する辞書型を渡します。
関数内では、引数を基にして “DELETE FROM table WHERE filed = value AND …” という SQL 文を組み立てます。
DELETE が成功すると、影響のあった行数を返し、失敗した場合は、None を返します。

    def delete(self, table, conditions):
        result = None
        sql = "DELETE FROM %s" % table
        bind = []

        if conditions is not None and len(conditions) > 0:
            sql += " WHERE "
            delim = ""

            for field, value in conditions.iteritems():
                if value is None:
                    sql += "%s `%s` IS NULL" % (delim, field)
                else:
                    sql += "%s `%s` = " % (delim, field)
                    sql += "%s"
                    bind.append(value)
                delim = " AND"

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            self.__connection.commit()
            # Return rows affected.
            return self.__cursor.rowcount
        return None
    ## End def delete
## End class

python で database クラスを作ってみる (Part 3)

今回はテーブルにデータを挿入して、選択するところまでやります。

テーブルにデータを挿入するため、SimpleDB クラスに insert 関数を定義します。
引数には table: テーブル名、data: 挿入するデータを辞書型で渡します。
関数内では、引数を基にして “INSERT table INTO (field, field…) VALUES (value, value…)” という SQL 文を組み立てます。
値はプリペアードステートメントで渡すので %s で組み立てておいて self.__cursor.execute(sql, bind) でバインドしています。
INSERT が成功すると、最後の行ID self.__cursor.lastrowid を返し、失敗した場合は、None を返します。

class SimpleDB(object):
... (省略)...

    def insert(self, table, data):
        sql = "INSERT INTO %s" % table
        bind = []

        if data is not None and len(data) > 0:
            # Build fields
            delim = ""
            sql += " ("
            for field, value in data.iteritems():
                sql += "%s `%s`" % (delim, field)
                delim = ","
            sql += ")"

            # Build values
            delim = ""
            sql += " VALUES ("
            for field, value in data.iteritems():
                sql += "%s " % (delim)
                sql += "%s"
                bind.append(value)
                delim = ","
            sql += ")"

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            self.__connection.commit()
            # Return last row ID.
            return self.__cursor.lastrowid
        return None
    ## End def insert

... (省略)...
## End class

次に、このテーブルからデータを取得するため、select 関数を定義します。
引数には table: テーブル名、conditions: 条件を指定する辞書型、sortby: ソートキー、sortdesc: ソート降順(True) を渡します。

関数内では、引数を基にして “SELECT * FROM table WHERE filed = value AND …” という SQL 文を組み立てます。
値はプリペアードステートメントで渡すので %s で組み立てておいて self.__cursor.execute(sql, bind) でバインドしています。
SELECT が成功すると、取得データを辞書型のリストで返し、失敗した場合は、None を返します。

class SimpleDB(object):
... (省略)...

    def select(self, table, conditions, sortby = None, sortdesc = False):
        result = None
        sql = "SELECT * FROM %s" % table
        bind = []

        if conditions is not None and len(conditions) > 0:
            sql += " WHERE "
            delim = ""

            for field, value in conditions.iteritems():
                if value is None:
                    sql += "%s `%s` IS NULL" % (delim, field)
                else:
                    sql += "%s `%s` = " % (delim, field)
                    sql += "%s"
                    bind.append(value)
                delim = " AND"

        if sortby is not None:
            sql += " ORDER BY %s %s" % (sortby, "DESC" if sortdesc else "ASC")

        if self.__cursor is not None:
            self.__cursor.execute(sql, bind)
            columns = self.__cursor.description
            result = []
            for row in self.__cursor.fetchall():
                item = {}
                for (index, value) in enumerate(row):
                    item[columns[index][0]] = value
                result.append(item)
        return result
    ## End def select

... (省略)...
## End class

python で database クラスを作ってみる (Part 2)

今回はクラスの定義とデータベースに接続、開く、閉じるところまでやっています。
クラス名は SimpleDB 、ファイルを simpledb.py とします。

コンストラクタで接続情報を渡し、open 関数で接続、カーソルを変数に格納しておきます。
close 関数でカーソルを閉じ、接続を閉じます。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import MySQLdb
from collections import OrderedDict

class SimpleDB(object):
    __host       = None
    __user       = None
    __password   = None
    __database   = None
    __cursor    = None
    __connection = None

    def __init__(self, host, database, user, password):
        self.__host = host
        self.__database = database
        self.__user = user
        self.__password = password
    ## End def __init

    def open(self):
        con = MySQLdb.connect(self.__host, self.__user, self.__password, self.__database)
        self.__connection = con
        self.__cursor = con.cursor()
    ## End def open

    def close(self):
        self.__cursor.close()
        self.__connection.close()
    ## End def close
## End class

if __name__ == '__main__':
    dbconfig = {
        "host": "localhost",
        "database": "<your database>",
        "user": "<your username>",
        "password": "<your secure password>",
        }

    try:
        db = SimpleDB(dbconfig['host'], dbconfig['database'], dbconfig['user'], dbconfig['password'])

        db.open()
        db.close()
    except Exception as e:
        print e.args