diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2005-12-14 05:45:49 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2005-12-14 05:45:49 +0000 |
| commit | b7e5d114f563e7c92c3e168f8d65ad0d4cfb1388 (patch) | |
| tree | 7b2c4a8644efbb33ec6ba5cfa6c40f80f1a631d1 /lib/sqlalchemy | |
| parent | 3cd30fdc70385fd771b3e305d1440a31da63d962 (diff) | |
| download | sqlalchemy-b7e5d114f563e7c92c3e168f8d65ad0d4cfb1388.tar.gz | |
added select_by, get_by, magic methods
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/mapping/mapper.py | 81 | ||||
| -rw-r--r-- | lib/sqlalchemy/mapping/properties.py | 28 |
2 files changed, 109 insertions, 0 deletions
diff --git a/lib/sqlalchemy/mapping/mapper.py b/lib/sqlalchemy/mapping/mapper.py index 390bfbd48..5fdf0e35b 100644 --- a/lib/sqlalchemy/mapping/mapper.py +++ b/lib/sqlalchemy/mapping/mapper.py @@ -290,6 +290,79 @@ class Mapper(object): option.process(mapper) return mapper_registry.setdefault(hashkey, mapper) + def get_by(self, **params): + """returns a single object instance based on the given key/value criterion. + this is either the first value in the result list, or None if the list is + empty. + + the keys are mapped to property or column names mapped by this mapper's Table, and the values + are coerced into a WHERE clause separated by AND operators. If the local property/column + names dont contain the key, a search will be performed against this mapper's immediate + list of relations as well, forming the appropriate join conditions if a matching property + is located. + + e.g. u = usermapper.get_by(user_name = 'fred') + """ + x = self.select_by(**params) + if len(x): + return x[0] + else: + return None + + def select_by(self, **params): + """returns an array of object instances based on the given key/value criterion. + + the keys are mapped to property or column names mapped by this mapper's Table, and the values + are coerced into a WHERE clause separated by AND operators. If the local property/column + names dont contain the key, a search will be performed against this mapper's immediate + list of relations as well, forming the appropriate join conditions if a matching property + is located. + + e.g. result = usermapper.select_by(user_name = 'fred') + """ + clause = None + for key, value in params.iteritems(): + if value is False: + continue + c = self._get_criterion(key, value) + if c is None: + raise "Cant find criterion for property '"+ key + "'" + if clause is None: + clause = c + else: + clause &= c + return self.select_whereclause(clause) + + def _get_criterion(self, key, value): + """used by select_by to match a key/value pair against + local properties, column names, or a matching property in this mapper's + list of relations.""" + if self.props.has_key(key): + return self.props[key].columns[0] == value + elif self.table.c.has_key(key): + return self.table.c[key] == value + else: + for prop in self.props.values(): + c = prop.get_criterion(key, value) + if c is not None: + return c + else: + return None + + def __getattr__(self, key): + if (key.startswith('select_by_')): + key = key[10:] + def foo(arg): + return self.select_by(**{key:arg}) + return foo + elif (key.startswith('get_by_')): + key = key[7:] + def foo(arg): + return self.get_by(**{key:arg}) + return foo + else: + raise AttributeError(key) + def selectone(self, *args, **params): """works like select(), but only returns the first result by itself, or None if no objects returned.""" @@ -565,6 +638,14 @@ class MapperProperty(object): raise NotImplementedError() def _copy(self): raise NotImplementedError() + def get_criterion(self, key, value): + """Returns a WHERE clause suitable for this MapperProperty corresponding to the + given key/value pair, where the key is a column or object property name, and value + is a value to be matched. This is only picked up by PropertyLoaders. + + this is called by a mappers select_by method to formulate a set of key/value pairs into + a WHERE criterion that spans multiple tables if needed.""" + return None def hash_key(self): """describes this property and its instantiated arguments in such a way as to uniquely identify the concept this MapperProperty represents,within diff --git a/lib/sqlalchemy/mapping/properties.py b/lib/sqlalchemy/mapping/properties.py index f8e2b1b1f..59cc85a5f 100644 --- a/lib/sqlalchemy/mapping/properties.py +++ b/lib/sqlalchemy/mapping/properties.py @@ -254,6 +254,34 @@ class PropertyLoader(MapperProperty): if self.secondaryjoin is not None: self.secondaryjoin.accept_visitor(processor) + def get_criterion(self, key, value): + """given a key/value pair, determines if this PropertyLoader's mapper contains a key of the + given name in its property list, or if this PropertyLoader's association mapper, if any, + contains a key of the given name in its property list, and returns a WHERE clause against + the given value if found. + + this is called by a mappers select_by method to formulate a set of key/value pairs into + a WHERE criterion that spans multiple tables if needed.""" + # TODO: optimization: change mapper to accept a WHERE clause with separate bind parameters + # then cache the generated WHERE clauses here, since the creation + the copy_container + # is an extra expense + if self.mapper.props.has_key(key): + if self.secondaryjoin is not None: + c = (self.mapper.props[key].columns[0]==value) & self.primaryjoin & self.secondaryjoin + else: + c = (self.mapper.props[key].columns[0]==value) & self.primaryjoin + return c.copy_container() + elif self.mapper.table.c.has_key(key): + if self.secondaryjoin is not None: + c = (self.mapper.table.c[key].columns[0]==value) & self.primaryjoin & self.secondaryjoin + else: + c = (self.mapper.table.c[key].columns[0]==value) & self.primaryjoin + return c.copy_container() + elif self.association is not None: + c = self.mapper._get_criterion(key, value) & self.primaryjoin + return c.copy_container() + + return None def register_deleted(self, obj, uow): if not self.private: |
