From 29e2f5297685ab900d1268a074c601d0a2beead2 Mon Sep 17 00:00:00 2001 From: Malthe Borch Date: Thu, 18 Oct 2012 10:17:38 +0200 Subject: [PATCH 1/3] Refactor. --- plone/dexterity/content.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 4181617..bf8d39b 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -53,7 +53,6 @@ class FTIAwareSpecification(ObjectSpecificationDescriptor): """ def __get__(self, inst, cls=None): - # We're looking at a class - fall back on default if inst is None: return getObjectSpecification(cls) @@ -316,16 +315,11 @@ class Container(DAVCollectionMixin, BrowserDefaultMixin, CMFCatalogAware, CMFOrd setattr(self, k, v) def __getattr__(self, name): - - # attribute was not found; try to look it up in the schema and return - # a default - - schema = SCHEMA_CACHE.get(self.portal_type) - if schema is not None: - field = schema.get(name, None) - if field is not None: - return deepcopy(field.default) - + try: + return DexterityContent.__getattr__(self, name) + except AttributeError: + pass + # Be specific about the implementation we use return CMFOrderedBTreeFolderBase.__getattr__(self, name) From 26d27cb66fd0b32cbb0cd76231df359257cf0baf Mon Sep 17 00:00:00 2001 From: Malthe Borch Date: Thu, 18 Oct 2012 10:25:18 +0200 Subject: [PATCH 2/3] Return an empty tuple if an FTI is not found, instead of ``None``. --- plone/dexterity/schema.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 4e18b50..eee4e29 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -77,12 +77,13 @@ class SchemaCache(object): if cached is None: subtypes = [] fti = queryUtility(IDexterityFTI, name=portal_type) - if fti is not None: - for behavior_name in fti.behaviors: - behavior = queryUtility(IBehavior, name=behavior_name) - if behavior is not None and behavior.marker is not None: - subtypes.append(behavior.marker) - cached = self.subtypes_cache[portal_type] = tuple(subtypes) + if fti is None: + return () + for behavior_name in fti.behaviors: + behavior = queryUtility(IBehavior, name=behavior_name) + if behavior is not None and behavior.marker is not None: + subtypes.append(behavior.marker) + cached = self.subtypes_cache[portal_type] = tuple(subtypes) return cached @synchronized(lock) From dd491480b869bbe21ee50ef413c263705af7b170 Mon Sep 17 00:00:00 2001 From: Malthe Borch Date: Thu, 18 Oct 2012 10:29:18 +0200 Subject: [PATCH 3/3] Default attribute accessor now returns a field default for a field provided by a subtype. --- docs/HISTORY.txt | 4 ++++ plone/dexterity/content.py | 8 +++++++- plone/dexterity/tests/test_content.py | 7 +++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/HISTORY.txt b/docs/HISTORY.txt index 54a7d3d..c51b411 100644 --- a/docs/HISTORY.txt +++ b/docs/HISTORY.txt @@ -4,6 +4,10 @@ Changelog 2.0.1 (unreleased) ------------------ +- The default attribute accessor now also looks through subtypes + (behaviors) to find a field default. + [malthe] + - Added support in the FTI to look up behaviors by utility name when getting additional schemata (i.e. fields provided by behaviors). diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index bf8d39b..2c6912b 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -185,7 +185,13 @@ class DexterityContent(DAVResourceMixin, PortalContent, DefaultDublinCoreImpl, C field = schema.get(name, None) if field is not None: return deepcopy(field.default) - + + # do the same for each subtype + for schema in SCHEMA_CACHE.subtypes(self.portal_type): + field = schema.get(name, None) + if field is not None: + return deepcopy(field.default) + raise AttributeError(name) # Let __name__ and id be identical. Note that id must be ASCII in Zope 2, diff --git a/plone/dexterity/tests/test_content.py b/plone/dexterity/tests/test_content.py index 38b2368..ff9fdd7 100644 --- a/plone/dexterity/tests/test_content.py +++ b/plone/dexterity/tests/test_content.py @@ -231,7 +231,7 @@ class TestContent(MockTestCase): pass class ISubtype(Interface): - pass + baz = zope.schema.TextLine(title=u"baz", default=u"baz") behavior1 = BehaviorRegistration(u"Behavior1", "", IBehavior1, None, None) behavior2 = BehaviorRegistration(u"Behavior2", "", IBehavior2, ISubtype, None) @@ -258,7 +258,10 @@ class TestContent(MockTestCase): # the cache. This is not the case, as evidenced by .count(1) above. self.assertEquals(True, ISubtype.providedBy(item)) self.assertEquals(True, ISchema.providedBy(item)) - + + # Subtypes provide field defaults. + self.assertEquals(u"baz", getattr(item, "baz", None)) + # We also need to ensure that the _v_ attribute doesn't hide any # interface set directly on the instance with alsoProvides() or # directlyProvides(). This is done by clearing the cache when these