]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- simplified the descriptor system to no longer use the hybrid extension, instead...
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 28 Dec 2010 22:50:36 +0000 (17:50 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 28 Dec 2010 22:50:36 +0000 (17:50 -0500)
the Proxy(QueryableAttribute) object as the public facing interface.   This simplifies
the descriptor system and will allow straightforward integration with attribute events.

lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/descriptor_props.py
lib/sqlalchemy/orm/util.py
test/orm/test_descriptor.py

index e2d2c559b0952f8ec200b4109ad5adf9a3a811c6..d6dc10ad47bb11b15abf0df2d849c8527ea09e39 100644 (file)
@@ -145,16 +145,25 @@ def create_proxied_attribute(descriptor):
     Returns a new QueryableAttribute type that delegates descriptor
     behavior and getattr() to the given descriptor.
     """
-
+    
+    # TODO: can move this to descriptor_props if the need for this
+    # function is removed from ext/hybrid.py
+    
     class Proxy(QueryableAttribute):
-        """A combination of InsturmentedAttribute and a regular descriptor."""
+        """Presents the :class:`.QueryableAttribute` interface as a
+        proxy on top of a Python descriptor / :class:`.PropComparator` 
+        combination.
+        
+        """
 
-        def __init__(self, class_, key, descriptor, comparator, adapter=None):
+        def __init__(self, class_, key, descriptor, comparator, 
+                                adapter=None, doc=None):
             self.class_ = class_
             self.key = key
             self.descriptor = descriptor
             self._comparator = comparator
             self.adapter = adapter
+            self.__doc__ = doc
             
         @util.memoized_property
         def comparator(self):
@@ -164,11 +173,12 @@ def create_proxied_attribute(descriptor):
                 self._comparator = self._comparator.adapted(self.adapter)
             return self._comparator
         
-        def adapted(self, adapter):
-            return self.__class__(self.class_, self.key, self.descriptor,
-                                       self._comparator,
-                                       adapter)
-
+        def __get__(self, instance, owner):
+            if instance is None:
+                return self
+            else:
+                return self.descriptor.__get__(instance, owner)
+                
         def __str__(self):
             return self.key
         
index 4c4bc821fb86e3aae5e615dfe5e66bd354375c1a..347f9bce9a9e1b762cc0eca8020c4b98ce239804 100644 (file)
@@ -7,8 +7,7 @@
 
 """Descriptor proprerties are more "auxilliary" properties
 that exist as configurational elements, but don't participate
-as actively in the load/persist ORM loop.   They all
-build on the "hybrid" extension to produce class descriptors.
+as actively in the load/persist ORM loop.   
 
 """
 
@@ -22,13 +21,12 @@ properties = util.importlater('sqlalchemy.orm', 'properties')
 class DescriptorProperty(MapperProperty):
     """:class:`MapperProperty` which proxies access to a 
         user-defined descriptor."""
-
-    def instrument_class(self, mapper):
-        from sqlalchemy.ext import hybrid
         
+    doc = None
+    
+    def instrument_class(self, mapper):
         prop = self
         
-        # hackety hack hack
         class _ProxyImpl(object):
             accepts_scalar_loader = False
             expire_missing = True
@@ -52,39 +50,25 @@ class DescriptorProperty(MapperProperty):
                 delattr(obj, self.name)
             def fget(obj):
                 return getattr(obj, self.name)
-            fget.__doc__ = self.doc
 
-            descriptor = hybrid.property_(
+            self.descriptor = property(
                 fget=fget,
                 fset=fset,
                 fdel=fdel,
             )
-        elif isinstance(self.descriptor, property):
-            descriptor = hybrid.property_(
-                fget=self.descriptor.fget,
-                fset=self.descriptor.fset,
-                fdel=self.descriptor.fdel,
-            )
-        else:
-            descriptor = hybrid.property_(
-                fget=self.descriptor.__get__,
-                fset=self.descriptor.__set__,
-                fdel=self.descriptor.__delete__,
-            )
         
         proxy_attr = attributes.\
-                    create_proxied_attribute(self.descriptor or descriptor)\
+                    create_proxied_attribute(self.descriptor)\
                     (
                         self.parent.class_,
                         self.key, 
-                        self.descriptor or descriptor,
-                        lambda: self._comparator_factory(mapper)
+                        self.descriptor,
+                        lambda: self._comparator_factory(mapper),
+                        doc=self.doc
                     )
-        def get_comparator(owner):
-            return util.update_wrapper(proxy_attr, descriptor)
-        descriptor.expr = get_comparator
-        descriptor.impl = _ProxyImpl(self.key)
-        mapper.class_manager.instrument_attribute(self.key, descriptor)
+
+        proxy_attr.impl = _ProxyImpl(self.key)
+        mapper.class_manager.instrument_attribute(self.key, proxy_attr)
     
 
 class CompositeProperty(DescriptorProperty):
index 52e250239bc9e1dff6ed451bdef3564214356966..4a8b1713c7948f19f50166b645436d0753839989 100644 (file)
@@ -247,13 +247,12 @@ class AliasedClass(object):
                         'parentmapper':self.__mapper}
                     )
 
-    def __adapt_prop(self, prop):
-        existing = getattr(self.__target, prop.key)
+    def __adapt_prop(self, existing, key):
         comparator = existing.comparator.adapted(self.__adapt_element)
 
-        queryattr = attributes.QueryableAttribute(self, prop.key,
+        queryattr = attributes.QueryableAttribute(self, key,
             impl=existing.impl, parententity=self, comparator=comparator)
-        setattr(self, prop.key, queryattr)
+        setattr(self, key, queryattr)
         return queryattr
 
     def __getattr__(self, key):
@@ -268,7 +267,7 @@ class AliasedClass(object):
             raise AttributeError(key)
         
         if isinstance(attr, attributes.QueryableAttribute):
-            return self.__adapt_prop(attr.property)
+            return self.__adapt_prop(attr, key)
         elif hasattr(attr, 'func_code'):
             is_method = getattr(self.__target, key, None)
             if is_method and is_method.im_self is not None:
index b36735e8e7f2628268bb5e4b22371356d1e4ccff..a09e1304714fa888383f619f2c0bbe220706a5b2 100644 (file)
@@ -1,7 +1,8 @@
-from sqlalchemy.orm import descriptor_props
+from sqlalchemy.orm import descriptor_props, aliased
 from sqlalchemy.orm.interfaces import PropComparator
+from sqlalchemy.orm.properties import ColumnProperty
 from sqlalchemy.sql import column
-from sqlalchemy import Column, Integer, func
+from sqlalchemy import Column, Integer, func, String
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.util import partial
 from test.orm import _base
@@ -104,3 +105,29 @@ class DescriptorInstrumentationTest(_base.ORMTest):
             "foo = upper(:upper_1)"
         )
         
+
+    def test_aliased_comparator(self):
+        class Comparator(ColumnProperty.Comparator):
+            __hash__ = None
+            def __eq__(self, other):
+                return func.foobar(self.__clause_element__()) ==\
+                            func.foobar(other)
+
+        Foo = self._fixture()
+        Foo._name = Column('name', String)
+        
+        def comparator_factory(self, mapper):
+            prop = mapper._props['_name']
+            return Comparator(prop, mapper)
+        
+        d = TestDescriptor(Foo, 'foo', comparator_factory=comparator_factory)
+        d.instrument_class(Foo.__mapper__)
+        
+        eq_(
+            str(Foo.foo == 'ed'),
+           "foobar(foo.name) = foobar(:foobar_1)"
+        )
+        eq_(
+            str(aliased(Foo).foo == 'ed'),
+            "foobar(foo_1.name) = foobar(:foobar_1)"
+        )