def process_query_property(self, query, paths, mappers): if self.alias: if isinstance(self.alias, basestring): mapper = mappers[-1] (root_mapper, propname) = paths[-1][-2:] prop = mapper.get_property(propname, resolve_synonyms=True) self.alias = prop.target.alias(self.alias) query._attributes[("user_defined_eager_row_processor", paths[-1])] = sql_util.ColumnAdapter(self.alias) else: (root_mapper, propname) = paths[-1][-2:] mapper = mappers[-1] prop = mapper.get_property(propname, resolve_synonyms=True) adapter = query._polymorphic_adapters.get(prop.mapper, None) query._attributes[("user_defined_eager_row_processor", paths[-1])] = adapter
def with_parent(self, instance, property=None): """add a join criterion corresponding to a relationship to the given parent instance. instance a persistent or detached instance which is related to class represented by this query. property string name of the property which relates this query's class to the instance. if None, the method will attempt to find a suitable property. currently, this method only works with immediate parent relationships, but in the future may be enhanced to work across a chain of parent mappers. """ from sqlalchemy.orm import properties mapper = object_mapper(instance) if property is None: for prop in mapper.iterate_properties: if isinstance(prop, properties.PropertyLoader) and prop.mapper is self.mapper: break else: raise exceptions.InvalidRequestError("Could not locate a property which relates instances of class '%s' to instances of class '%s'" % (self.mapper.class_.__name__, instance.__class__.__name__)) else: prop = mapper.get_property(property, resolve_synonyms=True) return self.filter(Query._with_lazy_criterion(instance, prop))
def __init__(self, **kwargs): for key, value in kwargs.items(): if ext.validate: if not mapper.get_property(key, resolve_synonyms=False, raiseerr=False): raise sa_exc.ArgumentError( "Invalid __init__ argument: '%s'" % key) setattr(self, key, value)
def __getattr__(self, key): mapper = class_mapper(self.cls, compile=False) if not mapper: return getattr(self.cls, key) else: prop = mapper.get_property(key) if not isinstance(prop, ColumnProperty): raise exceptions.InvalidRequestError("Property %r is not an instance of ColumnProperty (i.e. does not correspnd directly to a Column)." % key) return prop.columns[0]
def process_query_property(self, query, paths): if self.alias is not None and self.decorator is None: (mapper, propname) = paths[-1][-2:] prop = mapper.get_property(propname, resolve_synonyms=True) if isinstance(self.alias, basestring): self.alias = prop.target.alias(self.alias) self.decorator = mapperutil.create_row_adapter(self.alias) query._attributes[("eager_row_processor", paths[-1])] = self.decorator
def process_query_property(self, query, paths): if self.alias: if isinstance(self.alias, basestring): (mapper, propname) = paths[-1][-2:] prop = mapper.get_property(propname, resolve_synonyms=True) self.alias = prop.target.alias(self.alias) query._attributes[("user_defined_eager_row_processor", paths[-1])] = sql_util.ColumnAdapter(self.alias) else: query._attributes[("user_defined_eager_row_processor", paths[-1])] = None
def init_instance(self, mapper, class_, oldinit, instance, args, kwargs): session = kwargs.pop('_sa_session', self.context.registry()) if not isinstance(oldinit, types.MethodType): for key, value in kwargs.items(): if self.validate: if not mapper.get_property(key, resolve_synonyms=False, raiseerr=False): raise exceptions.ArgumentError("Invalid __init__ argument: '%s'" % key) setattr(instance, key, value) if self.save_on_init: session._save_impl(instance, entity_name=kwargs.pop('_sa_entity_name', None)) return EXT_CONTINUE
def from_dict(self, data): """ Update a mapped class with data from a JSON-style nested dict/list structure. """ # surrogate can be guessed from autoincrement/sequence but I guess # that's not 100% reliable, so we'll need an override mapper = sqlalchemy.orm.object_mapper(self) for key, value in data.iteritems(): if isinstance(value, dict): dbvalue = getattr(self, key) rel_class = mapper.get_property(key).mapper.class_ pk_props = rel_class._descriptor.primary_key_properties # If the data doesn't contain any pk, and the relationship # already has a value, update that record. if not [1 for p in pk_props if p.key in data] and \ dbvalue is not None: dbvalue.from_dict(value) else: record = rel_class.update_or_create(value) setattr(self, key, record) elif isinstance(value, list) and \ value and isinstance(value[0], dict): rel_class = mapper.get_property(key).mapper.class_ new_attr_value = [] for row in value: if not isinstance(row, dict): raise Exception( 'Cannot send mixed (dict/non dict) data ' 'to list relationships in from_dict data.') record = rel_class.update_or_create(row) new_attr_value.append(record) setattr(self, key, new_attr_value) else: setattr(self, key, value)
def init_instance(self, mapper, class_, oldinit, instance, args, kwargs): if self.save_on_init: entity_name = kwargs.pop("_sa_entity_name", None) session = kwargs.pop("_sa_session", None) if not isinstance(oldinit, types.MethodType): for key, value in kwargs.items(): if self.validate: if not mapper.get_property(key, resolve_synonyms=False, raiseerr=False): raise exceptions.ArgumentError("Invalid __init__ argument: '%s'" % key) setattr(instance, key, value) kwargs.clear() if self.save_on_init: session = session or self.context.registry() session._save_impl(instance, entity_name=entity_name) return EXT_CONTINUE
def __getattr__(self, key): mapper = class_mapper(self.cls, compile=False) if mapper: prop = mapper.get_property(key, raiseerr=False) if prop is None: raise exceptions.InvalidRequestError( "Class %r does not have a mapped column named %r" % (self.cls, key)) elif not isinstance(prop, ColumnProperty): raise exceptions.InvalidRequestError( "Property %r is not an instance of" " ColumnProperty (i.e. does not correspond" " directly to a Column)." % key) return getattr(self.cls, key)
def _join_to(self, prop, outerjoin=False, start=None): if start is None: start = self._joinpoint if isinstance(prop, list): keys = prop else: [keys,p] = self._locate_prop(prop, start=start) clause = self._from_obj[-1] currenttables = [clause] class FindJoinedTables(sql.NoColumnVisitor): def visit_join(self, join): currenttables.append(join.left) currenttables.append(join.right) FindJoinedTables().traverse(clause) mapper = start for key in keys: prop = mapper.get_property(key, resolve_synonyms=True) if prop._is_self_referential(): raise exceptions.InvalidRequestError("Self-referential query on '%s' property must be constructed manually using an Alias object for the related table." % str(prop)) # dont re-join to a table already in our from objects if prop.select_table not in currenttables: if outerjoin: if prop.secondary: clause = clause.outerjoin(prop.secondary, prop.get_join(mapper, primary=True, secondary=False)) clause = clause.outerjoin(prop.select_table, prop.get_join(mapper, primary=False)) else: clause = clause.outerjoin(prop.select_table, prop.get_join(mapper)) else: if prop.secondary: clause = clause.join(prop.secondary, prop.get_join(mapper, primary=True, secondary=False)) clause = clause.join(prop.select_table, prop.get_join(mapper, primary=False)) else: clause = clause.join(prop.select_table, prop.get_join(mapper)) elif prop.secondary is not None and prop.secondary not in currenttables: # TODO: this check is not strong enough for different paths to the same endpoint which # does not use secondary tables raise exceptions.InvalidRequestError("Can't join to property '%s'; a path to this table along a different secondary table already exists. Use explicit `Alias` objects." % prop.key) mapper = prop.mapper return (clause, mapper)
def compile(self, prop): """Called by the owning PropertyLoader to set up a backreference on the PropertyLoader's mapper. """ # try to set a LazyLoader on our mapper referencing the parent mapper mapper = prop.mapper.primary_mapper() if not mapper.get_property(self.key, raiseerr=False) is not None: pj = self.kwargs.pop('primaryjoin', None) sj = self.kwargs.pop('secondaryjoin', None) # the backref property is set on the primary mapper parent = prop.parent.primary_mapper() self.kwargs.setdefault('viewonly', prop.viewonly) self.kwargs.setdefault('post_update', prop.post_update) relation = PropertyLoader(parent, prop.secondary, pj, sj, backref=prop.key, is_backref=True, **self.kwargs) mapper._compile_property(self.key, relation) elif not isinstance(mapper.get_property(self.key), PropertyLoader): raise exceptions.ArgumentError( "Can't create backref '%s' on mapper '%s'; an incompatible " "property of that name already exists" % (self.key, str(mapper))) else: # else set one of us as the "backreference" parent = prop.parent.primary_mapper() if parent.class_ is not mapper.get_property( self.key)._get_target_class(): raise exceptions.ArgumentError( "Backrefs do not match: backref '%s' expects to connect to %s, " "but found a backref already connected to %s" % (self.key, str(parent.class_), str(mapper.get_property(self.key).mapper.class_))) if not mapper.get_property(self.key).is_backref: prop.is_backref = True if not prop.viewonly: prop._dependency_processor.is_backref = True # reverse_property used by dependencies.ManyToManyDP to check # association table operations prop.reverse_property = mapper.get_property(self.key) mapper.get_property(self.key).reverse_property = prop
def join_via(self, keys): """Given a list of keys that represents a path from this Query's mapper to a related mapper based on names of relations from one mapper to the next, return a ClauseElement representing a join from this Query's mapper to the endmost mapper. this method is deprecated in 0.4. """ mapper = self.mapper clause = None for key in keys: prop = mapper.get_property(key, resolve_synonyms=True) if clause is None: clause = prop.get_join(mapper) else: clause &= prop.get_join(mapper) mapper = prop.mapper return clause
def query_from_parent(cls, instance, property, **kwargs): """return a newly constructed Query object, with criterion corresponding to a relationship to the given parent instance. instance a persistent or detached instance which is related to class represented by this query. property string name of the property which relates this query's class to the instance. \**kwargs all extra keyword arguments are propigated to the constructor of Query. """ mapper = object_mapper(instance) prop = mapper.get_property(property, resolve_synonyms=True) target = prop.mapper criterion = cls._with_lazy_criterion(instance, prop) return Query(target, **kwargs).filter(criterion)