четверг, 29 апреля 2010 г.

Детальные метаданные sqlite3

В одном из предыдущих постов я описывал, как получить список таблиц в базе sqlite, и сокрушался, что непонятно, как получить детальную информацию о них.

Знающие люди ткнули носом в раздел документации sqlite, где описан набор прагм (pragma) для получения исчерпывающей информации о структуре базы. Подробно расписывать не буду, разработчики sqlite прекрасно сделали это сами.

Как выяснилось, эта метаинформация может быть довольно полезной при разработке интерфейсов к базе данных.


Для ее извлечения из базы применяю такой вот класс:
from PyQt4.QtSql import QSqlQuery

class DBMetadata(dict):
    u'''
    Описывает метаданные базы данных sqlite.
    Заполняется при создании, использует соединение по умолчанию.
    '''

    class TableMetadata(object):
        u'''
        Описывает структуру таблицы sqlite (поля, внешние ключи).
        Заполняется при создании, использует соединение по умолчанию.
        '''

        class FieldDetail(object):
            u'''
            Описание поля таблицы
            (индекс, тип, обязательность, значение по умолчанию).
            '''

            def __init__(self, query):
                self.cid = query.value(0).toInt()[0]
                self.type = unicode(query.value(2).toString())
                self.required = query.value(3).toBool()
                self.default = query.value(4)

        class FKDetail(object):
            u'''
            Описание внешнего ключа.
            '''

            def __init__(self, query):
                self.table = unicode(query.value(2).toString())
                self.to = unicode(query.value(4).toString())
                self.on_update = unicode(query.value(5).toString())
                self.on_delete = unicode(query.value(6).toString())
                self.match = unicode(query.value(7).toString())

        def __init__(self, table_name):
            self.fld = {}
            qpragma = QSqlQuery(u'pragma table_info({0})'.format(table_name))
            qpragma.exec_()
            while qpragma.next():
                self.fld[unicode(qpragma.value(1).toString())] = self.FieldDetail(qpragma)

            self.fk = {}
            qpragma = QSqlQuery(u'pragma foreign_key_list({0})'.format(table_name))
            qpragma.exec_()
            while qpragma.next():
                self.fk[unicode(qpragma.value(3).toString())] = self.FKDetail(qpragma)


    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        qsel = QSqlQuery(u"select tbl_name from sqlite_master "
                         "where type = 'table' and tbl_name not like 'sqlite%'")
        qsel.exec_()
        while qsel.next():
            table_name = unicode(qsel.value(0).toString())
            self[table_name] = self.TableMetadata(table_name)


Несколько примеров использования:
md = DBMetadata()
# порядковый номер поля sometable.somefield (начиная с 0)
md[u'sometable'].fld[u'somefield'].cid
# тип поля sometable.somefield
md[u'sometable'].fld[u'somefield'].type 
# имя таблицы, на которую ссылается внешний ключ sometable.somefieldfk
md[u'sometable'].fk[u'somefieldfk'].table 
# имя поля, на которое ссылается внешний ключ sometable.somefieldfk
md[u'sometable'].fk[u'somefieldfk'].to 


Использовать метаданные можно в следующих задачах:
  • формирование модельного индекса ячейки модели по индексу строки и имени поля в таблице (а также все остальные задачи, где требуется индекс колонки);
  • пакетное создание моделей для всех таблиц базы;
  • пакетный маппинг виджетов и колонок модели (при использовании QDataWidgetMapper);
  • проверка возможности удаления записи модели (ссылочная целостность);
  • и, наверняка, многое другое...

Об использовании метаданных в реальном проекте с примерами кода по всем задачам напишу отдельный пост. Продолжение следует...

Комментариев нет:

Отправить комментарий