問い合わせ項目を扱うクラスの中身
担当者が異動してしまうからといって、作りかけで投げてしまっては意味がない。K-enq.tdの開発は続いています。
さて、アンケートフォームの定義を扱うクラスとは別に、各フォームに追加される質問の項目を管理するクラスを作っていました。といっても、基本的にはアンケートフォームのクラス、FormDefinitionにそんなに違っていせん。
FormDefinitionとFormItemsの両方で使いたいメソッドがあったので、それらをFormsというクラスに持たせておいて、継承することにしました。こういうやりかたが正しいかどうかはわからないけど、まあ便利なのでいいことにしよう。
class Forms: # internal methods def _open_db(self): return sqlite3.connect(self.dbpath) def _pickle(self, value): return base64.encodestring(pickle.dumps(value)) def _unpickle(self, value): if value is None or value == u'None': return u'' else: return pickle.loads(base64.decodestring(value)) def _generate_id(self, digit=8): if not isinstance(digit, int): raise TypeError(u'need int, got %r' % \ type(digit)) seed = str(random.randint(0, sys.maxint - 1)) message = hashlib.new('sha512') message.update(seed) temp_id = message.hexdigest() if len(temp_id) > digit: return temp_id[0:digit] else: return temp_id
FormItemsでは、登録されている質問項目をソートするのに、order_numberというフィールドを使っています。ここに連番がふられていて、その番号にしたがって並べられるようにしました。なので、項目を追加する際にorder_numberをインクリメントできるように、最大値を知るメソッド_max_order_number()を作りました。最初は_count_item()を使ってたんですが、途中で抜けが発生したらいかんなと思いなおして、SQLiteの関数、max()を使ったものに置き換えました。
def _count_item(self, cursor, iid): cursor.execute('select count(*) from items where iid=?;', \ (iid,)) recordcount = cursor.fetchone() return recordcount[0] def _max_order_number(self): connection = self._open_db() cursor = connection.cursor() cursor.execute('select max(order_number) from items where fid=?', \ (self.fid,)) recordcount = cursor.fetchone() cursor.close() connection.close() return recordcount[0]
この連番があるので、削除の際には連番を崩さないよう、詰めてやらないといけません。SQLで対象となるレコードを絞り込んで、order_number=(order_number - 1)とすることにしました。こういうのが一発でできるので、SQLは便利ですね。
def delete_item(self, iid): if not isinstance(iid, basestring): raise TypeError(u'need str or unicode, got %r' % \ type(iid)) connection = self._open_db() cursor = connection.cursor() try: cursor.execute('select order_number from items \ where iid=? and fid=?;', \ (iid, self.fid)) record = cursor.fetchone() except: cursor.close() connection.close() raise tonum = None if isinstance(record, tuple): tonum = record[0] if tonum is not None: try: cursor.execute('delete from items where iid=? and fid=?;', \ (iid, self.fid)) except: cursor.close() connection.close() raise try: cursor.execute('update items \ set order_number=(order_number - 1) \ where fid=? and order_number>?;', \ (self.fid, tonum)) except: cursor.close() connection.close() raise cursor.close() connection.commit() connection.close()
項目の並べ替えも、delete_item()の応用です。対象のIDを指定し、それをどれだけずらしたいか数値で指定します。1とするとひとつ上昇し、-2とするとふたつ下がります。この移動量が項目の数を超えてしまったときは、OutOfRangeErrorを発生させることにして、それをキャッチするなりなんなりするなりは、その都度考えて書けばいいんじゃない? ということにします。
def reorder_item(self, iid, distance): if not isinstance(distance, int): raise TypeError(u'need int, got %r' % \ type(distance)) connection = self._open_db() cursor = connection.cursor() try: cursor.execute('select order_number from items \ where iid=? and fid=?;', \ (iid, self.fid)) except: cursor.close() connection.close() raise order_number = cursor.fetchone() if isinstance(order_number, tuple): order_number = order_number[0] if isinstance(order_number, basestring): order_number = int(order_number) new_number = order_number - distance max_number = self._max_order_number() if 0 <= new_number and new_number <= max_number: if new_number < order_number: try: cursor.execute('update items \ set order_number=(order_number + 1) \ where fid=? and order_number<? and order_number>=?;', \ (self.fid, order_number, new_number)) except: cursor.close() connection.close() raise elif new_number > order_number: try: cursor.execute('update items \ set order_number=(order_number - 1) \ where fid=? and order_number>? and order_number<=?;', \ (self.fid, order_number, new_number)) except: cursor.close() connection.close() raise try: cursor.execute('update items \ set order_number=? \ where iid=? and fid=?;', \ (new_number, iid, self.fid)) except: cursor.close() connection.close() raise else: cursor.close() connection.close() raise OutOfRangeError(u'Distance Outs of Range') cursor.close() connection.commit() connection.close()
とりあえず、今日はこれくらい。疲れてしまって、もう頭がまわりません。