ユーザ名簿のクラス Roster

d:id:imait:20090216:1234784679で、ユーザに関してはUserクラスを作るのではなくて、名簿のクラスを作ろうといっていました。といったわけで、今日は名簿を表現するクラス、Rosterを作っていました。

とりあえず、例示していたexists、countメソッドを作って、それからcreate_userとdelete_userメソッドを。パスワードはSHA256でハッシュにして保存することにしたので、後からパスワードを覗き見ることができません。

後は、Exceptionを真面目に用意することにして、けど例外にはなれていないので、自分のやりかたがいい方法かどうかはわからない。あと、try: except: finally: の使いかたもいまいちよくわかっていない感じです。

具体的にいうと、データベースではフィールドuid(ユーザID)を主キーにしているため、同じuidを追加しようとするとデータベースの不整合が生じたよというエラーが返ってきます。だから私は、追加しようとしているuidがテーブルに存在していないことを事前に確認してから追加するというようにしたのですが、もしかしたらこういったまどろっこしい方法をとるのではなく、発生する例外をうまくキャッチしてやる方がずっとスマートなんじゃないかとも思うんですね。ifで囲む囲まないに関わらず、どうせtryは使うんですから。

職場の技術の人に相談してみたら、確認した方が丁寧ですが、コードが増えるし、きりもないから、確認せず例外を受けるだけにするというのも手ですよ。どっちでもいいようにいわれて、だからどうしたものかと。

以下に書きかけですが、Rosterを掲載しておきます。

# -*- coding: utf-8 -*-
'''
Class Roster which treats users with sqlite3
'''

import hashlib
import sqlite3
import pickle
import base64


class Roster:
    '''Class of Roster of users'''

    def __init__(self, dbpath):
        self.dbpath = dbpath
        self.dbtablename = u'usertable'

        connection = self._open_db()
        cursor = connection.cursor()
        cursor.execute('select * from sqlite_master \
        where type=\'table\' and name=?;', \
                       (self.dbtablename, ))
        tablecount = cursor.fetchall()
        if len(tablecount) == 0:
            cursor.execute('create table %s (uid primary key, password, \
            data, created_time, accessed_time);' \
                           % self.dbtablename)
        cursor.close()
        connection.commit()
        connection.close()


    def exists(self, uid):
        if isinstance(uid, basestring):
            connection = self._open_db()
            cursor = connection.cursor()
            recordcount = self._count_user(cursor, uid)
            cursor.close()
            connection.close()
            if recordcount == 1:
                return True
            else:
                return False
        else:
            raise UserUnknownError

    def count(self):
        connection = self._open_db()
        cursor = connection.cursor()
        cursor.execute('select count(*) from %s;' \
                       % self.dbtablename)
        recordcount = cursor.fetchone()
        cursor.close()
        connection.close()
        return recordcount[0]

    def create_user(self, uid, password):
        message = hashlib.new('sha256')
        message.update(password)
        password = message.hexdigest()

        connection = self._open_db()
        cursor = connection.cursor()
        recordcount = self._count_user(cursor, uid)
        if recordcount == 0:
            try:
                cursor.execute('insert into %s (uid, password, created_time, \
                accessed_time) values(\'%s\', \'%s\', datetime(\'now\'), \
                datetime(\'now\'));' \
                               % (self.dbtablename, uid, password))
            except:
                raise
            finally:
                cursor.close()
                connection.commit()
                connection.close()
        else:
            cursor.close()
            connection.close()
            raise UserExistsError(u'user ' + uid + u' already exists')

    def delete_user(self, uid):
        connection = self._open_db()
        cursor = connection.cursor()
        recordcount = self._count_user(cursor, uid)
        if recordcount == 1:
            try:
                cursor.execute('delete from %s where uid=\'%s\';' \
                               % (self.dbtablename, uid))
            except:
                raise
            finally:
                cursor.close()
                connection.commit()
                connection.close()
        else:
            cursor.close()
            connection.close()
            raise UserNotExistsError(u'user ' + uid + u' not exists')

    # internal methods

    def _open_db(self):
        return sqlite3.connect(self.dbpath)

    def _count_user(self, cursor, uid):
        cursor.execute('select count(*) from %s where uid=\'%s\';' \
                       % (self.dbtablename, uid))
        recordcount = cursor.fetchone()
        return recordcount[0]



# Exceptions

class Error(Exception):
    '''Base class for exception in this module.'''
    pass

class UserExistsError(Error):
    '''Exception raised when created user which already exists'''

    def __init__(self, message):
        self.args = (message, )

class UserNotExistsError(Error):
    '''Exception raised when deleted user which not exists'''

    def __init__(self, message):
        self.args = (message, )

class UserUnknownError(Error):
    '''Exception raised when user is unknown.'''

    def __init__(self, message):
        self.args = (message, )

class PasswordIncorrectError(Error):
    '''Exception raised when password is incorrect.'''

    def __init__(self, message):
        self.args = (message, )