0001"""
0002SQLObject 0.10
0003--------------
0004
0005:author: Ian Bicking <ianb@colorstudy.com>
0006
0007SQLObject is a object-relational mapper.  See SQLObject.html or
0008SQLObject.txt for more.
0009
0010With the help by Oleg Broytmann <phd@phd.pp.ru> and many other contributors.
0011See Authors.txt.
0012
0013This program is free software; you can redistribute it and/or modify
0014it under the terms of the GNU Lesser General Public License as
0015published by the Free Software Foundation; either version 2.1 of the
0016License, or (at your option) any later version.
0017
0018This program is distributed in the hope that it will be useful,
0019but WITHOUT ANY WARRANTY; without even the implied warranty of
0020MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021GNU General Public License for more details.
0022
0023You should have received a copy of the GNU Lesser General Public
0024License along with this program; if not, write to the Free Software
0025Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
0026USA.
0027"""
0028
0029import threading
0030import weakref
0031import sqlbuilder
0032import dbconnection
0033import col
0034import styles
0035import types
0036import warnings
0037import joins
0038import index
0039import classregistry
0040import declarative
0041import events
0042from sresults import SelectResults
0043from formencode import schema, compound
0044from util.threadinglocal import local
0045
0046import sys
0047if sys.version_info[:3] < (2, 2, 0):
0048    raise ImportError, "SQLObject requires Python 2.2.0 or later"
0049
0050"""
0051This thread-local storage is needed for RowCreatedSignals. It gathers
0052code-blocks to execute _after_ the whole hierachy of inherited SQLObjects
0053is created. See SQLObject._create
0054"""
0055_postponed_local = local()
0056
0057NoDefault = sqlbuilder.NoDefault
0058
0059class SQLObjectNotFound(LookupError): pass
0060class SQLObjectIntegrityError(Exception): pass
0061
0062def makeProperties(obj):
0063    """
0064    This function takes a dictionary of methods and finds
0065    methods named like:
0066    * _get_attr
0067    * _set_attr
0068    * _del_attr
0069    * _doc_attr
0070    Except for _doc_attr, these should be methods.  It
0071    then creates properties from these methods, like
0072    property(_get_attr, _set_attr, _del_attr, _doc_attr).
0073    Missing methods are okay.
0074    """
0075
0076    if isinstance(obj, dict):
0077        def setFunc(var, value):
0078            obj[var] = value
0079        d = obj
0080    else:
0081        def setFunc(var, value):
0082            setattr(obj, var, value)
0083        d = obj.__dict__
0084
0085    props = {}
0086    for var, value in d.items():
0087        if var.startswith('_set_'):
0088            props.setdefault(var[5:], {})['set'] = value
0089        elif var.startswith('_get_'):
0090            props.setdefault(var[5:], {})['get'] = value
0091        elif var.startswith('_del_'):
0092            props.setdefault(var[5:], {})['del'] = value
0093        elif var.startswith('_doc_'):
0094            props.setdefault(var[5:], {})['doc'] = value
0095    for var, setters in props.items():
0096        if len(setters) == 1 and setters.has_key('doc'):
0097            continue
0098        if d.has_key(var):
0099            if isinstance(d[var], (types.MethodType, types.FunctionType)):
0100                warnings.warn(
0101                    "I tried to set the property %r, but it was "
0102                    "already set, as a method (%r).  Methods have "
0103                    "significantly different semantics than properties, "
0104                    "and this may be a sign of a bug in your code."
0105                    % (var, d[var]))
0106            continue
0107        setFunc(var,
0108                property(setters.get('get'), setters.get('set'),
0109                         setters.get('del'), setters.get('doc')))
0110
0111def unmakeProperties(obj):
0112    if isinstance(obj, dict):
0113        def delFunc(obj, var):
0114            del obj[var]
0115        d = obj
0116    else:
0117        delFunc = delattr
0118        d = obj.__dict__
0119
0120    for var, value in d.items():
0121        if isinstance(value, property):
0122            for prop in [value.fget, value.fset, value.fdel]:
0123                if prop and not d.has_key(prop.__name__):
0124                    delFunc(obj, var)
0125                    break
0126
0127def findDependencies(name, registry=None):
0128    depends = []
0129    for klass in classregistry.registry(registry).allClasses():
0130        if findDependantColumns(name, klass):
0131            depends.append(klass)
0132        else:
0133            for join in klass.sqlmeta.joins:
0134                if isinstance(join, joins.SORelatedJoin) and join.otherClassName == name:
0135                    depends.append(klass)
0136                    break
0137    return depends
0138
0139def findDependantColumns(name, klass):
0140    depends = []
0141    for col in klass.sqlmeta.columnList:
0142        if col.foreignKey == name and col.cascade is not None:
0143            depends.append(col)
0144    return depends
0145
0146def _collectAttributes(cls, new_attrs, look_for_class, delete=True,
0147                       set_name=False, sort=False):
0148    """
0149    Finds all attributes in `new_attrs` that are instances of
0150    `look_for_class`.  Returns them as a list.  If `delete` is true
0151    they are also removed from the `cls`.  If `set_name` is true, then
0152    the ``.name`` attribute is set for any matching objects.  If
0153    `sort` is true, then they will be sorted by ``obj.creationOrder``.
0154    """
0155    result = []
0156    for attr, value in new_attrs.items():
0157        if isinstance(value, look_for_class):
0158            result.append(value)
0159            if set_name:
0160                value.name = attr
0161            if delete:
0162                delattr(cls, attr)
0163    if sort:
0164        result.sort(
0165            lambda a, b: cmp(a.creationOrder, b.creationOrder))
0166    return result
0167
0168class CreateNewSQLObject:
0169    """
0170    Dummy singleton to use in place of an ID, to signal we want
0171    a new object.
0172    """
0173    pass
0174
0175class sqlmeta(object):
0176
0177    """
0178    This object is the object we use to keep track of all sorts of
0179    information.  Subclasses are made for each SQLObject subclass
0180    (dynamically if necessary), and instances are created to go
0181    alongside every SQLObject instance.
0182    """
0183
0184    table = None
0185    idName = None
0186    idSequence = None
0187    # This function is used to coerce IDs into the proper format,
0188    # so you should replace it with str, or another function, if you
0189    # aren't using integer IDs
0190    idType = int
0191    style = None
0192    lazyUpdate = False
0193    defaultOrder = None
0194    cacheValues = True
0195    registry = None
0196    fromDatabase = False
0197    # Default is false, but we set it to true for the *instance*
0198    # when necessary: (bad clever? maybe)
0199    expired = False
0200
0201    # This is a mapping from column names to SOCol (or subclass)
0202    # instances:
0203    columns = {}
0204    columnList = []
0205
0206    # This is a mapping from column names to Col (or subclass)
0207    # instances; these objects don't have the logic that the SOCol
0208    # objects do, and are not attached to this class closely.
0209    columnDefinitions = {}
0210
0211    # These are lists of the join and index objects:
0212    joins = []
0213    indexes = []
0214    indexDefinitions = []
0215    joinDefinitions = []
0216
0217    __metaclass__ = declarative.DeclarativeMeta
0218
0219    # These attributes shouldn't be shared with superclasses:
0220    _unshared_attributes = ['table', 'columns', 'childName']
0221
0222    # These are internal bookkeeping attributes; the class-level
0223    # definition is a default for the instances, instances will
0224    # reset these values.
0225
0226    # When an object is being created, it has an instance
0227    # variable _creating, which is true.  This way all the
0228    # setters can be captured until the object is complete,
0229    # and then the row is inserted into the database.  Once
0230    # that happens, _creating is deleted from the instance,
0231    # and only the class variable (which is always false) is
0232    # left.
0233    _creating = False
0234    _obsolete = False
0235    # Sometimes an intance is attached to a connection, not
0236    # globally available.  In that case, self.sqlmeta._perConnection
0237    # will be true.  It's false by default:
0238    _perConnection = False
0239
0240    # Inheritance definitions:
0241    parentClass = None # A reference to the parent class
0242    childClasses = {} # References to child classes, keyed by childName
0243    childName = None # Class name for inheritance child object creation
0244
0245    def __classinit__(cls, new_attrs):
0246        for attr in cls._unshared_attributes:
0247            if not new_attrs.has_key(attr):
0248                setattr(cls, attr, None)
0249        declarative.setup_attributes(cls, new_attrs)
0250
0251    def __init__(self, instance):
0252        self.instance = weakref.proxy(instance)
0253
0254    def send(cls, signal, *args, **kw):
0255        events.send(signal, cls.soClass, *args, **kw)
0256
0257    send = classmethod(send)
0258
0259    def setClass(cls, soClass):
0260        cls.soClass = soClass
0261        if not cls.style:
0262            cls.style = styles.defaultStyle
0263            try:
0264                if cls.soClass._connection and cls.soClass._connection.style:
0265                    cls.style = cls.soClass._connection.style
0266            except AttributeError:
0267                pass
0268        if cls.table is None:
0269            cls.table = cls.style.pythonClassToDBTable(cls.soClass.__name__)
0270        if cls.idName is None:
0271            cls.idName = cls.style.idForTable(cls.table)
0272
0273        # plainSetters are columns that haven't been overridden by the
0274        # user, so we can contact the database directly to set them.
0275        # Note that these can't set these in the SQLObject class
0276        # itself, because they specific to this subclass of SQLObject,
0277        # and cannot be shared among classes.
0278        cls._plainSetters = {}
0279        cls._plainGetters = {}
0280        cls._plainForeignSetters = {}
0281        cls._plainForeignGetters = {}
0282        cls._plainJoinGetters = {}
0283        cls._plainJoinAdders = {}
0284        cls._plainJoinRemovers = {}
0285
0286        # This is a dictionary of columnName: columnObject
0287        # None of these objects can be shared with superclasses
0288        cls.columns = {}
0289        cls.columnList = []
0290        # These, however, can be shared:
0291        cls.columnDefinitions = cls.columnDefinitions.copy()
0292        cls.indexes = []
0293        cls.indexDefinitions = cls.indexDefinitions[:]
0294        cls.joins = []
0295        cls.joinDefinitions = cls.joinDefinitions[:]
0296
0297    setClass = classmethod(setClass)
0298
0299    ############################################################
0300    ## Adding special values, like columns and indexes
0301    ############################################################
0302
0303    ########################################
0304    ## Column handling
0305    ########################################
0306
0307    def addColumn(cls, columnDef, changeSchema=False, connection=None):
0308        post_funcs = []
0309        cls.send(events.AddColumnSignal, cls.soClass, connection,
0310                 columnDef.name, columnDef, changeSchema, post_funcs)
0311        sqlmeta = cls
0312        soClass = cls.soClass
0313        del cls
0314        column = columnDef.withClass(soClass)
0315        name = column.name
0316        assert name != 'id', (
0317            "The 'id' column is implicit, and should not be defined as "
0318            "a column")
0319        assert name not in sqlmeta.columns, (
0320            "The class %s.%s already has a column %r (%r), you cannot "
0321            "add the column %r"
0322            % (soClass.__module__, soClass.__name__, name,
0323               sqlmeta.columnDefinitions[name], columnDef))
0324        # Collect columns from the parent classes to test
0325        # if the column is not in a parent class
0326        parent_columns = []
0327        for base in soClass.__bases__:
0328            if hasattr(base, "sqlmeta"):
0329                parent_columns.extend(base.sqlmeta.columns.keys())
0330        if hasattr(soClass, name):
0331            assert  (name in parent_columns) or (name == "childName"), (
0332                "The class %s.%s already has a variable or method %r, you cannot "
0333                "add the column %r"
0334                % (soClass.__module__, soClass.__name__, name, name))
0335        sqlmeta.columnDefinitions[name] = columnDef
0336        sqlmeta.columns[name] = column
0337        # A stable-ordered version of the list...
0338        sqlmeta.columnList.append(column)
0339
0340        ###################################################
0341        # Create the getter function(s).  We'll start by
0342        # creating functions like _SO_get_columnName,
0343        # then if there's no function named _get_columnName
0344        # we'll alias that to _SO_get_columnName.  This
0345        # allows a sort of super call, even though there's
0346        # no superclass that defines the database access.
0347        if sqlmeta.cacheValues:
0348            # We create a method here, which is just a function
0349            # that takes "self" as the first argument.
0350            getter = eval('lambda self: self._SO_loadValue(%s)' % repr(instanceName(name)))
0351
0352        else:
0353            # If we aren't caching values, we just call the
0354            # function _SO_getValue, which fetches from the
0355            # database.
0356            getter = eval('lambda self: self._SO_getValue(%s)' % repr(name))
0357        setattr(soClass, rawGetterName(name), getter)
0358
0359        # Here if the _get_columnName method isn't in the
0360        # definition, we add it with the default
0361        # _SO_get_columnName definition.
0362        if not hasattr(soClass, getterName(name)) or (name == 'childName'):
0363            setattr(soClass, getterName(name), getter)
0364            sqlmeta._plainGetters[name] = 1
0365
0366        #################################################
0367        # Create the setter function(s)
0368        # Much like creating the getters, we will create
0369        # _SO_set_columnName methods, and then alias them
0370        # to _set_columnName if the user hasn't defined
0371        # those methods themself.
0372
0373        # @@: This is lame; immutable right now makes it unsettable,
0374        # making the table read-only
0375        if not column.immutable:
0376            # We start by just using the _SO_setValue method
0377            setter = eval('lambda self, val: self._SO_setValue(%s, val, self.%s, self.%s)' % (repr(name), '_SO_from_python_%s' % name, '_SO_to_python_%s' % name))
0378            setattr(soClass, '_SO_from_python_%s' % name, column.from_python)
0379            setattr(soClass, '_SO_to_python_%s' % name, column.to_python)
0380            setattr(soClass, rawSetterName(name), setter)
0381            # Then do the aliasing
0382            if not hasattr(soClass, setterName(name)) or (name == 'childName'):
0383                setattr(soClass, setterName(name), setter)
0384                # We keep track of setters that haven't been
0385                # overridden, because we can combine these
0386                # set columns into one SQL UPDATE query.
0387                sqlmeta._plainSetters[name] = 1
0388
0389        ##################################################
0390        # Here we check if the column is a foreign key, in
0391        # which case we need to make another method that
0392        # fetches the key and constructs the sister
0393        # SQLObject instance.
0394        if column.foreignKey:
0395
0396            # We go through the standard _SO_get_columnName deal
0397            # we're giving the object, not the ID of the
0398            # object this time:
0399            origName = column.origName
0400            if sqlmeta.cacheValues:
0401                # self._SO_class_className is a reference
0402                # to the class in question.
0403                getter = eval('lambda self: self._SO_foreignKey(self._SO_loadValue(%r), self._SO_class_%s)' % (instanceName(name), column.foreignKey))
0404            else:
0405                # Same non-caching version as above.
0406                getter = eval('lambda self: self._SO_foreignKey(self._SO_getValue(%s), self._SO_class_%s)' % (repr(name), column.foreignKey))
0407            setattr(soClass, rawGetterName(origName), getter)
0408
0409            # And we set the _get_columnName version
0410            if not hasattr(soClass, getterName(origName)):
0411                setattr(soClass, getterName(origName), getter)
0412                sqlmeta._plainForeignGetters[origName] = 1
0413
0414            if not column.immutable:
0415                # The setter just gets the ID of the object,
0416                # and then sets the real column.
0417                setter = eval('lambda self, val: setattr(self, %s, self._SO_getID(val))' % (repr(name)))
0418                setattr(soClass, rawSetterName(origName), setter)
0419                if not hasattr(soClass, setterName(origName)):
0420                    setattr(soClass, setterName(origName), setter)
0421                    sqlmeta._plainForeignSetters[origName] = 1
0422
0423            classregistry.registry(sqlmeta.registry).addClassCallback(
0424                column.foreignKey,
0425                lambda foreign, me, attr: setattr(me, attr, foreign),
0426                soClass, '_SO_class_%s' % column.foreignKey)
0427
0428        if column.alternateMethodName:
0429            func = eval('lambda cls, val, connection=None: cls._SO_fetchAlternateID(%s, %s, val, connection=connection)' % (repr(column.name), repr(column.dbName)))
0430            setattr(soClass, column.alternateMethodName, classmethod(func))
0431
0432        if changeSchema:
0433            conn = connection or soClass._connection
0434            conn.addColumn(sqlmeta.table, column)
0435
0436        if soClass._SO_finishedClassCreation:
0437            makeProperties(soClass)
0438
0439        for func in post_funcs:
0440            func(soClass, column)
0441
0442    addColumn = classmethod(addColumn)
0443
0444    def addColumnsFromDatabase(sqlmeta, connection=None):
0445        soClass = sqlmeta.soClass
0446        conn = connection or soClass._connection
0447        for columnDef in conn.columnsFromSchema(sqlmeta.table, soClass):
0448            if columnDef.name not in sqlmeta.columnDefinitions:
0449                if isinstance(columnDef.name, unicode):
0450                    columnDef.name = columnDef.name.encode('ascii')
0451                sqlmeta.addColumn(columnDef)
0452
0453    addColumnsFromDatabase = classmethod(addColumnsFromDatabase)
0454
0455    def delColumn(cls, column, changeSchema=False, connection=None):
0456        sqlmeta = cls
0457        soClass = sqlmeta.soClass
0458        if isinstance(column, str):
0459            column = sqlmeta.columns[column]
0460        if isinstance(column, col.Col):
0461            for c in sqlmeta.columns.values():
0462                if column is c.columnDef:
0463                    column = c
0464                    break
0465            else:
0466                raise IndexError(
0467                    "Column with definition %r not found" % column)
0468        post_funcs = []
0469        cls.send(events.DeleteColumnSignal, connection, column.name, column,
0470                 post_funcs)
0471        name = column.name
0472        del sqlmeta.columns[name]
0473        del sqlmeta.columnDefinitions[name]
0474        sqlmeta.columnList.remove(column)
0475        delattr(soClass, rawGetterName(name))
0476        if sqlmeta._plainGetters.has_key(name)