Ejemplo n.º 1
0
    def add(self, record):
        if isinstance(self.__collector, orb.Pipe):
            cls = self.__collector.throughModel()
            data = {
                self.__collector.from_(): self.__record,
                self.__collector.to(): record
            }

            with WriteLocker(self.__cacheLock):
                self.__cache = defaultdict(dict)

            return cls.ensureExists(data, context=self.context())

        elif isinstance(self.__collector, orb.ReverseLookup):
            record.set(self.__collector.targetColumn(), self.__record)
            record.save()

            with WriteLocker(self.__cacheLock):
                self.__cache = defaultdict(dict)

            return True

        else:
            try:
                records = self.__cache['records'][self.__context]
            except KeyError:
                if self.__model is None or type(record) == self.__model:
                    self.__model = type(record)
                    self.__cache['records'][self.__context] = []
                else:
                    raise NotImplementedError

            records.append(record)
            return True
Ejemplo n.º 2
0
    def first(self, **context):
        if self.isNull():
            return None

        context = self.context(**context)

        try:
            with ReadLocker(self.__cacheLock):
                return self.__cache['first'][context]
        except KeyError:
            try:
                with ReadLocker(self.__cacheLock):
                    return self.__cache['first'][context]
            except IndexError:
                return None
            except KeyError:
                try:
                    with ReadLocker(self.__cacheLock):
                        raw = self.__preload['first'][context]
                except KeyError:
                    context.limit = 1
                    context.order = [(self.__model.schema().idColumn().name(),
                                      'desc')]
                    records = self.records(context=context)
                    record = records[0] if records else None
                else:
                    record = self._process([raw], context)[0]

                with WriteLocker(self.__cacheLock):
                    self.__cache['first'][context] = record
                return record
Ejemplo n.º 3
0
    def count(self, **context):
        if self.isNull():
            return 0

        context = self.context(**context)
        try:
            with ReadLocker(self.__cacheLock):
                return self.__cache['count'][context]
        except KeyError:
            try:
                with ReadLocker(self.__cacheLock):
                    return len(self.__cache['records'][context])
            except KeyError:
                optimized_context = context.copy()
                optimized_context.columns = [self.__model.schema().idColumn()]
                optimized_context.expand = None
                optimized_context.order = None

                try:
                    with ReadLocker(self.__cacheLock):
                        count = self.__preload['count'][context]
                except KeyError:
                    try:
                        with ReadLocker(self.__cacheLock):
                            raw = self.__preload['records'][context]
                            count = len(raw)
                    except KeyError:
                        conn = optimized_context.db.connection()
                        count = conn.count(self.__model, optimized_context)

                with WriteLocker(self.__cacheLock):
                    self.__cache['count'][context] = count
                return count
Ejemplo n.º 4
0
    def removeRecords(self, remove, options):
        """
        Removes the inputted record from the database.

        :param      remove  | {<orb.Table>: [<orb.Query>, ..], ..}
                    options | <orb.ContextOptions>

        :return     <int> number of rows removed
        """
        if not remove:
            return 0

        # include various schema records to remove
        count = 0
        DELETE = self.sql('DELETE')
        for table, queries in remove.items():
            with WriteLocker(self.__concurrencyLocks[table.schema().name()],
                             delay=0.1):
                for query in queries:
                    data = {}
                    sql = DELETE(table, query, options=options, IO=data)
                    if options.dryRun:
                        print sql % data
                    else:
                        count += self.execute(sql, data)[1]

        return count
Ejemplo n.º 5
0
 def push(transaction, threadId=None):
     """
     Pushes a new transaction onto the stack.
     
     :param     transaction | <Transaction>
     """
     threadId = threadId or threading.current_thread().ident
     with WriteLocker(Transaction._stackLock):
         Transaction._stack[threadId].append(transaction)
Ejemplo n.º 6
0
 def _cleanup(self):
     """
     Cleans up any expired keys from the cache.
     """
     now = datetime.datetime.now()
     with WriteLocker(self._cacheLock):
         for key, expires in self._expiresAt.items():
             if expires < now:
                 self._expiresAt.pop(key, None)
                 self._cache.pop(key, None)
