def __perform_updates__(self, update_cursor, select_after_update=False): if len(self.__changed_columns__) == 0: return else: info = {} for column, datatypes in self.__changed_columns__.items(): for dt in datatypes: if not info.has_key(column): update_expression = dt.update_expression(self) if update_expression is not None: info[column] = update_expression statement = sql.update(self.__relation__, self.__primary_key__.where(), info) update_cursor.execute(statement) if select_after_update: need_select = [] for column, dbprops in self.__changed_columns__.items(): dbprops = filter(lambda d: d.__select_after_insert__(self), dbprops) if len(dbprops) > 0: need_select.append((column, dbprops)) if len(need_select) > 0: columns = map(lambda (c, d): c, need_select) query = sql.select(columns, self.__relation__, self.__primary_key__.where()) update_cursor.execute(query) tpl = update_cursor.fetchone() for (column, dbprops), value in zip(need_select, tpl): for dbprop in dbprops: dbprop.__set_from_result__(self.__ds__(), self, value) # Clear the list of changed columns self.__changed_columns__.clear()
def all(self, *clauses): """ This method will return all entries in the child relation (or a subset specified by clauses) and a list of those primary keys which are present in the link table. You can check if a dbobj is linked by doing: >>> result, active_keys = dbobj.relation.all() >>> for a in result: ... if a.__primary_key__.values() in active_keys: ... do_something(a) ... else: ... do_somethin_else(a) """ if self.dbobj.__is_stored__(): relations = ( self.relationship.link_relation, self.child_class().__view__, ) join_clauses = self.add_where(clauses) child_pkey = keys.primary_key(self.child_class()) query = sql.select( tuple(child_pkey.columns()), relations, *join_clauses) cursor = self.ds().execute(query) active_keys = list(cursor.fetchall()) else: active_keys = [] result = self.ds().select(self.child_class(), *clauses) return ( result, active_keys, )
def select(self, *clauses): """ Use like this: >>> result = dbobj.children.select(sql.where(...)) This will yield those child objects that fit the condition in the where statements. Note that your WHERE will be integrated into a more complex WHERE clause. The many2many relationship uses a LEFT JOIN to connect the link_relation and the child relation. You must do that by hand. Also, doing so might mess up your db, so you might want to use FOREIGN KEY constraints on the link relation. """ relations = ( self.relationship.link_relation, self.child_class().__view__, ) clauses = self.add_where(clauses) query = sql.select( self.child_class().__select_expressions__( full_column_names=True), relations, *clauses) return self.ds().run_select( self.child_class(), query)
def select(self, dbclass, *clauses): """ SELECT dbobjs from the database, according to clauses. @param dbclass: The dbclass of the objects to be selected. @param clauses: A list of t4.orm.sql clauses instances (or equivalent Python object i.e. strings) that are added to the sql.select query. See t4.orm.sql.select for details """ from dbobject import dbobject clauses = filter(lambda clause: clause is not None, clauses) full_column_names = False for clause in clauses: if isinstance(clause, (sql.left_join, sql.right_join,)): full_column_names = True # GROUP BY clauses receive special treatment (in a good # way): If their first and only column (i.e. parameter # passed on creation) is a dbclass, it is replaced by the # dbclass' columns. This is a shorthand to formulate # joins. (And t4.sql can't know about dbclasses). if isinstance(clause, sql.group_by) and \ len(clause._columns) == 1 and \ hasattr(clause._columns[0], "__select_expressions__"): clause._columns = clause._columns[0].__select_expressions__( True) query = sql.select(dbclass.__select_expressions__(full_column_names), dbclass.__view__, *clauses) return self.run_select(dbclass, query)
def select_after_insert(self, dbobj): """ This method will be run after each INSERT statement automaticaly generated by a ds to pick up default values and primary keys set by the backend. See insert(). """ properties = [] columns = [] for property in dbobj.__dbproperties__(): if property.__select_after_insert__(dbobj): properties.append(property) columns.append(property.column) if len(properties) > 0: where = self.select_after_insert_where(dbobj) query = sql.select(columns, dbobj.__relation__, where) self._modify_cursor.execute(query) tpl = self._modify_cursor.fetchone() if tpl is None: raise ObjectWasNotInserted() for property, value in zip(properties, tpl): property.__set_from_result__(self, dbobj, value)
def len(self, *clauses): """ Return the number of child objects associated with a parent. You may supply a where clause. The same things apply as for the where clause for select(), see above. """ clauses = self.add_where(clauses) query = sql.select("COUNT(*)", ( self.relationship.link_relation, self.child_class().__view__, ), *clauses) len, = self.ds().query_one(query) return len
def count(self, dbclass, *clauses): """ All clauses except the WHERE clause will be ignored (including OFFSET and LIMIT!) @param dbclass: See select() above. @param clauses: See select() above. @return: An integer value indicating the number of objects of dbclass select() would return if run with these clauses. """ clauses = filter(lambda clause: (isinstance(clause, sql.where) or isinstance(clause, sql.left_join)), clauses) query = sql.select("COUNT(*)", dbclass.__view__, *clauses) return self.query_one(query)[0]
def __get__(self, dbobj, owner="What??"): if dbobj is None: return self self.check_dbobj(dbobj) if self.isset(dbobj): return getattr(dbobj, self.data_attribute_name()) else: # Consturct the SQL query query = sql.select( self.child_column.column, self.child_relation, self.child_where(dbobj), self.orderby ) cursor = dbobj.__ds__().execute(query) ret = map(lambda tpl: self.child_column.__convert__(tpl[0]), cursor.fetchall()) ret = tuple(ret) setattr(dbobj, self.data_attribute_name(), ret) return ret
def count(self): """ This is a helper function that will perform a query as SELECT COUNT(*) ... appropriate to determine the number of rows in this result. This will remove all clauses of the original select except the WHERE clause. This can't be called __len__(), because then it is used by list() and yields a superflous SELECT query. """ if not isinstance(self.select, sql.select): raise TypeError("result.count() can only work if the select was a" "sql.select instance!") where = filter(lambda clause: isinstance(clause, (sql.where, sql.left_join)), self.select.clauses) count_select = sql.select(sql.expression("COUNT(*)"), self.select.relations, *where) count, = self.ds.query_one(count_select) return count
def __get__(self, dbobj, owner="Who??"): if dbobj is None: return self self.check_dbobj(dbobj) if self.isset(dbobj): return getattr(dbobj, self.data_attribute_name()) else: # Consturct the SQL query query = sql.select( ( self.child_key_column.column, self.child_value_column.column, ), self.child_relation, self.child_where(dbobj) ) cursor = dbobj.__ds__().execute(query) ret = map(lambda tpl: ( self.child_key_column.__convert__(tpl[0]), self.child_value_column.__convert__(tpl[1]), ), cursor.fetchall()) ret = self.sqldict_dict(self, dbobj, dict(ret)) setattr(dbobj, self.data_attribute_name(), ret) return ret
def __get__(self, dbobj, owner="I don't know what this is for"): if dbobj is None: return self self.check_dbobj(dbobj) if self.isset(dbobj): return getattr(dbobj, self.data_attribute_name()) else: query = sql.select(( self.column, ), dbobj.__view__, dbobj.__primary_key__.where()) cursor = dbobj.__ds__().execute(query) row = cursor.fetchone() if row is None: raise IllegelPrimaryKey() # This shouldn't happen value = row[0] # The way this is handled is a little strange. Let me explain! # The point is, __set_from_result__() may convert the # data retreived from the RDBMS into some other Python # representation (t4.orm.util.pickle works that way for instance). # So we use the function to do its job and, if we're not supposed # to cache the value, *undo* the changes it made on the dbobj. # This presumes that the data_attribute_name() mechanism is used # by __set_from_result__(), which is relatively save, I guess. self.inside_datatype.__set_from_result__(dbobj.__ds__(), dbobj, value) ret = getattr(dbobj, self.data_attribute_name()) if not self.cache and hasattr(dbobj, self.data_attribute_name()): delattr(dbobj, self.data_attribute_name()) return ret