sqlobject-cvs Mailing List for SQLObject (Page 192)
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
You can subscribe to this list here.
2003 |
Jan
|
Feb
|
Mar
(9) |
Apr
(74) |
May
(29) |
Jun
(16) |
Jul
(28) |
Aug
(10) |
Sep
(57) |
Oct
(9) |
Nov
(29) |
Dec
(12) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2004 |
Jan
(7) |
Feb
(14) |
Mar
(6) |
Apr
(3) |
May
(12) |
Jun
(34) |
Jul
(9) |
Aug
(29) |
Sep
(22) |
Oct
(2) |
Nov
(15) |
Dec
(52) |
2005 |
Jan
(47) |
Feb
(78) |
Mar
(14) |
Apr
(35) |
May
(33) |
Jun
(16) |
Jul
(26) |
Aug
(63) |
Sep
(40) |
Oct
(96) |
Nov
(96) |
Dec
(123) |
2006 |
Jan
(159) |
Feb
(144) |
Mar
(64) |
Apr
(31) |
May
(88) |
Jun
(48) |
Jul
(16) |
Aug
(64) |
Sep
(87) |
Oct
(92) |
Nov
(56) |
Dec
(76) |
2007 |
Jan
(94) |
Feb
(103) |
Mar
(126) |
Apr
(123) |
May
(85) |
Jun
(11) |
Jul
(130) |
Aug
(47) |
Sep
(65) |
Oct
(70) |
Nov
(12) |
Dec
(11) |
2008 |
Jan
(30) |
Feb
(55) |
Mar
(88) |
Apr
(20) |
May
(50) |
Jun
|
Jul
(38) |
Aug
(1) |
Sep
(9) |
Oct
(5) |
Nov
(6) |
Dec
(39) |
2009 |
Jan
(8) |
Feb
(16) |
Mar
(3) |
Apr
(33) |
May
(44) |
Jun
(1) |
Jul
(10) |
Aug
(33) |
Sep
(74) |
Oct
(22) |
Nov
|
Dec
(15) |
2010 |
Jan
(28) |
Feb
(22) |
Mar
(46) |
Apr
(29) |
May
(1) |
Jun
(1) |
Jul
(27) |
Aug
(8) |
Sep
(5) |
Oct
(33) |
Nov
(24) |
Dec
(41) |
2011 |
Jan
(4) |
Feb
(12) |
Mar
(35) |
Apr
(29) |
May
(19) |
Jun
(16) |
Jul
(32) |
Aug
(25) |
Sep
(5) |
Oct
(11) |
Nov
(21) |
Dec
(12) |
2012 |
Jan
(3) |
Feb
(4) |
Mar
(20) |
Apr
(4) |
May
(25) |
Jun
(13) |
Jul
|
Aug
|
Sep
(2) |
Oct
(25) |
Nov
(9) |
Dec
(1) |
2013 |
Jan
(6) |
Feb
(8) |
Mar
|
Apr
(10) |
May
(31) |
Jun
(7) |
Jul
(18) |
Aug
(33) |
Sep
(4) |
Oct
(16) |
Nov
|
Dec
(27) |
2014 |
Jan
(2) |
Feb
|
Mar
|
Apr
(11) |
May
(39) |
Jun
(8) |
Jul
(11) |
Aug
(4) |
Sep
|
Oct
(27) |
Nov
|
Dec
(71) |
2015 |
Jan
(17) |
Feb
(47) |
Mar
(33) |
Apr
|
May
|
Jun
(9) |
Jul
(7) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(8) |
2016 |
Jan
(4) |
Feb
(4) |
Mar
|
Apr
|
May
(12) |
Jun
(7) |
Jul
(9) |
Aug
(31) |
Sep
(8) |
Oct
(3) |
Nov
(15) |
Dec
(1) |
2017 |
Jan
(13) |
Feb
(7) |
Mar
(14) |
Apr
(8) |
May
(10) |
Jun
(4) |
Jul
(2) |
Aug
(1) |
Sep
|
Oct
(8) |
Nov
(4) |
Dec
(5) |
2018 |
Jan
(2) |
Feb
(8) |
Mar
|
Apr
(4) |
May
|
Jun
(6) |
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(1) |
Dec
|
2019 |
Jan
(1) |
Feb
(16) |
Mar
(1) |
Apr
(3) |
May
(5) |
Jun
(1) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
(3) |
2020 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
(1) |
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
(2) |
Nov
|
Dec
(2) |
2021 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
(1) |
Dec
|
2022 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(6) |
Oct
(1) |
Nov
(1) |
Dec
(4) |
2023 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
(3) |
Sep
(2) |
Oct
(2) |
Nov
(4) |
Dec
|
2024 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
(1) |
Aug
|
Sep
(1) |
Oct
|
Nov
|
Dec
(9) |
2025 |
Jan
|
Feb
(4) |
Mar
(2) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <ian...@us...> - 2003-03-31 05:51:31
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv12426/SQLObject Modified Files: Col.py DBConnection.py SQLObject.py Log Message: Support for reading a class from a table definition (for MySQL) Index: Col.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Col.py 29 Mar 2003 20:13:14 -0000 1.2 --- Col.py 31 Mar 2003 05:51:28 -0000 1.3 *************** *** 5,11 **** import SQLBuilder from util import splitWords import Constraints ! class NoDefault: pass True, False = 1==1, 0==1 --- 5,12 ---- import SQLBuilder from util import splitWords + import re import Constraints ! NoDefault = SQLBuilder.NoDefault True, False = 1==1, 0==1 *************** *** 90,94 **** def __repr__(self): ! r = '<Col %s.%s' % (self.className, self.name) if self.default is not NoDefault: r += ' default=%s' % repr(self.default) --- 91,95 ---- def __repr__(self): ! r = '<%s %s' % (self.__class__.__name__, self.name) if self.default is not NoDefault: r += ' default=%s' % repr(self.default) *************** *** 97,100 **** --- 98,103 ---- if self.alternateID: r += ' alternate ID' + if self.notNull: + r += ' not null' return r + '>' *************** *** 227,230 **** --- 230,254 ---- del kw[name] return value + + def guessClass(t): + if t.startswith('int'): + return IntCol, {} + elif t.startswith('varchar'): + return StringCol, {'length': int(t[8:-1])} + elif t.startswith('char'): + return StringCol, {'length': int(t[5:-1]), + 'varchar': False} + elif t.startswith('datetime'): + return DateTimeCol, {} + else: + return Col, {} + + _generatePythonNameRE = re.compile('_.') + def generatePythonName(name): + if name.endswith('_id'): + return generatePythonName(name[:-3] + "ID") + def subber(match): + return match.group(0)[1].upper() + return _generatePythonNameRE.sub(subber, name) all = [] Index: DBConnection.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** DBConnection.py 31 Mar 2003 02:00:58 -0000 1.8 --- DBConnection.py 31 Mar 2003 05:51:28 -0000 1.9 *************** *** 4,7 **** --- 4,8 ---- import SQLBuilder from Cache import CacheSet + import Col try: *************** *** 305,312 **** column.mysqlCreateSQL())) ! def dropColumn(self, tableName, column): self.query('ALTER TABLE %s DROP COLUMN %s' % (tableName, column.dbName)) class PostgresConnection(DBAPI): --- 306,329 ---- column.mysqlCreateSQL())) ! def delColumn(self, tableName, column): self.query('ALTER TABLE %s DROP COLUMN %s' % (tableName, column.dbName)) + + def columnsFromSchema(self, tableName): + colData = self.queryAll("SHOW COLUMNS FROM %s" + % tableName) + results = [] + for field, t, nullAllowed, key, default, extra in colData: + if field == 'id': + continue + colClass, kw = Col.guessClass(t) + kw['name'] = Col.generatePythonName(field) + kw['notNull'] = not nullAllowed + kw['default'] = default + # @@ skip key... + # @@ skip extra... + results.append(colClass(**kw)) + return results class PostgresConnection(DBAPI): Index: SQLObject.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** SQLObject.py 31 Mar 2003 02:00:58 -0000 1.11 --- SQLObject.py 31 Mar 2003 05:51:28 -0000 1.12 *************** *** 173,180 **** newClass = type.__new__(cls, className, bases, dict) ! for column in newClass._columns: newClass.addColumn(column) ! for join in newClass._joins: newClass.addJoin(join) --- 173,182 ---- newClass = type.__new__(cls, className, bases, dict) ! for column in newClass._columns[:]: newClass.addColumn(column) + if dict.get('_fromDatabase'): + newClass.addColumnsFromDatabase() ! for join in newClass._joins[:]: newClass.addJoin(join) *************** *** 233,242 **** def unmakeProperties(obj): if isinstance(obj, dict): ! def delFunc(var): del obj[var] d = obj else: ! def delFunc(var): ! delattr(obj, var) d = obj.__dict__ --- 235,243 ---- def unmakeProperties(obj): if isinstance(obj, dict): ! def delFunc(obj, var): del obj[var] d = obj else: ! delFunc = delattr d = obj.__dict__ *************** *** 244,249 **** if isinstance(value, property): for prop in [value.fget, value.fset, value.fdel]: ! if prop and not dict.has_key(prop.__name__): ! delFunc(var) def generateTableName(s): --- 245,251 ---- if isinstance(value, property): for prop in [value.fget, value.fset, value.fdel]: ! if prop and not d.has_key(prop.__name__): ! delFunc(obj, var) ! break def generateTableName(s): *************** *** 317,320 **** --- 319,325 ---- cls._SO_columnDict[name] = column + if column not in cls._columns: + cls._columns.append(column) + ################################################### # Create the getter function(s). We'll start by *************** *** 415,419 **** --- 420,432 ---- addColumn = classmethod(addColumn) + def addColumnsFromDatabase(cls): + for column in cls._connection.columnsFromSchema(cls._table): + if not cls._SO_columnDict.has_key(column.name): + cls.addColumn(column) + + addColumnsFromDatabase = classmethod(addColumnsFromDatabase) + def delColumn(cls, column, changeSchema=False): + cls._columns.remove(column) if isinstance(column, str): column = self._SO_columnDict[column] *************** *** 428,435 **** if column.foreignKey: delattr(cls, rawGetterName(name)[:-2]) ! if cls._SO_plainForeignGetter.has_key(name[:-2]): delattr(cls, getterName(name)[:-2]) delattr(cls, rawSetterName(name)[:-2]) ! if cls._SO_plainForeignSetter.has_key(name[:-2]): delattr(cls, setterName(name)[:-2]) if column.alternateMethodName: --- 441,448 ---- if column.foreignKey: delattr(cls, rawGetterName(name)[:-2]) ! if cls._SO_plainForeignGetters.has_key(name[:-2]): delattr(cls, getterName(name)[:-2]) delattr(cls, rawSetterName(name)[:-2]) ! if cls._SO_plainForeignSetters.has_key(name[:-2]): delattr(cls, setterName(name)[:-2]) if column.alternateMethodName: *************** *** 437,441 **** if changeSchema: ! cls._connection.delColumn(self._table, column) if cls._SO_finishedClassCreation: --- 450,454 ---- if changeSchema: ! cls._connection.delColumn(cls._table, column) if cls._SO_finishedClassCreation: *************** *** 449,452 **** --- 462,468 ---- # join class. meth = join.joinMethodName + + if join not in cls._joins: + cls._joins.append(join) # The method name for adding a joined object: *************** *** 502,505 **** --- 518,522 ---- def delJoin(cls, join): meth = join.joinMethodName + cls._joins.remove(join) appendMeth = meth[0].upper() + meth[1:] for i in range(len(cls._SO_joinList)): |
From: <ian...@us...> - 2003-03-31 02:01:01
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv25249/SQLObject Modified Files: DBConnection.py SQLObject.py Log Message: More dynamicism -- add columns and joins at runtime, including changing the database schema to match (MySQL only). Also have default ordering. Index: DBConnection.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** DBConnection.py 14 Mar 2003 08:59:24 -0000 1.7 --- DBConnection.py 31 Mar 2003 02:00:58 -0000 1.8 *************** *** 300,303 **** --- 300,313 ---- return False + def addColumn(self, tableName, column): + self.query('ALTER TABLE %s ADD COLUMN %s' % + (tableName, + column.mysqlCreateSQL())) + + def dropColumn(self, tableName, column): + self.query('ALTER TABLE %s DROP COLUMN %s' % + (tableName, + column.dbName)) + class PostgresConnection(DBAPI): Index: SQLObject.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** SQLObject.py 30 Mar 2003 01:47:35 -0000 1.10 --- SQLObject.py 31 Mar 2003 02:00:58 -0000 1.11 *************** *** 1,3 **** ! Scol""" SQLObject.py Ian Bicking <ia...@co...> 17 Oct 2002 --- 1,3 ---- ! """ SQLObject.py Ian Bicking <ia...@co...> 17 Oct 2002 *************** *** 81,84 **** --- 81,90 ---- # database directly to set them. dict['_SO_plainSetters'] = {} + dict['_SO_plainGetters'] = {} + dict['_SO_plainForeignSetters'] = {} + dict['_SO_plainForeignGetters'] = {} + dict['_SO_plainJoinGetters'] = {} + dict['_SO_plainJoinAdders'] = {} + dict['_SO_plainJoinRemovers'] = {} # This is a dictionary of columnName: columnObject *************** *** 91,94 **** --- 97,104 ---- dict['_cacheValues'] = cacheValues + # The _defaultOrder is used by SelectResults + if not dict.has_key('_defaultOrder'): + dict['_defaultOrder'] = None + # We fix up the columns here -- replacing any # strings with simply-contructed Col objects, *************** *** 97,101 **** columns = [] for column in dict.get('_columns', []): ! if type(column) is type(""): column = Col.Col(column) columns.append(column) --- 107,111 ---- columns = [] for column in dict.get('_columns', []): ! if isinstance(column, str): column = Col.Col(column) columns.append(column) *************** *** 103,202 **** dict['_columns'] = columns - - for column in columns: - name = column.name - dict['_SO_columnDict'][name] = column - # if the column is someColumn, then capName - # is SomeColumn. We use this when we want to - # prefix it, like setSomeColumn. - capName = capitalize(name) - - ################################################### - # Create the getter function(s). We'll start by - # creating functions like _SO_get_columnName, - # then if there's no function named _get_columnName - # we'll alias that to _SO_get_columnName. This - # allows a sort of super call, even though there's - # no superclass that defines the database access. - if cacheValues: - - # We create a method here, which is just a function - # that takes "self" as the first argument. The - # basic logic of the lambda is: - # When we have cached values, self._SO_autoInitDone - # is true, so we skip out of the parenthesis. - # If we don't, we run self._SO_autoInit() which - # gets the cached values (for all columns). - # The cached values are stored in something - # like _SO_val_columnName, so we return that - # last. - dict[rawGetterName(name)] = eval('lambda self: (self._SO_autoInitDone or self._SO_autoInit()) and self.%s' % (instanceName(name))) - else: - # If we aren't caching values, we just call the - # function _SO_getValue, which fetches from the - # database. - dict[rawGetterName(name)] = eval('lambda self: self._SO_getValue(%s)' % repr(name)) - - # Here if the _get_columnName method isn't in the - # definition, we add it with the default - # _SO_get_columnName definition. - if not dict.has_key(getterName(name)): - dict[getterName(name)] = dict[rawGetterName(name)] - - ################################################# - # Create the setter function(s) - # Much like creating the getters, we will create - # _SO_set_columnName methods, and then alias them - # to _set_columnName if the user hasn't defined - # those methods themself. - - # We start by just using the _SO_setValue method - dict[rawSetterName(name)] = eval('lambda self, val: self._SO_setValue(%s, val)' % repr(name)) - # Then do the aliasing - if not dict.has_key(setterName(name)): - dict[setterName(name)] = dict[rawSetterName(name)] - # We keep track of setters that haven't been - # overridden, because we can combine these - # set columns into one SQL UPDATE query. - dict['_SO_plainSetters'][name] = 1 - - - ################################################## - # Here we check if the column is a foreign key, in - # which case we need to make another method that - # fetches the key and constructs the sister - # SQLObject instance. - if column.foreignKey: - - # We go through the standard _SO_get_columnName - # deal, except chopping off the "ID" ending since - # we're giving the object, not the ID of the - # object this time: - if cacheValues: - # self._SO_class_className is a reference - # to the class in question. - dict[rawGetterName(name)[:-2]] = eval('lambda self: (self._SO_autoInitDone or self._SO_autoInit()) and self._SO_class_%s(self.%s)' % (column.foreignKey, instanceName(name))) - else: - # Same non-caching version as above. - dict[rawGetterName(name)[:-2]] = eval('lambda self: self._SO_class_%s(self._SO_getValue(%s))' % (column.foreignKey, repr(name))) - # And we set the _get_columnName version - # (sans ID ending) - if not dict.has_key(getterName(name)[:-2]): - dict[getterName(name)[:-2]] = dict[rawGetterName(name)[:-2]] - - # The setter just gets the ID of the object, - # and then sets the real column. - dict[rawSetterName(name)[:-2]] = eval('lambda self, val: setattr(self, %s, self._SO_getID(val))' % (repr(name))) - if not dict.has_key(setterName(name)[:-2]): - dict[setterName(name)[:-2]] = dict[rawSetterName(name)[:-2]] - # We'll need to put in a real reference at - # some point. See needSet at the top of the - # file for more on this. - needSet.append((className, column.foreignKey)) - - if column.alternateMethodName: - func = eval('lambda cls, val: cls._SO_fetchAlternateID(%s, val)' % repr(column.dbName)) - dict[column.alternateMethodName] = classmethod(func) - ###################################################### # Set some attributes to their defaults, if necessary. --- 113,116 ---- *************** *** 235,285 **** if not dict.has_key('_joins'): dict['_joins'] = [] - for join in dict['_joins']: - - # The name of the method we'll create. If it's - # automatically generated, it's generated by the - # join class. - meth = join.joinMethodName - - # The method name for adding a joined object: - appendMeth = meth[0].upper() + meth[1:] - - # The join sometimes needs to know who we are, - # mostly to generate some internal names: - join.initCallingClass(className, dict['_table']) - - # Now that the join is set up, we add it to our - # list of joins: - dict['_SO_joinList'].append(join) - - # The function fetches the join by index, and - # then lets the join object do the rest of the - # work: - func = eval('lambda self: self._SO_joinList[%i].performJoin(self)' % (len(dict['_SO_joinList'])-1)) - - # And we do the standard _SO_get_... _get_... deal - dict[rawGetterName(meth)] = func - if not dict.has_key(getterName(meth)): - dict[getterName(meth)] = func - - # Some joins allow you to remove objects from the - # join. - if hasattr(join, 'remove'): - - # Again, we let it do the remove, and we do the - # standard naming trick. - func = eval('lambda self, obj: self._SO_joinList[%i].remove(self, obj)' % (len(dict['_SO_joinList'])-1)) - dict['_SO_remove' + join.addRemovePrefix] = func - if not dict.has_key('remove' + appendMeth): - dict['remove' + join.addRemovePrefix] = func - - # Some joins allow you to add objects. - if hasattr(join, 'add'): - - # And again... - func = eval('lambda self, obj: self._SO_joinList[%i].add(self, obj)' % (len(dict['_SO_joinList'])-1)) - dict['_SO_add' + join.addRemovePrefix] = func - if not dict.has_key('add' + appendMeth): - dict['add' + join.addRemovePrefix] = func # We use the magic "q" attribute for accessing lazy --- 149,152 ---- *************** *** 295,299 **** # a real connection. if dict.has_key('_connection') and \ ! type(dict['_connection']) is type(""): dict['_connection'] = DBConnection.connectionForName(dict['_connection']) --- 162,166 ---- # a real connection. if dict.has_key('_connection') and \ ! isinstance(dict['_connection'], str): dict['_connection'] = DBConnection.connectionForName(dict['_connection']) *************** *** 306,309 **** --- 173,182 ---- newClass = type.__new__(cls, className, bases, dict) + for column in newClass._columns: + newClass.addColumn(column) + + for join in newClass._joins: + newClass.addJoin(join) + # Register it, for use with needSet classRegistry[className] = newClass *************** *** 312,319 **** setNeedSet() # And return the class return newClass ! def makeProperties(dict): """ This function takes a dictionary of methods and finds --- 185,194 ---- setNeedSet() + newClass._SO_finishedClassCreation = True + # And return the class return newClass ! def makeProperties(obj): """ This function takes a dictionary of methods and finds *************** *** 328,334 **** Missing methods are okay. """ ! props = {} ! for var, value in dict.items(): if var.startswith('_set_'): props.setdefault(var[5:], {})['set'] = value --- 203,218 ---- Missing methods are okay. """ ! ! if isinstance(obj, dict): ! def setFunc(var, value): ! obj[var] = value ! d = obj ! else: ! def setFunc(var, value): ! setattr(obj, var, value) ! d = obj.__dict__ ! props = {} ! for var, value in d.items(): if var.startswith('_set_'): props.setdefault(var[5:], {})['set'] = value *************** *** 342,349 **** if len(setters) == 1 and setters.has_key('doc'): continue ! if dict.has_key(var): continue ! dict[var] = property(setters.get('get'), setters.get('set'), ! setters.get('del'), setters.get('doc')) ! return dict def generateTableName(s): --- 226,249 ---- if len(setters) == 1 and setters.has_key('doc'): continue ! if d.has_key(var): continue ! setFunc(var, ! property(setters.get('get'), setters.get('set'), ! setters.get('del'), setters.get('doc'))) ! ! def unmakeProperties(obj): ! if isinstance(obj, dict): ! def delFunc(var): ! del obj[var] ! d = obj ! else: ! def delFunc(var): ! delattr(obj, var) ! d = obj.__dict__ ! ! for var, value in d.items(): ! if isinstance(value, property): ! for prop in [value.fget, value.fset, value.fdel]: ! if prop and not dict.has_key(prop.__name__): ! delFunc(var) def generateTableName(s): *************** *** 398,402 **** # Some databases annoyingly return longs for INT ! if type(id) is type(1L): id = int(id) --- 298,302 ---- # Some databases annoyingly return longs for INT ! if isinstance(id, long): id = int(id) *************** *** 413,416 **** --- 313,526 ---- return val + def addColumn(cls, column, changeSchema=False): + name = column.name + cls._SO_columnDict[name] = column + + ################################################### + # Create the getter function(s). We'll start by + # creating functions like _SO_get_columnName, + # then if there's no function named _get_columnName + # we'll alias that to _SO_get_columnName. This + # allows a sort of super call, even though there's + # no superclass that defines the database access. + if cls._cacheValues: + + # We create a method here, which is just a function + # that takes "self" as the first argument. The + # basic logic of the lambda is: + # When we have cached values, self._SO_autoInitDone + # is true, so we skip out of the parenthesis. + # If we don't, we run self._SO_autoInit() which + # gets the cached values (for all columns). + # The cached values are stored in something + # like _SO_val_columnName, so we return that + # last. + setattr(cls, rawGetterName(name), eval('lambda self: (self._SO_autoInitDone or self._SO_autoInit()) and self.%s' % (instanceName(name)))) + else: + # If we aren't caching values, we just call the + # function _SO_getValue, which fetches from the + # database. + setattr(cls, rawGetterName(name), eval('lambda self: self._SO_getValue(%s)' % repr(name))) + + # Here if the _get_columnName method isn't in the + # definition, we add it with the default + # _SO_get_columnName definition. + if not hasattr(cls, getterName(name)): + setattr(cls, getterName(name), getattr(cls, rawGetterName(name))) + cls._SO_plainGetters[name] = 1 + + ################################################# + # Create the setter function(s) + # Much like creating the getters, we will create + # _SO_set_columnName methods, and then alias them + # to _set_columnName if the user hasn't defined + # those methods themself. + + # We start by just using the _SO_setValue method + setattr(cls, rawSetterName(name), eval('lambda self, val: self._SO_setValue(%s, val)' % repr(name))) + # Then do the aliasing + if not hasattr(cls, setterName(name)): + setattr(cls, setterName(name), getattr(cls, rawSetterName(name))) + # We keep track of setters that haven't been + # overridden, because we can combine these + # set columns into one SQL UPDATE query. + cls._SO_plainSetters[name] = 1 + + + ################################################## + # Here we check if the column is a foreign key, in + # which case we need to make another method that + # fetches the key and constructs the sister + # SQLObject instance. + if column.foreignKey: + + # We go through the standard _SO_get_columnName + # deal, except chopping off the "ID" ending since + # we're giving the object, not the ID of the + # object this time: + if cls._cacheValues: + # self._SO_class_className is a reference + # to the class in question. + setattr(cls, rawGetterName(name)[:-2], eval('lambda self: (self._SO_autoInitDone or self._SO_autoInit()) and self._SO_class_%s(self.%s)' % (column.foreignKey, instanceName(name)))) + else: + # Same non-caching version as above. + setattr(cls, rawGetterName(name)[:-2], eval('lambda self: self._SO_class_%s(self._SO_getValue(%s))' % (column.foreignKey, repr(name)))) + # And we set the _get_columnName version + # (sans ID ending) + if not hasattr(cls, getterName(name)[:-2]): + setattr(cls, getterName(name)[:-2], getattr(cls, rawGetterName(name)[:-2])) + cls._SO_plainForeignGetters[name[:-2]] = 1 + + # The setter just gets the ID of the object, + # and then sets the real column. + setattr(cls, rawSetterName(name)[:-2], eval('lambda self, val: setattr(self, %s, self._SO_getID(val))' % (repr(name)))) + if not hasattr(cls, setterName(name)[:-2]): + setattr(cls, setterName(name)[:-2], getattr(cls, rawSetterName(name)[:-2])) + cls._SO_plainForeignSetters[name[:-2]] = 1 + # We'll need to put in a real reference at + # some point. See needSet at the top of the + # file for more on this. + needSet.append((cls.__name__, column.foreignKey)) + + if column.alternateMethodName: + func = eval('lambda cls, val: cls._SO_fetchAlternateID(%s, val)' % repr(column.dbName)) + setattr(cls, column.alternateMethodName, classmethod(func)) + + if changeSchema: + cls._connection.addColumn(cls._table, column) + + if cls._SO_finishedClassCreation: + makeProperties(cls) + + addColumn = classmethod(addColumn) + + def delColumn(cls, column, changeSchema=False): + if isinstance(column, str): + column = self._SO_columnDict[column] + name = column.name + del cls._SO_columnDict[name] + delattr(cls, rawGetterName(name)) + if cls._SO_plainGetters.has_key(name): + delattr(cls, getterName(name)) + delattr(cls, rawSetterName(name)) + if cls._SO_plainSetters.has_key(name): + delattr(cls, setterName(name)) + if column.foreignKey: + delattr(cls, rawGetterName(name)[:-2]) + if cls._SO_plainForeignGetter.has_key(name[:-2]): + delattr(cls, getterName(name)[:-2]) + delattr(cls, rawSetterName(name)[:-2]) + if cls._SO_plainForeignSetter.has_key(name[:-2]): + delattr(cls, setterName(name)[:-2]) + if column.alternateMethodName: + delattr(cls, column.alternateMethodName) + + if changeSchema: + cls._connection.delColumn(self._table, column) + + if cls._SO_finishedClassCreation: + unmakeProperties(cls) + + delColumn = classmethod(delColumn) + + def addJoin(cls, join): + # The name of the method we'll create. If it's + # automatically generated, it's generated by the + # join class. + meth = join.joinMethodName + + # The method name for adding a joined object: + appendMeth = meth[0].upper() + meth[1:] + + # The join sometimes needs to know who we are, + # mostly to generate some internal names: + join.initCallingClass(cls.__name__, cls._table) + + # Now that the join is set up, we add it to our + # list of joins: + cls._SO_joinList.append(join) + index = len(cls._SO_joinList)-1 + + # The function fetches the join by index, and + # then lets the join object do the rest of the + # work: + func = eval('lambda self: self._SO_joinList[%i].performJoin(self)' % index) + + # And we do the standard _SO_get_... _get_... deal + setattr(cls, rawGetterName(meth), func) + if not hasattr(cls, getterName(meth)): + setattr(cls, getterName(meth), func) + cls._SO_plainJoinGetters[meth] = 1 + + # Some joins allow you to remove objects from the + # join. + if hasattr(join, 'remove'): + + # Again, we let it do the remove, and we do the + # standard naming trick. + func = eval('lambda self, obj: self._SO_joinList[%i].remove(self, obj)' % index) + setattr(cls, '_SO_remove' + join.addRemovePrefix, func) + if not hasattr(cls, 'remove' + appendMeth): + setattr(cls, 'remove' + join.addRemovePrefix, func) + cls._SO_plainJoinRemovers[meth] = 1 + + # Some joins allow you to add objects. + if hasattr(join, 'add'): + + # And again... + func = eval('lambda self, obj: self._SO_joinList[%i].add(self, obj)' % (len(dict['_SO_joinList'])-1)) + setattr(cls, '_SO_add' + join.addRemovePrefix, func) + if not hasattr(cls, 'add' + appendMeth): + setattr(cls, 'add' + join.addRemovePrefix, func) + cls._SO_plainJoinAdders[meth] = 1 + + if cls._SO_finishedClassCreation: + makeProperties(cls) + + addJoin = classmethod(addJoin) + + def delJoin(cls, join): + meth = join.joinMethodName + appendMeth = meth[0].upper() + meth[1:] + for i in range(len(cls._SO_joinList)): + if cls._SO_joinList[i] is join: + cls._SO_joinList[i] = None + delattr(cls, rawGetterName(meth)) + if cls._SO_plainJoinGetters.has_key(meth): + delattr(cls, getterName(meth)) + if hasattr(join, 'remove'): + delattr(cls, '_SO_remove' + join.addRemovePrefix) + if cls._SO_plainJoinRemovers.has_key(meth): + delattr(cls, 'remove' + join.addRemovePrefix) + if hasattr(join, 'add'): + delattr(cls, '_SO_add' + join.addRemovePrefix) + if cls._SO_plainJoinAdders.has_key(meth): + delattr(cls, 'add' + join.addRemovePrefix) + + if cls._SO_finishedClassCreation: + unmakeProperties(cls) + + delJoin = classmethod(delJoin) + def _init(self, id, connection=None): assert id is not None *************** *** 633,637 **** # 3-03 @@: Should this have a connection argument? def select(cls, clause, clauseTables=None, ! orderBy=None, groupBy=None, limit=None, lazyColumns=False): return SelectResults(cls, clause, clauseTables=clauseTables, --- 743,747 ---- # 3-03 @@: Should this have a connection argument? def select(cls, clause, clauseTables=None, ! orderBy=NoDefault, groupBy=None, limit=None, lazyColumns=False): return SelectResults(cls, clause, clauseTables=clauseTables, *************** *** 640,643 **** --- 750,763 ---- select = classmethod(select) + def selectBy(cls, **kw): + return SelectResults(cls, + ' '.join(['%s = %s' % + (cls._SO_columnDict[key].dbName, + SQLBuilder.sqlRepr(value)) + for key, value + in kw.items()])) + + selectBy = classmethod(selectBy) + # 3-03 @@: Should these have a connection argument? def dropTable(cls, ifExists=False): *************** *** 858,862 **** **ops): self.sourceClass = sourceClass ! if type(clause) is type("") and clause == 'all': clause = SQLBuilder.SQLConstant('1 = 1') self.clause = clause --- 978,982 ---- **ops): self.sourceClass = sourceClass ! if isinstance(clause, str) and clause == 'all': clause = SQLBuilder.SQLConstant('1 = 1') self.clause = clause *************** *** 869,872 **** --- 989,994 ---- self.tables = tablesDict.keys() self.ops = ops + if self.ops.get('orderBy', NoDefault) is NoDefault: + self.ops['orderBy'] = sourceClass._defaultOrder def clone(self, **newOps): |
From: <ian...@us...> - 2003-03-31 02:01:01
|
Update of /cvsroot/sqlobject/SQLObject/tests In directory sc8-pr-cvs1:/tmp/cvs-serv25249/tests Modified Files: SQLObjectTest.py test.py Log Message: More dynamicism -- add columns and joins at runtime, including changing the database schema to match (MySQL only). Also have default ordering. Index: SQLObjectTest.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/tests/SQLObjectTest.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** SQLObjectTest.py 2 Mar 2003 23:11:37 -0000 1.1 --- SQLObjectTest.py 31 Mar 2003 02:00:58 -0000 1.2 *************** *** 16,25 **** --- 16,35 ---- def setUp(self): unittest.TestCase.setUp(self) + #__connection__.debug = self.debugSQL for c in self.classes: if hasattr(c, 'drop'): __connection__.query(c.drop) + else: + c.dropTable(ifExists=True) if hasattr(c, 'create'): __connection__.query(c.create) + else: + c.createTable(ifExists=True) + #__connection__.debug = 0 + self.inserts() __connection__.debug = self.debugSQL + + def inserts(self): + pass def tearDown(self): Index: test.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/tests/test.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** test.py 2 Mar 2003 23:11:38 -0000 1.1 --- test.py 31 Mar 2003 02:00:58 -0000 1.2 *************** *** 27,32 **** classes = [Counter] ! def setUp(self): ! SQLObjectTest.setUp(self) for i in range(100): Counter.new(number=i) --- 27,31 ---- classes = [Counter] ! def inserts(self): for i in range(100): Counter.new(number=i) *************** *** 77,82 **** classes = [Counter2] ! def setUp(self): ! SQLObjectTest.setUp(self) for i in range(10): for j in range(10): --- 76,80 ---- classes = [Counter2] ! def inserts(self): for i in range(10): for j in range(10): *************** *** 86,89 **** --- 84,136 ---- self.assertEquals([(c.n1, c.n2) for c in counters], value) + ######################################## + ## Dynamic column tests + ######################################## + + class Person(SQLObject): + + _columns = [StringCol('name', length=100)] + _defaultOrder = 'name' + + class Phone(SQLObject): + + _columns = [StringCol('phone', length=12)] + _defaultOrder = 'phone' + + class PeopleTest(SQLObjectTest): + + classes = [Person, Phone] + + def inserts(self): + for n in ['jane', 'tim', 'bob', 'jake']: + Person.new(name=n) + for p in ['555-555-5555', '555-394-2930', + '444-382-4854']: + Phone.new(phone=p) + + def testDefaultOrder(self): + self.assertEqual(list(Person.select('all')), + list(Person.select('all', orderBy=Person._defaultOrder))) + + def testDynamicColumn(self): + nickname = StringCol('nickname', length=10) + Person.addColumn(nickname, changeSchema=True) + n = Person.new(name='robert', nickname='bob') + self.assertEqual([p.name for p in Person.select('all')], + ['bob', 'jake', 'jane', 'robert', 'tim']) + + def testDynamicJoin(self): + col = KeyCol('personID', foreignKey='Person') + Phone.addColumn(col, changeSchema=True) + join = MultipleJoin('Phone') + Person.addJoin(join) + for phone in Phone.select('all'): + if phone.phone.startswith('555'): + phone.person = Person.selectBy(name='tim')[0] + else: + phone.person = Person.selectBy(name='bob')[0] + self.assertEqual([p.phone for p in Person.selectBy(name='tim')[0].phones], + ['555-555-5555', '555-394-2930']) + ######################################## |
From: <ian...@us...> - 2003-03-30 01:47:41
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv12603 Modified Files: Cache.py SQLObject.py Log Message: Updated some places where the old cache system was still used Index: Cache.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Cache.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** Cache.py 14 Mar 2003 08:59:24 -0000 1.1 --- Cache.py 30 Mar 2003 01:47:35 -0000 1.2 *************** *** 123,126 **** --- 123,132 ---- self.lock.release() + def purge(self, id): + if self.cache.has_key(id): + del self.cache[id] + if self.expiredCache.has_key(id): + del self.expiredCache[id] + class CacheSet(object): Index: SQLObject.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** SQLObject.py 14 Mar 2003 08:59:24 -0000 1.9 --- SQLObject.py 30 Mar 2003 01:47:35 -0000 1.10 *************** *** 1,3 **** ! """ SQLObject.py Ian Bicking <ia...@co...> 17 Oct 2002 --- 1,3 ---- ! Scol""" SQLObject.py Ian Bicking <ia...@co...> 17 Oct 2002 *************** *** 292,303 **** makeProperties(dict) - # The instanceCache is a cache of already-created - # instances. This way there's never more than one - # of any instance. This needs to be a class attribute, - # since the class shares its instances. And an - # accompanying lock. - dict['_SO_instanceCache'] = {} - dict['_SO_instanceCacheLock'] = threading.Lock() - # If the connection is named, we turn the name into # a real connection. --- 292,295 ---- *************** *** 702,706 **** WHERE %s = %s """ % (self._table, self._idName, SQLBuilder.sqlRepr(self.id))) ! del self._SO_instanceCache[self.id] def __repr__(self): --- 694,698 ---- WHERE %s = %s """ % (self._table, self._idName, SQLBuilder.sqlRepr(self.id))) ! self._connection.cache.purge(self.id) def __repr__(self): |
From: <ian...@us...> - 2003-03-29 20:13:19
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv1408 Modified Files: Col.py Log Message: Disallowed explicit "id" column (via assert) Index: Col.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/Col.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** Col.py 14 Mar 2003 03:52:01 -0000 1.1 --- Col.py 29 Mar 2003 20:13:14 -0000 1.2 *************** *** 27,30 **** --- 27,31 ---- assert SQLBuilder.sqlIdentifier(name), 'Name must be SQL-safe (letters, numbers, underscores): %s' \ % repr(name) + assert name != 'id', 'The column name "id" is reserved for SQLObject use (and is implicitly created).' # .name is public self.name = name |
From: <ian...@us...> - 2003-03-14 09:14:31
|
Update of /cvsroot/sqlobject/SQLObject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv22961 Modified Files: DBConnection.py SQLObject.py Added Files: Cache.py Log Message: Fancier caching -- weak references, so objects can expire from cache, as well as having things expire from the cache. New Cache module that helps implement this. --- NEW FILE: Cache.py --- import threading from weakref import ref from time import time as now class CacheFactory(object): """ CacheFactory caches object creation. Each object should be referenced by a single hashable ID (note tuples of hashable values are also hashable). """ def __init__(self, expireFrequency=100, expireFraction=2, cache=True): """ Every expireFrequency times that an item is retrieved from this cache, the expire method is called. The expire method then expires an arbitrary fraction of the cached objects. The idea is at no time will the cache be entirely emptied, placing a potentially high load at that moment, but everything object will have its time to go eventually. The fraction is given as an integer, and one in that many objects are expired (i.e., the default is 1/2 of objects are expired). By setting cache to False, items won't be cached. However, in all cases a weak reference is kept to created objects, and if the object hasn't been garbage collected it will be returned. """ self.expireFrequency = expireFrequency self.expireCount = expireFrequency self.expireOffset = 0 self.expireFraction = expireFraction self.doCache = cache if self.doCache: self.cache = {} self.expiredCache = {} self.lock = threading.Lock() def get(self, id): if self.doCache: if self.expireCount > self.expireFrequency: # Two threads could hit the expire in a row, but # that's not so bad. At least by setting expireCount # back to zero right away we avoid this. The expire # method has a lock, so it's threadsafe. self.expireCount = 0 self.expire() try: return self.cache[id] except KeyError: pass self.lock.acquire() try: val = self.cache[id] except KeyError: pass else: self.lock.release() return val try: val = self.expiredCache[id]() except KeyError: return None else: del self.expiredCache[id] if val is None: return None self.cache[id] = val self.lock.release() return val else: try: val = self.expiredCache[id]() if val is not None: return val except KeyError: pass self.lock.acquire() try: val = self.expiredCache[id]() except KeyError: return None else: if val is None: del self.expiredCache[id] return None self.lock.release() return val def put(self, id, obj): if self.doCache: self.cache[id] = obj else: self.expiredCache[id] = ref(obj) self.lock.release() def created(self, id, obj): if self.doCache: self.cache[id] = obj else: self.expiredCache[id] = ref(obj) def expire(self): self.lock.acquire() keys = self.cache.keys() for i in xrange(self.expireOffset, len(keys), self.expireFraction): id = keys[i] self.expiredCache[id] = ref(self.cache[id]) del self.cache[id] # This offset tries to balance out which objects we expire, so # no object will just hang out in the cache forever. self.expireOffset = (self.expiredOffset + 1) % self.expireFraction self.lock.release() class CacheSet(object): def __init__(self, *args, **kw): self.caches = {} self.args = args self.kw = kw def get(self, id, cls): try: return self.caches[cls.__name__].get(id) except KeyError: self.caches[cls.__name__] = CacheFactory(*self.args, **self.kw) return self.caches[cls.__name__].get(id) def put(self, id, cls, obj): self.caches[cls.__name__].put(id, obj) def created(self, id, cls, obj): try: self.caches[cls.__name__].created(id, obj) except KeyError: self.caches[cls.__name__] = CacheFactory(*self.args, **self.kw) self.caches[cls.__name__].created(id, obj) Index: DBConnection.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** DBConnection.py 14 Mar 2003 03:52:01 -0000 1.6 --- DBConnection.py 14 Mar 2003 08:59:24 -0000 1.7 *************** *** 3,6 **** --- 3,7 ---- import threading import SQLBuilder + from Cache import CacheSet try: *************** *** 26,30 **** class DBConnection: ! def __init__(self, name=None, debug=False): if name: assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name]) --- 27,31 ---- class DBConnection: ! def __init__(self, name=None, debug=False, cache=True): if name: assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name]) *************** *** 32,37 **** self.name = name self.debug = debug ! self.instanceCache = {} ! self.instanceCacheLock = threading.Lock() --- 33,37 ---- self.name = name self.debug = debug ! self.cache = CacheSet(cache=cache) Index: SQLObject.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** SQLObject.py 14 Mar 2003 03:52:01 -0000 1.8 --- SQLObject.py 14 Mar 2003 08:59:24 -0000 1.9 *************** *** 409,447 **** id = int(id) - # If there is no specific connection given (so we don't - # care about transactions), we cache instances for the - # entire class, and we use the class cache: if connection is None: ! cache = cls._SO_instanceCache ! lock = cls._SO_instanceCacheLock ! # Otherwise we get the cache that belongs to the ! # connection: else: ! cache = connection.instanceCache.setdefault(cls.__name__, {}) ! lock = connection.instanceCacheLock ! # We're optimistic that we find a cached instance, if ! # so we can return it right away: ! if cache.has_key(id): ! return cache[id] ! else: ! # But if now we need to make a lock to make sure ! # no other threads make an instance while we're ! # making one: ! lock.acquire() ! # And just in case one has already finished in ! # the short time it took us to get here: ! if cache.has_key(id): ! lock.release() ! return cache[id] ! # Create the object, put it in the cache: ! inst = object.__new__(cls, id, connection) ! cache[id] = inst ! # Use the _init function -- __init__ can't be ! # used, since it's called even when we return an ! # already-created instance from the cache: ! inst._init(id, connection) ! lock.release() ! return inst def _init(self, id, connection=None): --- 409,423 ---- id = int(id) if connection is None: ! cache = cls._connection.cache else: ! cache = connection.cache ! val = cache.get(id, cls) ! if val is None: ! val = object.__new__(cls, id, connection) ! val._init(id, connection) ! cache.put(id, cls, val) ! return val def _init(self, id, connection=None): *************** *** 635,655 **** del self._SO_createValues del self._SO_creating ! if self._SO_perConnection: ! cache = self._connection.instanceCache.setdefault(self.__class__.__name__, {}) ! lock = self._connection.instanceCacheLock ! else: ! cache = self._SO_instanceCache ! lock = self._SO_instanceCacheLock ! # Everyone has to wait while we do the insert, so that a ! # select can't recreate this object during these next ! # moments. But at least we know that this object can't ! # be in the cache yet. ! lock.acquire() # Do the insert -- most of the SQL in this case is left # up to DBConnection, since getting a new ID is # non-standard. id = self._connection.queryInsertID(self._table, names, values) ! cache[id] = self ! lock.release() self._init(id) --- 611,621 ---- del self._SO_createValues del self._SO_creating ! # Do the insert -- most of the SQL in this case is left # up to DBConnection, since getting a new ID is # non-standard. id = self._connection.queryInsertID(self._table, names, values) ! cache = self._connection.cache ! cache.created(id, self.__class__, self) self._init(id) *************** *** 694,698 **** cls._connection.createTable(cls) if createJoinTables: ! self.createJoinTables(ifExists=ifExists) createTable = classmethod(createTable) --- 660,664 ---- cls._connection.createTable(cls) if createJoinTables: ! cls.createJoinTables(ifExists=ifExists) createTable = classmethod(createTable) *************** *** 701,705 **** # but right now it's making tables directly. result = [] ! for join in soClass._joins: if not join.hasIntermediateTable(): continue --- 667,671 ---- # but right now it's making tables directly. result = [] ! for join in cls._joins: if not join.hasIntermediateTable(): continue *************** *** 711,715 **** continue if ifExists and \ ! self._connection.tableExists(join.intermediateTable): continue self._connection.query( --- 677,681 ---- continue if ifExists and \ ! cls._connection.tableExists(join.intermediateTable): continue self._connection.query( *************** *** 720,723 **** --- 686,690 ---- join.otherColumn, self.joinSQLType(join))) + createJoinTables = classmethod(createJoinTables) def clearTable(cls): |
From: <ian...@us...> - 2003-03-14 09:10:16
|
Update of /cvsroot/sqlobject/SQLObject/docs In directory sc8-pr-cvs1:/tmp/cvs-serv26566/docs Modified Files: News.txt Log Message: Added notes on changes Index: News.txt =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/docs/News.txt,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** News.txt 13 Mar 2003 20:29:16 -0000 1.3 --- News.txt 14 Mar 2003 09:10:11 -0000 1.4 *************** *** 8,11 **** --- 8,29 ---- .. _start: + SQLObject 0.3 + ============= + + Features + -------- + + * New column classes (see `Col` module), indicates type + + * Table creation (SQL schema generation) via new class method + `createTable`. + + * Objects are not cached indefinitely. Cached objects are expired + into a weak dictionary (it allows objects to be garbage collected if + nowhere else in the program is using the object, but until it is + collected it's still available to the cache). Some cache control, + pass ``nocache=True`` to your connection object to eliminate as much + caching as possible. See `Cache` module for a bit more. + SQLObject 0.2.1 =============== |
From: <ian...@us...> - 2003-03-14 04:18:00
|
Update of /cvsroot/sqlobject/SQLObject In directory sc8-pr-cvs1:/tmp/cvs-serv21080 Modified Files: setup.py Log Message: test Index: setup.py =================================================================== RCS file: /cvsroot/sqlobject/SQLObject/setup.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** setup.py 14 Mar 2003 04:14:35 -0000 1.3 --- setup.py 14 Mar 2003 04:17:53 -0000 1.4 *************** *** 8,10 **** license="LGPL", packages=["SQLObject"]) ! --- 8,10 ---- license="LGPL", packages=["SQLObject"]) ! |