Ejemplo n.º 7
0
    def expire(self, key=None):
        """
        Expires the given key from the local cache.

        :param      key | <hashable>
        """
        with WriteLocker(self._cacheLock):
            if key:
                self._cache.pop(key, None)
                self._expiresAt.pop(key, None)
            else:
                self._cache.clear()
                self._expiresAt.clear()
Ejemplo n.º 8
0
    def delete(self, **context):
        """
        Removes this record from the database.  If the dryRun \
        flag is specified then the command will be logged and \
        not executed.

        :note       From version 0.6.0 on, this method now accepts a mutable
                    keyword dictionary of values.  You can supply any member
                    value for either the <orb.LookupOptions> or
                    <orb.Context>, as well as the keyword 'lookup' to
                    an instance of <orb.LookupOptions> and 'options' for
                    an instance of the <orb.Context>

        :return     <int>
        """
        if not self.isRecord():
            return 0

        event = orb.events.DeleteEvent(record=self, context=context)
        if self.processEvent(event):
            self.onDelete(event)

        if event.preventDefault:
            return 0

        with WriteLocker(self.__dataLock):
            self.__loaded.clear()

        context = self.context(**context)
        conn = context.db.connection()
        _, count = conn.delete([self], context)

        # clear out the old values
        if count == 1:
            col = self.schema().column(self.schema().idColumn())
            with WriteLocker(self.__dataLock):
                self.__values[col.name()] = (None, None)

        return count
Ejemplo n.º 9
0
    def pushDefaultContext(cls, context):
        defaults = getattr(cls, '_{0}__defaults'.format(cls.__name__), None)
        if defaults is None:
            defaults = defaultdict(list)
            lock = ReadWriteLock()
            setattr(cls, '_{0}__defaults'.format(cls.__name__), defaults)
            setattr(cls, '_{0}__defaultsLock'.format(cls.__name__), lock)
        else:
            lock = getattr(cls, '_{0}__defaultsLock'.format(cls.__name__))

        tid = threading.currentThread().ident
        with WriteLocker(lock):
            defaults[tid].append(context)
Ejemplo n.º 10
0
    def _load(self, event):
        """
        Processes a load event by setting the properties of this record
        to the data restored from the database.

        :param event: <orb.events.LoadEvent>
        """
        if not event.data:
            return

        context = self.context()
        schema = self.schema()
        dbname = schema.dbname()
        clean = {}

        for col, value in event.data.items():
            try:
                model_dbname, col_name = col.split('.')
            except ValueError:
                col_name = col
                model_dbname = dbname

            # make sure the value we're setting is specific to this model
            try:
                column = schema.column(col_name)
            except orb.errors.ColumnNotFound:
                column = None

            if model_dbname != dbname or (column in clean and isinstance(
                    clean[column], Model)):
                continue

            # look for preloaded reverse lookups and pipes
            elif not column:
                self.__preload[col_name] = value

            # extract the value from the database
            else:
                value = column.dbRestore(value, context=context)
                clean[column] = value

        # update the local values
        with WriteLocker(self.__dataLock):
            for col, val in clean.items():
                default = val if not isinstance(val, dict) else val.copy()
                self.__values[col.name()] = (default, val)
                self.__loaded.add(col)

        if self.processEvent(event):
            self.onLoad(event)
Ejemplo n.º 11
0
    def setValue(self, key, value, timeout=None):
        """
        Caches the inputted key and value to this instance.

        :param      key     | <hashable>
                    value   | <variant>
        """
        if not self.isEnabled():
            return

        timeout = timeout or self.timeout()

        with WriteLocker(self._cacheLock):
            self._cache[key] = value
            self._expiresAt[key] = datetime.datetime.now(
            ) + datetime.timedelta(seconds=timeout)
