mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-16 13:47:20 -04:00
91501e06a1
The architecture of Load is mostly rewritten here. The change includes removal of the "pluggable" aspect of the loader options, which would patch new methods onto Load. This has been replaced by normal methods that respond normally to typing annotations. As part of this change, the bake_loaders() and unbake_loaders() options, which have no effect since 1.4 and were unlikely to be in any common use, have been removed. Additionally, to support annotations for methods that make use of @decorator, @generative etc., modified format_argspec_plus to no longer return "args", instead returns "grouped_args" which is always grouped and allows return annotations to format correctly. Fixes: #6986 Change-Id: I6117c642345cdde65a64389bba6057ddd5374427
272 lines
8.9 KiB
Python
272 lines
8.9 KiB
Python
from sqlalchemy import ForeignKey
|
|
from sqlalchemy import Integer
|
|
from sqlalchemy import testing
|
|
from sqlalchemy.orm import attributes
|
|
from sqlalchemy.orm import class_mapper
|
|
from sqlalchemy.orm import exc as orm_exc
|
|
from sqlalchemy.orm import sync
|
|
from sqlalchemy.orm import unitofwork
|
|
from sqlalchemy.testing import assert_raises_message
|
|
from sqlalchemy.testing import eq_
|
|
from sqlalchemy.testing import fixtures
|
|
from sqlalchemy.testing.fixtures import fixture_session
|
|
from sqlalchemy.testing.schema import Column
|
|
from sqlalchemy.testing.schema import Table
|
|
|
|
|
|
class AssertsUOW:
|
|
def _get_test_uow(self, session):
|
|
uow = unitofwork.UOWTransaction(session)
|
|
deleted = set(session._deleted)
|
|
new = set(session._new)
|
|
dirty = set(session._dirty_states).difference(deleted)
|
|
for s in new.union(dirty):
|
|
uow.register_object(s)
|
|
for d in deleted:
|
|
uow.register_object(d, isdelete=True)
|
|
return uow
|
|
|
|
|
|
class SyncTest(
|
|
fixtures.MappedTest, testing.AssertsExecutionResults, AssertsUOW
|
|
):
|
|
@classmethod
|
|
def define_tables(cls, metadata):
|
|
Table(
|
|
"t1",
|
|
metadata,
|
|
Column("id", Integer, primary_key=True),
|
|
Column("foo", Integer),
|
|
)
|
|
Table(
|
|
"t2",
|
|
metadata,
|
|
Column("id", Integer, ForeignKey("t1.id"), primary_key=True),
|
|
Column("t1id", Integer, ForeignKey("t1.id")),
|
|
)
|
|
|
|
@classmethod
|
|
def setup_classes(cls):
|
|
class A(cls.Basic):
|
|
pass
|
|
|
|
class B(cls.Basic):
|
|
pass
|
|
|
|
@classmethod
|
|
def setup_mappers(cls):
|
|
cls.mapper_registry.map_imperatively(cls.classes.A, cls.tables.t1)
|
|
cls.mapper_registry.map_imperatively(cls.classes.B, cls.tables.t2)
|
|
|
|
def _fixture(self):
|
|
A, B = self.classes.A, self.classes.B
|
|
session = fixture_session()
|
|
uowcommit = self._get_test_uow(session)
|
|
a_mapper = class_mapper(A)
|
|
b_mapper = class_mapper(B)
|
|
self.a1 = a1 = A()
|
|
self.b1 = b1 = B()
|
|
uowcommit = self._get_test_uow(session)
|
|
return (
|
|
uowcommit,
|
|
attributes.instance_state(a1),
|
|
attributes.instance_state(b1),
|
|
a_mapper,
|
|
b_mapper,
|
|
)
|
|
|
|
def test_populate(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
a1.obj().id = 7
|
|
assert "id" not in b1.obj().__dict__
|
|
sync.populate(a1, a_mapper, b1, b_mapper, pairs, uowcommit, False)
|
|
eq_(b1.obj().id, 7)
|
|
eq_(b1.obj().__dict__["id"], 7)
|
|
assert ("pk_cascaded", b1, b_mapper.c.id) not in uowcommit.attributes
|
|
|
|
def test_populate_flag_cascaded(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
a1.obj().id = 7
|
|
assert "id" not in b1.obj().__dict__
|
|
sync.populate(a1, a_mapper, b1, b_mapper, pairs, uowcommit, True)
|
|
eq_(b1.obj().id, 7)
|
|
eq_(b1.obj().__dict__["id"], 7)
|
|
eq_(uowcommit.attributes[("pk_cascaded", b1, b_mapper.c.id)], True)
|
|
|
|
def test_populate_unmapped_source(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(b_mapper.c.id, b_mapper.c.id)]
|
|
assert_raises_message(
|
|
orm_exc.UnmappedColumnError,
|
|
"Can't execute sync rule for source column 't2.id'; "
|
|
r"mapper 'Mapper\[A\(t1\)\]' does not map this column.",
|
|
sync.populate,
|
|
a1,
|
|
a_mapper,
|
|
b1,
|
|
b_mapper,
|
|
pairs,
|
|
uowcommit,
|
|
False,
|
|
)
|
|
|
|
def test_populate_unmapped_dest(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(a_mapper.c.id, a_mapper.c.id)]
|
|
assert_raises_message(
|
|
orm_exc.UnmappedColumnError,
|
|
r"Can't execute sync rule for destination "
|
|
r"column 't1.id'; "
|
|
r"mapper 'Mapper\[B\(t2\)\]' does not map this column.",
|
|
sync.populate,
|
|
a1,
|
|
a_mapper,
|
|
b1,
|
|
b_mapper,
|
|
pairs,
|
|
uowcommit,
|
|
False,
|
|
)
|
|
|
|
def test_clear(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(a_mapper.c.id, b_mapper.c.t1id)]
|
|
b1.obj().t1id = 8
|
|
eq_(b1.obj().__dict__["t1id"], 8)
|
|
sync.clear(b1, b_mapper, pairs)
|
|
eq_(b1.obj().__dict__["t1id"], None)
|
|
|
|
def test_clear_pk(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
b1.obj().id = 8
|
|
eq_(b1.obj().__dict__["id"], 8)
|
|
assert_raises_message(
|
|
AssertionError,
|
|
"Dependency rule tried to blank-out primary key "
|
|
"column 't2.id' on instance '<B",
|
|
sync.clear,
|
|
b1,
|
|
b_mapper,
|
|
pairs,
|
|
)
|
|
|
|
def test_clear_unmapped(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(a_mapper.c.id, a_mapper.c.foo)]
|
|
assert_raises_message(
|
|
orm_exc.UnmappedColumnError,
|
|
"Can't execute sync rule for destination "
|
|
r"column 't1.foo'; mapper 'Mapper\[B\(t2\)\]' does not "
|
|
"map this column.",
|
|
sync.clear,
|
|
b1,
|
|
b_mapper,
|
|
pairs,
|
|
)
|
|
|
|
def test_update(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().id = 10
|
|
a1._commit_all(a1.dict)
|
|
a1.obj().id = 12
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
dest = {}
|
|
sync.update(a1, a_mapper, dest, "old_", pairs)
|
|
eq_(dest, {"id": 12, "old_id": 10})
|
|
|
|
def test_update_unmapped(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(b_mapper.c.id, b_mapper.c.id)]
|
|
dest = {}
|
|
assert_raises_message(
|
|
orm_exc.UnmappedColumnError,
|
|
"Can't execute sync rule for source column 't2.id'; "
|
|
r"mapper 'Mapper\[A\(t1\)\]' does not map this column.",
|
|
sync.update,
|
|
a1,
|
|
a_mapper,
|
|
dest,
|
|
"old_",
|
|
pairs,
|
|
)
|
|
|
|
def test_populate_dict(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().id = 10
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
dest = {}
|
|
sync.populate_dict(a1, a_mapper, dest, pairs)
|
|
eq_(dest, {"id": 10})
|
|
|
|
def test_populate_dict_unmapped(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().id = 10
|
|
pairs = [(b_mapper.c.id, b_mapper.c.id)]
|
|
dest = {}
|
|
assert_raises_message(
|
|
orm_exc.UnmappedColumnError,
|
|
"Can't execute sync rule for source column 't2.id'; "
|
|
r"mapper 'Mapper\[A\(t1\)\]' does not map this column.",
|
|
sync.populate_dict,
|
|
a1,
|
|
a_mapper,
|
|
dest,
|
|
pairs,
|
|
)
|
|
|
|
def test_source_modified_unmodified(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().id = 10
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
eq_(sync.source_modified(uowcommit, a1, a_mapper, pairs), False)
|
|
|
|
def test_source_modified_no_pairs(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
eq_(sync.source_modified(uowcommit, a1, a_mapper, []), False)
|
|
|
|
def test_source_modified_modified(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().id = 10
|
|
a1._commit_all(a1.dict)
|
|
a1.obj().id = 12
|
|
pairs = [(a_mapper.c.id, b_mapper.c.id)]
|
|
eq_(sync.source_modified(uowcommit, a1, a_mapper, pairs), True)
|
|
|
|
def test_source_modified_composite(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().foo = 10
|
|
a1._commit_all(a1.dict)
|
|
a1.obj().foo = 12
|
|
pairs = [
|
|
(a_mapper.c.id, b_mapper.c.id),
|
|
(a_mapper.c.foo, b_mapper.c.id),
|
|
]
|
|
eq_(sync.source_modified(uowcommit, a1, a_mapper, pairs), True)
|
|
|
|
def test_source_modified_composite_unmodified(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
a1.obj().foo = 10
|
|
a1._commit_all(a1.dict)
|
|
pairs = [
|
|
(a_mapper.c.id, b_mapper.c.id),
|
|
(a_mapper.c.foo, b_mapper.c.id),
|
|
]
|
|
eq_(sync.source_modified(uowcommit, a1, a_mapper, pairs), False)
|
|
|
|
def test_source_modified_no_unmapped(self):
|
|
uowcommit, a1, b1, a_mapper, b_mapper = self._fixture()
|
|
pairs = [(b_mapper.c.id, b_mapper.c.id)]
|
|
assert_raises_message(
|
|
orm_exc.UnmappedColumnError,
|
|
"Can't execute sync rule for source column 't2.id'; "
|
|
r"mapper 'Mapper\[A\(t1\)\]' does not map this column.",
|
|
sync.source_modified,
|
|
uowcommit,
|
|
a1,
|
|
a_mapper,
|
|
pairs,
|
|
)
|