--- /dev/null
+.. change::
+ :tags: usecase, orm
+ :tickets: 10610
+
+ The ``populate_existing`` execution option is now honored when passed in the
+ :paramref:`.Session.get.execution_options` dict by the method
+ :meth:`.Session.get` and analogous in other session kinds. The current
+ :paramref:`.Session.get.populate_existing` parameter will takes precedence if
+ specified, overriding the value of the execution options.
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
:param populate_existing: causes the method to unconditionally emit
a SQL query and refresh the object with the newly loaded data,
regardless of whether or not the object is already present.
+ Setting this flag takes precedence over passing it as an
+ execution option.
:param with_for_update: optional boolean ``True`` indicating FOR UPDATE
should be used, or may be a dictionary containing flags to
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
:param populate_existing: causes the method to unconditionally emit
a SQL query and refresh the object with the newly loaded data,
regardless of whether or not the object is already present.
+ Setting this flag takes precedence over passing it as an
+ execution option.
:param with_for_update: optional boolean ``True`` indicating FOR UPDATE
should be used, or may be a dictionary containing flags to
ident: _PKIdentityArgument,
*,
options: Optional[Sequence[ORMOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
db_load_fn: Callable[..., _O],
*,
options: Optional[Sequence[ExecutableOption]] = None,
- populate_existing: bool = False,
+ populate_existing: bool | None = None,
with_for_update: ForUpdateParameter = None,
identity_token: Optional[Any] = None,
execution_options: OrmExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
) -> Optional[_O]:
+ # set populate_existing value; direct parameter
+ # takes precedence over execution_options
+ if populate_existing is not None:
+ execution_options = {
+ **execution_options, # type: ignore[typeddict-item]
+ "populate_existing": populate_existing,
+ }
+ else:
+ populate_existing = execution_options.get(
+ "populate_existing", False
+ )
+
# convert composite types to individual args
if (
is_composite_class(primary_key_identity)
"Cls",
5,
options=None,
- populate_existing=False,
+ populate_existing=None,
with_for_update=None,
identity_token=None,
execution_options=util.EMPTY_DICT,
for key, value in expected_opts.items():
eq_(gather_options[key], value)
+ @testing.combinations(
+ ("default", None, {}, None),
+ ("arg_true", True, {}, True),
+ ("arg_false", False, {}, False),
+ ("arg_true_exe_false", True, {"populate_existing": False}, True),
+ ("arg_false_exe_true", False, {"populate_existing": True}, False),
+ (
+ "exe_true",
+ None,
+ {"populate_existing": True},
+ True,
+ ),
+ (
+ "exe_false",
+ None,
+ {"populate_existing": False},
+ False,
+ ),
+ argnames="session_parameter,execution_opt,expected_pe",
+ id_="iaaa",
+ )
+ @testing.variation("object_in_session", [True, False])
+ def test_get_populate_existing(
+ self,
+ session_parameter,
+ execution_opt,
+ expected_pe,
+ object_in_session,
+ ):
+ users, User = self.tables.users, self.classes.User
+ self.mapper_registry.map_imperatively(User, users)
+
+ s = fixture_session()
+
+ s.add(User(id=1, name="name"))
+ s.commit()
+ s.close()
+ if object_in_session:
+ # prevent GC of the object
+ _ = s.get(User, 1)
+ s.connection().execute(
+ update(User).where(User.id == 1).values(name="newname")
+ )
+
+ gather_options = []
+
+ @event.listens_for(s, "do_orm_execute")
+ def check(ctx: ORMExecuteState) -> None:
+ gather_options.append(ctx.execution_options)
+
+ res = s.get(
+ User,
+ 1,
+ populate_existing=session_parameter,
+ execution_options=execution_opt,
+ )
+
+ if not object_in_session or expected_pe:
+ # object not in session (so we load) or we expected
+ # populate_existing to be set (so we load), ensure newer value and
+ # gather_options is present
+ eq_(res.name, "newname")
+
+ if expected_pe is None:
+ assert "populate_existing" not in gather_options[0]
+ else:
+ eq_(gather_options[0]["populate_existing"], expected_pe)
+
+ else:
+ # object was in the session and no populate_existing, so
+ # do_orm_execute never called
+ eq_(gather_options, [])
+ eq_(res.name, "name")
+
def test_autocommit_kw_accepted_but_must_be_false(self):
Session(autocommit=False)