Ejemplo n.º 12
0
    def init(self):
        columns = self.schema().columns().values()
        with WriteLocker(self.__dataLock):
            for column in columns:
                if column.name() not in self.__values and not column.testFlag(
                        column.Flags.Virtual):
                    value = column.default()
                    if column.testFlag(column.Flags.I18n):
                        value = {self.__context.locale: value}
                    elif column.testFlag(column.Flags.Polymorphic):
                        value = type(self).__name__

                    self.__values[column.name()] = (value, value)

        event = orb.events.InitEvent(record=self)
        if self.processEvent(event):
            self.onInit(event)
Ejemplo n.º 13
0
 def pop(transaction=None, threadId=None):
     """
     Removes the latest transaction from the stack.
     
     :return     <Transaction> || None
     """
     threadId = threadId or threading.current_thread().ident
     with WriteLocker(Transaction._stackLock):
         if transaction:
             try:
                 Transaction._stack[threadId].remove(transaction)
             except (KeyError, ValueError):
                 return None
         else:
             try:
                 Transaction._stack[threadId].pop()
             except IndexError:
                 return None
Ejemplo n.º 14
0
    def last(self, **context):
        if self.isNull():
            return None

        context = self.context(**context)
        try:
            with ReadLocker(self.__cacheLock):
                return self.__cache['last'][context]
        except KeyError:
            try:
                with ReadLocker(self.__cacheLock):
                    raw = self.__preload['last'][context]
            except KeyError:
                record = self.reversed().first(context=context)
            else:
                record = self._process([raw], context)[0]

            with WriteLocker(self.__cacheLock):
                self.__cache['last'][context] = record
            return record
Ejemplo n.º 15
0
    def ids(self, **context):
        if self.isNull():
            return []

        context = self.context(**context)
        try:
            with ReadLocker(self.__cacheLock):
                return self.__cache['ids'][context]
        except KeyError:
            try:
                with ReadLocker(self.__cacheLock):
                    ids = self.__preload['ids'][context]
            except KeyError:
                ids = self.records(columns=[self.__model.schema().idColumn()],
                                   returning='values',
                                   context=context)

            with WriteLocker(self.__cacheLock):
                self.__cache['ids'][context] = ids

            return ids
Ejemplo n.º 16
0
    def records(self, **context):
        if self.isNull():
            return []

        context = self.context(**context)

        try:
            with ReadLocker(self.__cacheLock):
                return self.__cache['records'][context]
        except KeyError:
            try:
                with ReadLocker(self.__cacheLock):
                    raw = self.__preload['records'][context]
            except KeyError:
                conn = context.db.connection()
                raw = conn.select(self.__model, context)

            records = self._process(raw, context)

            with WriteLocker(self.__cacheLock):
                self.__cache['records'][context] = records
            return records
Ejemplo n.º 17
0
    def update(self, records, lookup, options):
        """
        Updates the modified data in the database for the
        inputted record.  If the dryRun flag is specified then
        the command will be logged but not executed.

        :param      record   | <orb.Table>
                    lookup   | <orb.LookupOptions>
                    options  | <orb.ContextOptions>

        :return     <dict> changes
        """
        # convert the recordset to a list
        if orb.RecordSet.typecheck(records):
            records = list(records)

        # wrap the record in a list
        elif orb.Table.recordcheck(records) or orb.View.recordcheck(records):
            records = [records]

        updater = defaultdict(list)
        changes = []
        for record in records:
            rchanges = record.changeset(columns=lookup.columns)
            changes.append(rchanges)

            if options.force:
                pass

            elif not record.isRecord():
                continue

            elif not rchanges:
                continue

            schemas = [record.schema()]

            for schema in schemas:
                updater[schema].append((record, rchanges))

        if not updater:
            if len(records) > 1:
                return []
            else:
                return {}

        cmds = []
        data = {}
        locks = []

        UPDATE = self.sql('UPDATE')

        for schema, changes in updater.items():
            locks.append(
                WriteLocker(self.__concurrencyLocks[schema.name()], delay=0.1))
            icmd = UPDATE(schema, changes, options=options, IO=data)
            cmds.append(icmd)

        cmd = u'\n'.join(cmds)

        if options.dryRun:
            print cmd % data
            if len(changes) == 1:
                return {}
            else:
                return []
        else:
            with MultiContext(*locks):
                results, _ = self.execute(cmd, data, autoCommit=False)

        if not self.commit():
            if len(changes) == 1:
                return {}
            return []

        # update the values for the database
        for record in records:
            record._markAsLoaded(self.database(), columns=lookup.columns)

        if len(changes) == 1:
            return changes[0]
        return changes
