def getCount(cls, db, where = None): """Request number of records in this table. """ cls.checkTable(db) count = cls.COUNT(where) count = db.select(count, from_ = cls).value(0, count) logger.debug('Model.getCount(%s, db= %s, where= %s) = %s' % (cls, db, where, count)) return count
def __new__(cls, name, bases, attrs): NewModel = type.__new__(cls, name, bases, attrs) NewModel._name = NewModel.__dict__.get('_name', name.lower()) # db table name if NewModel._name is None: # we need only Model subclasses; if db table name is None - __new__ is called for Model itself return NewModel # return without any processing logger.debug('Finishing initialization of model `%s`' % NewModel) # assure each class has its own attribute, because by default _indexes is inherited from the parent class NewModel._indexes = list(NewModel._indexes) attrs = OrderedDict(inspect.getmembers(NewModel)) fields = [] for fieldName, field in attrs.items(): if isinstance(field, orm.fields.Field): fields.append((fieldName, field)) # sort by definition order (as __dict__ is unsorted) - for field recreation order fields = OrderedDict(sorted(fields, key = lambda f: f[1]._id)) for fieldName, field in fields.items(): if not fieldName.islower() or fieldName.startswith('_'): raise orm.ModelError('Field `%s` in model `%s`: field names must be lowercase and ' 'must not start with `_`.' % (fieldName, name)) # recreate the field - to handle correctly inheritance of Tables newField = field.__class__(name = fieldName, table = NewModel, label = field.label) try: newField._init_(*field._initArgs, **field._initKwargs) # and initialize it except Exception: print('Failed to init a field:', fieldName, field._initArgs, field._initKwargs) raise # each class has its own field object. Inherited and parent tables do not share field attributes setattr(NewModel, fieldName, newField) indexesDict = OrderedDict() # to filter duplicate indexes by index name for index in NewModel._indexes: if index.table is not NewModel: # inherited index if not isinstance(index, orm.Index): raise orm.ModelError('Found a non Index in the _indexes.') if index.table is not NewModel: # index was inherited from parent model - recreate it with fields from new model indexFields = [orm.IndexField(NewModel[indexField.field.name], indexField.sortOrder, indexField.prefixLength) for indexField in index.indexFields] # replace fields by name with fields from new model index = orm.Index(indexFields, index.type, index.name, index.method, **index.other) for indexField in index.indexFields: if issubclass(NewModel, indexField.field.table): indexField.field = NewModel[indexField.field.name] # to assure that field from this model, and from parent, is used else: raise orm.ModelError('Field `%s` in index is not from model `%s`.' % (indexField.field, NewModel)) indexesDict[index.name] = index NewModel._indexes = indexesDict.values() NewModel._ordering = list() NewModel._checkedDbs = set() return NewModel
def _execute(self, *a, **b): query = a[0] logger.debug('DB query: %s' % query) t0 = time.time() try: result = self.cursor.execute(*a, **b) except Exception: logger.warning(query) raise self._timings.append((query, round(time.time() - t0, 4))) return result
def __init__(self, uri = '', connect = True, autocommit = True): """URI is already without protocol.""" self.uri = uri logger.debug('Creating adapter for `%s`' % self) self._timings = [] if connect: self.connection = self.connect() self.cursor = self.connection.cursor() else: self.connection = None self.cursor = None self.autocommit = autocommit
def getColumns(self, tableName): """Get columns of a table""" self.execute("PRAGMA table_info('%s')" % tableName) # name, type, notnull, dflt_value, pk columns = {} for row in self.cursor.fetchall(): logger.debug('Found table column: %s, %s' % (tableName, row)) typeName = row[2].lower() # INTEGER PRIMARY KEY fields are auto-generated in sqlite # INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! autoincrement = bool(typeName == 'integer' and row[5]) if 'int' in typeName or 'bool' in typeName: # booleans are sotred as ints in sqlite typeName = 'int' elif typeName not in ('blob', 'text'): raise TypeError('Unexpected data type: %s' % typeName) column = Column(type = typeName, field = None, name = row[1], default = row[4], precision = 19, nullable = (not row[3]), autoincrement = autoincrement) columns[column.name] = column logger.debug('Reproduced table column: %s, %s' % (tableName, column)) return columns
def checkTable(cls, db): """Check if corresponding table for this model exists in the db and has all necessary columns. Add checkTable call in very model method that uses a db. """ assert isinstance(db, orm.GenericAdapter), 'Need a database adapter' if db.uri in cls._checkedDbs: # this db was already checked return logger.debug('Model.checkTable: checking db table %s' % cls) tableName = cls._name if tableName not in db.getTables(): cls._handleTableMissing(db) # import pprint modelColumns = {field.column.name: field.column for field in cls} dbColumns = db.getColumns(tableName) # logger.debug(pprint.pformat(list(column.str() for column in dbColumns.values()))) # logger.debug(pprint.pformat(list(column.str() for column in modelColumns.values()))) for columnName, column in modelColumns.items(): dbColumn = dbColumns.pop(columnName, None) if not dbColumn: # model column is not found in the db print('Column in the db not found: %s' % column.str()) logger.debug('CREATE TABLE query:\n%s' % db.getCreateTableQuery(cls)) cls._checkedDbs.add(db.uri)
def get(cls, db, where, orderby = False, limit = False, select_related = False): """Get records from this table which fall under the given condition. @param db: adapter to use @param where: condition to filter @param order: list of field to sort by @param limit: tuple (from, to) @param select_related: whether to retrieve objects related by foreign keys in the same query """ logger.debug("Model.get('%s', db= %s, where= %s, limit= %s)" % (cls, db, where, limit)) cls.checkTable(db) orderby = orderby or cls._ordering # use default table ordering if no ordering passed fields = list(cls) from_ = [cls] recordFields = [] if select_related: for i, field in enumerate(cls): if isinstance(field, orm.RecordField): recordFields.append((i, field)) fields.extend(field.referTable) from_.append(orm.LeftJoin(field.referTable, field == field.referTable.id)) #print(db._select(*fields, from_ = from_, where = where, orderby = orderby, limit = limit)) rows = db.select(*fields, from_ = from_, where = where, orderby = orderby, limit = limit) for row in rows: record = cls(db, *zip(cls, row)) if select_related: fieldOffset = len(cls) for i, recordField in recordFields: referTable = recordField.referTable if row[i] is None: referRecord = None else: # if referRecord.id is None: # missing record !!! integrity error referRecord = referTable(db, *zip(referTable, row[fieldOffset:])) setattr(record, recordField.name, referRecord) fieldOffset += len(referTable) yield record