]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- A warning is emitted if the :meth:`.MapperEvents.before_configured`
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 11 Mar 2014 16:39:00 +0000 (12:39 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 11 Mar 2014 16:39:00 +0000 (12:39 -0400)
or :meth:`.MapperEvents.after_configured` events are applied to a
specific mapper or mapped class, as the events are only invoked
for the :class:`.Mapper` target at the general level.

doc/build/changelog/changelog_09.rst
lib/sqlalchemy/orm/events.py
test/orm/test_events.py

index 58ac33559b70aec9adedaf34aa27c9aecb1e47b6..fc39ea347a626fc08cac0eae6c1b433260b985a0 100644 (file)
 .. changelog::
     :version: 0.9.4
 
+    .. change::
+        :tags: feature, orm
+
+        A warning is emitted if the :meth:`.MapperEvents.before_configured`
+        or :meth:`.MapperEvents.after_configured` events are applied to a
+        specific mapper or mapped class, as the events are only invoked
+        for the :class:`.Mapper` target at the general level.
+
     .. change::
         :tags: feature, orm
 
index 52dcca2328c0c90a4b165d53e8e594b303218e35..b6847523082b215d86e27d3ad7fcef59d757782e 100644 (file)
@@ -508,6 +508,13 @@ class MapperEvents(event.Events):
         target, identifier, fn = \
             event_key.dispatch_target, event_key.identifier, event_key.fn
 
+        if identifier in ("before_configured", "after_configured") and \
+            target is not mapperlib.Mapper:
+            util.warn(
+                    "'before_configured' and 'after_configured' ORM events "
+                    "only invoke with the mapper() function or Mapper class "
+                    "as the target.")
+
         if not raw or not retval:
             if not raw:
                 meth = getattr(cls, identifier)
@@ -590,11 +597,30 @@ class MapperEvents(event.Events):
         note is usually called automatically as mappings are first
         used.
 
+        This event can **only** be applied to the :class:`.Mapper` class
+        or :func:`.mapper` function, and not to individual mappings or
+        mapped classes.  It is only invoked for all mappings as a whole::
+
+            from sqlalchemy.orm import mapper
+
+            @event.listens_for(mapper, "before_configured")
+            def go():
+                # ...
+
         Theoretically this event is called once per
         application, but is actually called any time new mappers
         are to be affected by a :func:`.orm.configure_mappers`
         call.   If new mappings are constructed after existing ones have
-        already been used, this event can be called again.
+        already been used, this event can be called again.  To ensure
+        that a particular event is only called once and no further, the
+        ``once=True`` argument (new in 0.9.4) can be applied::
+
+            from sqlalchemy.orm import mapper
+
+            @event.listens_for(mapper, "before_configured", once=True)
+            def go():
+                # ...
+
 
         .. versionadded:: 0.9.3
 
@@ -607,11 +633,29 @@ class MapperEvents(event.Events):
         note is usually called automatically as mappings are first
         used.
 
+        This event can **only** be applied to the :class:`.Mapper` class
+        or :func:`.mapper` function, and not to individual mappings or
+        mapped classes.  It is only invoked for all mappings as a whole::
+
+            from sqlalchemy.orm import mapper
+
+            @event.listens_for(mapper, "after_configured")
+            def go():
+                # ...
+
         Theoretically this event is called once per
         application, but is actually called any time new mappers
         have been affected by a :func:`.orm.configure_mappers`
         call.   If new mappings are constructed after existing ones have
-        already been used, this event can be called again.
+        already been used, this event can be called again.  To ensure
+        that a particular event is only called once and no further, the
+        ``once=True`` argument (new in 0.9.4) can be applied::
+
+            from sqlalchemy.orm import mapper
+
+            @event.listens_for(mapper, "after_configured", once=True)
+            def go():
+                # ...
 
         """
 
index edafc3a8b298ed4855567ef2a969e39526730cfd..5260a724a7b08c19ae19b5f72e19c1af0e290365 100644 (file)
@@ -283,6 +283,42 @@ class MapperEventsTest(_RemoveListeners, _fixtures.FixtureTest):
         eq_(canary1, ['before_update', 'after_update'])
         eq_(canary2, [])
 
+    def test_before_after_configured_warn_on_non_mapper(self):
+        User, users = self.classes.User, self.tables.users
+
+        m1 = Mock()
+
+        mapper(User, users)
+        assert_raises_message(
+            sa.exc.SAWarning,
+            "before_configured' and 'after_configured' ORM events only "
+            "invoke with the mapper\(\) function or Mapper class as the target.",
+            event.listen, User, 'before_configured', m1
+        )
+
+        assert_raises_message(
+            sa.exc.SAWarning,
+            "before_configured' and 'after_configured' ORM events only "
+            "invoke with the mapper\(\) function or Mapper class as the target.",
+            event.listen, User, 'after_configured', m1
+        )
+
+    def test_before_after_configured(self):
+        User, users = self.classes.User, self.tables.users
+
+        m1 = Mock()
+        m2 = Mock()
+
+        mapper(User, users)
+
+        event.listen(mapper, "before_configured", m1)
+        event.listen(mapper, "after_configured", m2)
+
+        s = Session()
+        s.query(User)
+
+        eq_(m1.mock_calls, [call()])
+        eq_(m2.mock_calls, [call()])
 
     def test_retval(self):
         User, users = self.classes.User, self.tables.users