Ejemplo n.º 18
0
 def clear(self):
     with WriteLocker(self.__cacheLock):
         self.__cache = defaultdict(dict)
Ejemplo n.º 19
0
    def insert(self, records, lookup, options):
        """
        Inserts the table instance into the database.  If the
        dryRun flag is specified, then the command will be
        logged but not executed.

        :param      records  | <orb.Table>
                    lookup   | <orb.LookupOptions>
                    options  | <orb.ContextOptions>

        :return     <dict> changes
        """
        # convert the recordset to a list
        if orb.RecordSet.typecheck(records):
            records = list(records)

        # wrap the record in a list
        elif orb.Table.recordcheck(records) or orb.View.recordcheck(records):
            records = [records]

        # determine the proper records for insertion
        inserter = defaultdict(list)
        changes = []
        for record in records:
            # make sure we have some data to insert
            rchanges = record.changeset(columns=lookup.columns)
            changes.append(rchanges)

            # do not insert records that already exist
            if options.force:
                pass
            elif record.isRecord() or not rchanges:
                continue

            inserter[record.schema()].append(record)

        cmds = []
        data = {}

        autoinc = options.autoIncrement
        INSERT = self.sql('INSERT')
        INSERTED_KEYS = self.sql('INSERTED_KEYS')

        locks = []
        for schema, schema_records in inserter.items():
            if not schema_records:
                continue

            colcount = len(schema.columns())
            batchsize = self.insertBatchSize()
            size = batchsize / max(int(round(colcount / 10.0)), 1)

            for batch in projex.iters.batch(schema_records, size):
                batch = list(batch)
                icmd = INSERT(schema,
                              batch,
                              columns=lookup.columns,
                              autoincrement=autoinc,
                              options=options,
                              IO=data)
                if icmd:
                    cmds.append(icmd)

            if cmds:
                locks.append(
                    WriteLocker(self.__concurrencyLocks[schema.name()],
                                delay=0.1))

            # for inherited schemas in non-OO tables, we'll define the
            # primary keys before insertion
            if autoinc and INSERTED_KEYS:
                cmd = INSERTED_KEYS(schema, count=len(schema_records), IO=data)
                cmds.append(cmd)

        if not cmds:
            return {}

        cmd = u'\n'.join(cmds)

        if options.dryRun:
            print cmd % data

            if len(changes) == 1:
                return {}
            else:
                return []
        else:
            with MultiContext(*locks):
                results, _ = self.execute(cmd, data, autoCommit=False)

        if not self.commit():
            if len(changes) == 1:
                return {}
            return []

        # update the values for the database
        for i, record in enumerate(records):
            try:
                record.updateOptions(**options.assigned())
                record._updateFromDatabase(results[i])
            except IndexError:
                pass

            record._markAsLoaded(self.database(), columns=lookup.columns)

        if len(changes) == 1:
            return changes[0]
        return changes
