]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
update versioned rows examples for 1.4
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Oct 2021 22:56:25 +0000 (18:56 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 9 Oct 2021 22:56:25 +0000 (18:56 -0400)
Repaired the examples in examples/versioned_rows to use SQLAlchemy 1.4 APIs
correctly; these examples had been missed when API changes like removing
"passive" from ``Session.is_modified()`` were made as well as the
``do_orm_execute()`` event hook were added.

Fixes: #7169
Change-Id: I30930a3b185dc0f73be4467faa2f97c5ebf1a36d

doc/build/changelog/unreleased_14/7169.rst [new file with mode: 0644]
doc/build/conf.py
examples/versioned_rows/versioned_map.py
examples/versioned_rows/versioned_rows.py
examples/versioned_rows/versioned_rows_w_versionid.py
examples/versioned_rows/versioned_update_old_row.py

diff --git a/doc/build/changelog/unreleased_14/7169.rst b/doc/build/changelog/unreleased_14/7169.rst
new file mode 100644 (file)
index 0000000..e737855
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, examples
+    :tickets: 7169
+
+    Repaired the examples in examples/versioned_rows to use SQLAlchemy 1.4 APIs
+    correctly; these examples had been missed when API changes like removing
+    "passive" from ``Session.is_modified()`` were made as well as the
+    ``do_orm_execute()`` event hook were added.
index 2fb5b8e68c5233db149e892f808538e858944cbc..1b434fad277275818673fc86905b9576d10c5182 100644 (file)
@@ -58,6 +58,7 @@ changelog_sections = [
     "orm declarative",
     "orm querying",
     "orm configuration",
+    "examples",
     "engine",
     "sql",
     "schema",
index 9abbb3e09cdddba94e4179a16ce1a5827ee30f66..c2fa6c2a91fe4eba0f5d4ea16d4090214f37667c 100644 (file)
@@ -53,9 +53,7 @@ def before_flush(session, flush_context, instances):
 
     """
     for instance in session.dirty:
-        if hasattr(instance, "new_version") and session.is_modified(
-            instance, passive=True
-        ):
+        if hasattr(instance, "new_version") and session.is_modified(instance):
 
             # make it transient
             instance.new_version(session)
index 01067431c88a00d9ce619adaae5d18ba130f8019..7179d04749dc437e2e28e75169d3f66e789f9969 100644 (file)
@@ -34,7 +34,7 @@ def before_flush(session, flush_context, instances):
     for instance in session.dirty:
         if not isinstance(instance, Versioned):
             continue
-        if not session.is_modified(instance, passive=True):
+        if not session.is_modified(instance):
             continue
 
         if not attributes.instance_state(instance).has_identity:
index ac5d0f58afa7f1b1ac6582b36e0752f9144af663..87d246da1d9931d096faf812781e0ec7f6443d49 100644 (file)
@@ -64,7 +64,7 @@ def before_flush(session, flush_context, instances):
     for instance in session.dirty:
         if not isinstance(instance, Versioned):
             continue
-        if not session.is_modified(instance, passive=True):
+        if not session.is_modified(instance):
             continue
 
         if not attributes.instance_state(instance).has_identity:
index 0da28cf42daa6c8cbe62b4f16366b395188baf39..049c1a7a0211b1a9ae8deddb546ee749726a6b34 100644 (file)
@@ -1,6 +1,6 @@
 """Illustrates the same UPDATE into INSERT technique of ``versioned_rows.py``,
 but also emits an UPDATE on the **old** row to affect a change in timestamp.
-Also includes a :meth:`.QueryEvents.before_compile` hook to limit queries
+Also includes a :meth:`.SessionEvents.do_orm_execute` hook to limit queries
 to only the most recent version.
 
 """
@@ -8,22 +8,22 @@ to only the most recent version.
 import datetime
 import time
 
+from sqlalchemy import and_
 from sqlalchemy import Column
 from sqlalchemy import create_engine
 from sqlalchemy import DateTime
 from sqlalchemy import event
 from sqlalchemy import inspect
 from sqlalchemy import Integer
-from sqlalchemy import literal
 from sqlalchemy import String
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.orm import attributes
 from sqlalchemy.orm import backref
 from sqlalchemy.orm import make_transient
 from sqlalchemy.orm import make_transient_to_detached
-from sqlalchemy.orm import Query
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import Session
+from sqlalchemy.orm import with_loader_criteria
 
 
 Base = declarative_base()
@@ -38,6 +38,9 @@ def current_time():
 
 
 class VersionedStartEnd(object):
+    start = Column(DateTime, primary_key=True)
+    end = Column(DateTime, primary_key=True)
+
     def __init__(self, **kw):
         # reduce some verbosity when we make a new object
         kw.setdefault("start", current_time() - datetime.timedelta(days=3))
@@ -88,7 +91,7 @@ def before_flush(session, flush_context, instances):
     for instance in session.dirty:
         if not isinstance(instance, VersionedStartEnd):
             continue
-        if not session.is_modified(instance, passive=True):
+        if not session.is_modified(instance):
             continue
 
         if not attributes.instance_state(instance).has_identity:
@@ -100,27 +103,18 @@ def before_flush(session, flush_context, instances):
         session.add(instance)
 
 
-@event.listens_for(Query, "before_compile", retval=True)
-def before_compile(query):
+@event.listens_for(Session, "do_orm_execute", retval=True)
+def do_orm_execute(execute_state):
     """ensure all queries for VersionedStartEnd include criteria"""
 
-    for ent in query.column_descriptions:
-        entity = ent["entity"]
-        if entity is None:
-            continue
-        insp = inspect(ent["entity"])
-        mapper = getattr(insp, "mapper", None)
-        if mapper and issubclass(mapper.class_, VersionedStartEnd):
-            query = query.enable_assertions(False).filter(
-                # using a literal "now" because SQLite's "between"
-                # seems to be inclusive. In practice, this would be
-                # ``func.now()`` and we'd be using PostgreSQL
-                literal(
-                    current_time() + datetime.timedelta(seconds=1)
-                ).between(ent["entity"].start, ent["entity"].end)
-            )
-
-    return query
+    ct = current_time() + datetime.timedelta(seconds=1)
+    execute_state.statement = execute_state.statement.options(
+        with_loader_criteria(
+            VersionedStartEnd,
+            lambda cls: and_(ct > cls.start, ct < cls.end),
+            include_aliases=True,
+        )
+    )
 
 
 class Parent(VersionedStartEnd, Base):