diff options
-rw-r--r-- | lib/sqlalchemy/orm/__init__.py | 64 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/properties.py | 12 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 7 | ||||
-rw-r--r-- | test/orm/mapper.py | 7 |
5 files changed, 58 insertions, 36 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index 42964545d..3d8461dfd 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -253,30 +253,46 @@ def composite(class_, *cols, **kwargs): """Return a composite column-based property for use with a Mapper. This is very much like a column-based property except the given class - is used to construct values composed of one or more columns. The class must - implement a constructor with positional arguments matching the order of - columns given, as well as a __colset__() method which returns its attributes - in column order. + is used to represent "composite" values composed of one or more columns. + + The class must implement a constructor with positional arguments matching + the order of columns supplied here, as well as a __composite_values__() + method which returns values in the same order. + + A simple example is representing separate two columns in a table as a + single, first-class "Point" object:: + + class Point(object): + def __init__(self, x, y): + self.x = x + self.y = y + def __composite_values__(self): + return (self.x, self.y) + + # and then in the mapping: + ... composite(Point, mytable.c.x, mytable.c.y) ... + + Arguments are: - class\_ - the "composite type" class. + class\_ + The "composite type" class. - \*cols - list of Column objects to be mapped. + \*cols + List of Column objects to be mapped. - group - a group name for this property when marked as deferred. + group + A group name for this property when marked as deferred. - deferred - when True, the column property is "deferred", meaning that - it does not load immediately, and is instead loaded when the - attribute is first accessed on an instance. See also - [sqlalchemy.orm#deferred()]. + deferred + When True, the column property is "deferred", meaning that + it does not load immediately, and is instead loaded when the + attribute is first accessed on an instance. See also + [sqlalchemy.orm#deferred()]. - comparator - an optional instance of [sqlalchemy.orm#PropComparator] which - provides SQL expression generation functions for this composite - type. + comparator + An optional instance of [sqlalchemy.orm#PropComparator] which + provides SQL expression generation functions for this composite + type. """ return CompositeProperty(class_, *cols, **kwargs) @@ -483,8 +499,7 @@ def extension(ext): return ExtensionOption(ext) def eagerload(name): - """Return a ``MapperOption`` that will convert the property of the - given name into an eager load. + """Return a ``MapperOption`` that will convert the property of the given name into an eager load. Used with ``query.options()``. """ @@ -492,11 +507,10 @@ def eagerload(name): return strategies.EagerLazyOption(name, lazy=False) def eagerload_all(name): - """Return a ``MapperOption`` that will convert all - properties along the given dot-separated path into an - eager load. + """Return a ``MapperOption`` that will convert all properties along the given dot-separated path into an eager load. - e.g:: + For example, this:: + query.options(eagerload_all('orders.items.keywords'))... will set all of 'orders', 'orders.items', and 'orders.items.keywords' diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 600dab41f..c7bdc4274 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -100,12 +100,12 @@ class CompositeProperty(ColumnProperty): obj = getattr(object, self.key, None) if obj is None: obj = self.composite_class(*[None for c in self.columns]) - for a, b in zip(self.columns, value.__colset__()): + for a, b in zip(self.columns, value.__composite_values__()): if a is column: setattr(obj, b, value) def get_col_value(self, column, value): - for a, b in zip(self.columns, value.__colset__()): + for a, b in zip(self.columns, value.__composite_values__()): if a is column: return b @@ -114,10 +114,14 @@ class CompositeProperty(ColumnProperty): if other is None: return sql.and_(*[a==None for a in self.prop.columns]) else: - return sql.and_(*[a==b for a, b in zip(self.prop.columns, other.__colset__())]) + return sql.and_(*[a==b for a, b in + zip(self.prop.columns, + other.__composite_values__())]) def __ne__(self, other): - return sql.or_(*[a!=b for a, b in zip(self.prop.columns, other.__colset__())]) + return sql.or_(*[a!=b for a, b in + zip(self.prop.columns, + other.__composite_values__())]) class PropertyLoader(StrategizedProperty): """Describes an object property that holds a single item or list diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 37d5b10a3..1b5871858 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -76,8 +76,8 @@ class Query(object): # convert composite types to individual args # TODO: account for the order of columns in the # ColumnProperty it corresponds to - if hasattr(ident, '__colset__'): - ident = ident.__colset__() + if hasattr(ident, '__composite_values__'): + ident = ident.__composite_values__() key = self.mapper.identity_key_from_primary_key(ident) return self._get(key, ident, **kwargs) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 8a0cc6035..6e2860f3a 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -37,9 +37,12 @@ class ColumnLoader(LoaderStrategy): def _init_composite_attribute(self): self.logger.info("register managed composite attribute %s on class %s" % (self.key, self.parent.class_.__name__)) def copy(obj): - return self.parent_property.composite_class(*obj.__colset__()) + return self.parent_property.composite_class( + *obj.__composite_values__()) def compare(a, b): - for col, aprop, bprop in zip(self.columns, a.__colset__(), b.__colset__()): + for col, aprop, bprop in zip(self.columns, + a.__composite_values__(), + b.__composite_values__()): if not col.type.compare_values(aprop, bprop): return False else: diff --git a/test/orm/mapper.py b/test/orm/mapper.py index 12670c654..414a1936f 100644 --- a/test/orm/mapper.py +++ b/test/orm/mapper.py @@ -818,7 +818,7 @@ class CompositeTypesTest(ORMTest): def __init__(self, x, y): self.x = x self.y = y - def __colset__(self): + def __composite_values__(self): return [self.x, self.y] def __eq__(self, other): return other.x == self.x and other.y == self.y @@ -893,8 +893,9 @@ class CompositeTypesTest(ORMTest): def __init__(self, id, version): self.id = id self.version = version - def __colset__(self): - return [self.id, self.version] + def __composite_values__(self): + # a tuple this time + return (self.id, self.version) def __eq__(self, other): return other.id == self.id and other.version == self.version def __ne__(self, other): |