Ejemplo n.º 20
0
    def set(self, column, value, useMethod=True, **context):
        """
        Sets the value for this record at the inputted column
        name.  If the columnName provided doesn't exist within
        the schema, then the ColumnNotFound error will be
        raised.

        :param      columnName      | <str>
                    value           | <variant>

        :return     <bool> changed
        """
        col = self.schema().column(column, raise_=False)

        if col is None:
            # allow setting of collections as well
            collector = self.schema().collector(column)
            if collector:
                my_context = self.context()

                for k, v in my_context.raw_values.items():
                    if k not in orb.Context.QueryFields:
                        context.setdefault(k, v)

                sub_context = orb.Context(**context)
                method = collector.settermethod()
                if method and useMethod:
                    return method(self, value, context=sub_context)
                else:
                    records = self.get(collector.name(), context=sub_context)
                    records.update(value,
                                   useMethod=useMethod,
                                   context=sub_context)

                    # remove any preloaded values from the collector
                    self.__preload.pop(collector.name(), None)

                    return records
            else:
                raise errors.ColumnNotFound(self.schema().name(), column)

        elif col.testFlag(col.Flags.ReadOnly):
            raise errors.ColumnReadOnly(column)

        context = self.context(**context)
        if useMethod:
            method = col.settermethod()
            if method:
                keywords = list(funcutil.extract_keywords(method))
                if 'locale' in keywords:
                    return method(self, value, locale=context.locale)
                else:
                    return method(self, value)

        with WriteLocker(self.__dataLock):
            orig, curr = self.__values.get(col.name(), (None, None))
            value = col.store(value, context)

            # update the context based on the locale value
            if col.testFlag(col.Flags.I18n) and isinstance(
                    curr, dict) and isinstance(value, dict):
                new_value = curr.copy()
                new_value.update(value)
                value = new_value

            try:
                change = curr != value
            except TypeError:
                change = True

            if change:
                self.__values[col.name()] = (orig, value)

        # broadcast the change event
        if change:
            if col.testFlag(col.Flags.I18n) and context.locale != 'all':
                old_value = curr.get(context.locale) if isinstance(
                    curr, dict) else curr
                new_value = value.get(context.locale) if isinstance(
                    value, dict) else value
            else:
                old_value = curr
                new_value = value

            event = orb.events.ChangeEvent(record=self,
                                           column=col,
                                           old=old_value,
                                           value=new_value)
            if self.processEvent(event):
                self.onChange(event)
            if event.preventDefault:
                with WriteLocker(self.__dataLock):
                    orig, _ = self.__values.get(col.name(), (None, None))
                    self.__values[col.name()] = (orig, curr)
                return False
            else:
                return change
        else:
            return False
Ejemplo n.º 21
0
    def save(self, values=None, after=None, before=None, **context):
        """
        Commits the current change set information to the database,
        or inserts this object as a new record into the database.
        This method will only update the database if the record
        has any local changes to it, otherwise, no commit will
        take place.  If the dryRun flag is set, then the SQL
        will be logged but not executed.

        :param values: None or dictionary of values to update before save
        :param after: <orb.Model> || None (optional)
                      if provided, this save call will be delayed
                      until after the given record has been saved,
                      triggering a PostSaveEvent callback
        :param before: <orb.Model> || None (optional)
                      if provided, this save call will be delayed
                      until before the given record is about to be
                      saved, triggering a PreSaveEvent callback


        :note       From version 0.6.0 on, this method now accepts a mutable
                    keyword dictionary of values.  You can supply any member
                    value for either the <orb.LookupOptions> or
                    <orb.Context>, 'options' for
                    an instance of the <orb.Context>

        :return     <bool> success
        """
        # specify that this save call should be performed after the save of
        # another record, useful for chaining events
        if after is not None:
            callback = orb.events.Callback(self.save, values=values, **context)
            after.addCallback(orb.events.PostSaveEvent,
                              callback,
                              record=after,
                              once=True)
            return callback

        # specify that this save call should be performed before the save
        # of another record, useful for chaining events
        elif before is not None:
            callback = orb.events.Callback(self.save, values=values, **context)
            after.addCallback(orb.events.PreSaveEvent,
                              callback,
                              record=after,
                              once=True)
            return callback

        if values is not None:
            self.update(values, **context)

        # create the commit options
        context = self.context(**context)
        new_record = not self.isRecord()

        # create the pre-commit event
        changes = self.changes(columns=context.columns)
        event = orb.events.PreSaveEvent(record=self,
                                        context=context,
                                        newRecord=new_record,
                                        changes=changes)
        if self.processEvent(event):
            self.onPreSave(event)

        if event.preventDefault:
            return event.result

        # check to see if we have any modifications to store
        if not (self.isModified() and self.validate()):
            return False

        conn = context.db.connection()
        if not self.isRecord():
            records, _ = conn.insert([self], context)
            if records:
                event = orb.events.LoadEvent(record=self, data=records[0])
                self._load(event)
        else:
            conn.update([self], context)

        # mark all the data as committed
        cols = [self.schema().column(c).name() for c in context.columns or []]
        with WriteLocker(self.__dataLock):
            for col_name, (_, value) in self.__values.items():
                if not cols or col_name in cols:
                    self.__values[col_name] = (value, value)

        # create post-commit event
        event = orb.events.PostSaveEvent(record=self,
                                         context=context,
                                         newRecord=new_record,
                                         changes=changes)
        if self.processEvent(event):
            self.onPostSave(event)
        return True
Ejemplo n.º 22
0
    def get(self, column, useMethod=True, **context):
        """
        Returns the value for the column for this record.

        :param      column      | <orb.Column> || <str>
                    default     | <variant>
                    inflated    | <bool>

        :return     <variant>
        """
        if isinstance(column, (str, unicode)) and '.' in column:
            parts = column.split('.')
            sub_context = context.copy()
            sub_context['inflated'] = True
            value = self
            for part in parts[:-1]:
                if not value:
                    return None
                value = value.get(part, useMethod=useMethod, **sub_context)

            if value:
                return value.get(parts[-1], useMethod=useMethod, **context)
            else:
                return None
        else:
            my_context = self.context()

            for k, v in my_context.raw_values.items():
                if k not in orb.Context.QueryFields:
                    context.setdefault(k, v)

            sub_context = orb.Context(**context)

            # normalize the given column
            col = self.schema().column(column, raise_=False)
            if not col:
                collector = self.schema().collector(column)
                if collector:
                    try:
                        return self.__cache[collector][sub_context]
                    except KeyError:
                        records = collector(self,
                                            useMethod=useMethod,
                                            context=sub_context)
                        self.__cache[collector][sub_context] = records
                        return records
                else:
                    raise errors.ColumnNotFound(self.schema().name(), column)

            # lookup the shortuct value vs. the local one
            elif col.shortcut():
                return self.get(col.shortcut(), **context)

            # don't inflate if the requested value is a field
            if column == col.field():
                sub_context.inflated = False

            # call the getter method fot this record if one exists
            if useMethod:
                method = col.gettermethod()
                if method is not None:
                    return method(self, context=sub_context)

            # grab the current value
            with ReadLocker(self.__dataLock):
                old_value, value = self.__values.get(col.name(), (None, None))

            # return a reference when desired
            out_value = col.restore(value, sub_context)
            if isinstance(out_value,
                          orb.Model) and not isinstance(value, orb.Model):
                with WriteLocker(self.__dataLock):
                    self.__values[col.name()] = (old_value, out_value)
            return out_value
Ejemplo n.º 23
0
 def preload(self, cache, **context):
     context = self.context(**context)
     with WriteLocker(self.__cacheLock):
         for key, value in cache.items():
             self.__preload.setdefault(key, {})
             self.__preload[key][context] = value
Ejemplo n.º 24
0
    def update(self, records, useMethod=True, **context):
        if useMethod and self.__collector is not None and self.__collector.settermethod(
        ) is not None:
            return self.__collector.settermethod()(self.__record, records,
                                                   **context)

        # clean up the records for removal
        if isinstance(records, dict):
            if 'ids' in records:
                return self.update(records['ids'])
            elif 'records' in records:
                return self.update(records['records'])
            else:
                raise orb.errors.OrbError(
                    'Invalid input for collection update: {0}'.format(records))
        else:
            output_records = []

            if isinstance(records, (list, set, tuple)):
                ids = []
                id_col = self.__model.schema().idColumn()

                for record in records:
                    if isinstance(record, dict):
                        record_attributes = record
                        record_id = record_attributes.pop(id_col.name(), None)
                        if not record_id:
                            if isinstance(self.__collector, orb.ReverseLookup):
                                reference_col = self.__collector.targetColumn()
                                reference_id = self.__record.id()
                                record_attributes.pop(reference_col.name(),
                                                      None)
                                record_attributes[
                                    reference_col.field()] = reference_id

                            record = self.__model.create(
                                record_attributes, **context).id()
                        else:
                            record = self.__model(record_id, **context)
                            if record_attributes:
                                record.update(record_attributes)
                                record.save()

                    output_records.append(record)

                    if isinstance(record, orb.Model):
                        ids.append(record.id())
                    else:
                        ids.append(record)

            elif isinstance(records, orb.Collection):
                ids = records.ids()
                output_records = records.records()

            else:
                raise orb.errors.OrbError(
                    'Invalid input for collection update: {0}'.format(records))

            # update a pipe
            if isinstance(self.__collector, orb.Pipe):
                pipe = self.__collector

                orb_context = self.context(**context)
                through = pipe.throughModel()
                curr_ids = self.ids()

                remove_ids = set(curr_ids) - set(ids)
                add_ids = set(ids) - set(curr_ids)

                # remove old records
                if remove_ids:
                    q = orb.Query(through, pipe.from_()) == self.__record
                    q &= orb.Query(through, pipe.to()).in_(remove_ids)
                    orb_context.where = q
                    through.select(context=orb_context).delete()

                # create new records
                if add_ids:
                    collection = orb.Collection([
                        through({
                            pipe.from_(): self.__record,
                            pipe.to(): id
                        },
                                context=orb_context) for id in add_ids
                    ])
                    collection.save()

            # udpate a reverse lookup
            elif isinstance(self.__collector, orb.ReverseLookup):
                orb_context = self.context(**context)
                source = self.__collector.targetColumn()
                model = source.schema().model()

                q = orb.Query(source) == self.__record
                if ids:
                    q &= orb.Query(model).notIn(ids)

                # determine the reverse lookups to remove from this collection
                remove = model.select(where=q, context=orb_context)

                # check the remove action to determine how to handle this situation
                if self.__collector.removeAction() == 'delete':
                    remove.delete()
                else:
                    for record in remove:
                        record.set(source, None)
                        record.save()

                # determine the new records to add to this collection
                if ids:
                    q = orb.Query(model).in_(ids)
                    q &= (orb.Query(source) !=
                          self.__record) | (orb.Query(source) == None)

                    add = model.select(where=q, context=orb_context)
                    for record in add:
                        record.set(source, self.__record)
                        record.save()

            else:
                raise NotImplementedError

            # cache the output records
            with WriteLocker(self.__cacheLock):
                self.__preload.clear()
                self.__cache = defaultdict(dict)

            return output_records
Ejemplo n.º 25
0
    def values(self, *columns, **context):
        if self.isNull():
            return []

        orig_context = context
        context = self.context(**orig_context)

        try:
            with ReadLocker(self.__cacheLock):
                return self.__cache['values'][(context, columns)]
        except KeyError:
            try:
                with ReadLocker(self.__cacheLock):
                    records = self.__cache['records'][context]
            except KeyError:
                try:
                    with ReadLocker(self.__cacheLock):
                        raw = self.__preload['records'][context]
                except KeyError:
                    context.columns = columns
                    conn = context.db.connection()
                    raw = conn.select(self.__model, context)

                schema = self.__model.schema()
                values = []
                fields = [schema.column(col) for col in columns]
                for record in raw:
                    if not context.inflated:
                        record_values = [
                            record[field.field()] for field in fields
                        ]
                    else:
                        record_values = []
                        for i, field in enumerate(fields):
                            col = columns[i]
                            raw_values = orig_context.copy()
                            raw_values['distinct'] = None
                            if isinstance(
                                    field, orb.ReferenceColumn
                            ) and raw_values.get('inflated') is None:
                                raw_values['inflated'] = col != field.field()

                            val = field.restore(
                                record[field.field()],
                                context=orb.Context(**raw_values))
                            record_values.append(val)

                    if len(fields) == 1:
                        values.append(record_values[0])
                    else:
                        values.append(record_values)

                with WriteLocker(self.__cacheLock):
                    self.__cache['values'][(context, columns)] = values
                return values

            # use preloaded cache for values when possible
            else:
                if len(columns) == 1:
                    return [
                        record.get(columns[0]) if record else None
                        for record in records
                    ]
                else:
                    return [(record.get(c) for c in columns)
                            for record in records]