Skip to content

Commit 5d74e5d

Browse files
authored
Merge pull request #7 from wayfair-contribs/sqlalchemy_1_4_plus_compability
The library adjusted to work with SQLAlchemy 1.4.x
2 parents b5cb2dc + 8a91133 commit 5d74e5d

File tree

6 files changed

+272
-214
lines changed

6 files changed

+272
-214
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ sqlnet.log
2121
/test.py
2222
/.cache/
2323
*.sw[o,p]
24+
.idea
25+
.eggs

lib/sqlalchemy_bulk_lazy_loader.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
from sqlalchemy import util, inspect, Column
2-
from sqlalchemy.orm import properties, attributes, interfaces, strategy_options
1+
from sqlalchemy import Column, inspect, util
2+
from sqlalchemy.orm import RelationshipProperty, attributes, interfaces, strategy_options
33
from sqlalchemy.orm.strategies import LazyLoader
4-
from sqlalchemy.sql.elements import BinaryExpression, BindParameter, BooleanClauseList
54
from sqlalchemy.sql import operators
5+
from sqlalchemy.sql.elements import BinaryExpression, BindParameter, BooleanClauseList
66

77

88
class UnsupportedRelationError(Exception):
99
"""Error to use if a relationship can't be loaded by this lazy loader"""
1010

1111

1212
class BulkLazyLoader(LazyLoader):
13-
1413
def __init__(self, parent, strategy_key):
1514
super(BulkLazyLoader, self).__init__(parent, strategy_key)
1615
criterion, param_keys = self._simple_lazy_clause
@@ -25,7 +24,7 @@ def register_loader(cls):
2524
"""
2625
call this method before defining any mappers to make this loader available with `lazy="bulk"`
2726
"""
28-
decorator = properties.RelationshipProperty.strategy_for(lazy="bulk")
27+
decorator = RelationshipProperty.strategy_for(lazy="bulk")
2928
decorator(cls)
3029

3130
def _get_join_col_from_criterion(self, criterion, reverse=False):
@@ -54,7 +53,6 @@ def _get_similar_unpopulated_models(self, model, session):
5453
This finds all other models of this class in the session that also need to have this relationship lazyloaded
5554
"""
5655
model_class = model.__class__
57-
dict_ = attributes.instance_dict(model)
5856
similar_models = []
5957
for possible_model in session.identity_map.values():
6058
is_same_class = isinstance(possible_model, model_class)
@@ -138,13 +136,16 @@ def _validate_relation(self):
138136
if value is not None or ident is None:
139137
self._unsupported_relation()
140138

141-
def _emit_lazyload(self, session, state, ident_key, passive):
139+
def _emit_lazyload(
140+
self, session, state, ident_key, passive, loadopt, extra_criteria
141+
):
142142
"""
143143
This is the main method from LazyLoader we need to overwrite. Unfortunately I don't think there's
144144
a clean way to add bulk functionality without partially copy/pasting from LazyLoader#_emit_lazyload
145145
"""
146146

147-
q = session.query(self.mapper, self._join_col)._adapt_all_clauses()
147+
q = session.query(self.mapper, self._join_col)
148+
q._compile_options += {"_orm_only_from_obj_alias": False}
148149

149150
# -------------- COPIED/MODIFIED FROM LAZYLOADER -----------------
150151

@@ -168,7 +169,7 @@ def _emit_lazyload(self, session, state, ident_key, passive):
168169
q = q.options(
169170
strategy_options.Load.for_existing_path(
170171
q._current_path[rev.parent]
171-
).lazyload(rev.key)
172+
).lazyload(rev)
172173
)
173174

174175
# ------------ CUSTOM BULK LOGIC --------------------------

setup.cfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# TAKEN FROM SQLALCHEMY
1+
# TAKEN FROM SQLALCHEMY v1.4.25
2+
# https://github.com/sqlalchemy/sqlalchemy/blob/rel_1_4_25/setup.cfg
23

34
[tool:pytest]
4-
addopts= --tb native -v -r fxX --maxfail=25
5+
addopts= --tb native -v -r fxX --maxfail=25 -p no:warnings -p no:logging
56
python_files=test/*test_*.py
67

78
[sqla_testing]

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from setuptools import setup, find_packages
1+
from setuptools import setup
22
# To use a consistent encoding
33
from codecs import open
44
from os import path
@@ -11,7 +11,7 @@
1111

1212
setup(
1313
name='SQLAlchemy-bulk-lazy-loader',
14-
version='0.9.9',
14+
version='0.10.0',
1515
description='A Bulk Lazy Loader for Sqlalchemy that solves the n + 1 loading problem',
1616
long_description=long_description,
1717
url='https://github.com/operator/sqlalchemy_bulk_lazy_loader',
@@ -25,9 +25,9 @@
2525
'License :: OSI Approved :: MIT License',
2626
'Programming Language :: Python :: 3',
2727
],
28-
tests_require=['pytest >= 2.5.2', 'mock', 'pytest-xdist'],
28+
tests_require=['pytest >= 6.2.3', 'mock', 'pytest-xdist'],
2929
keywords='sqlalchemy orm lazyload joinedload subqueryload',
3030
py_modules=['sqlalchemy_bulk_lazy_loader'],
3131
package_dir={'': 'lib'},
32-
install_requires=['SQLAlchemy>=1.0'],
32+
install_requires=["SQLAlchemy~=1.4"],
3333
)

test/_fixtures.py

Lines changed: 113 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
1-
from sqlalchemy import MetaData, Integer, String, ForeignKey, Text
2-
from sqlalchemy import util, desc
3-
from sqlalchemy.testing.schema import Table
4-
from sqlalchemy.testing.schema import Column
5-
from sqlalchemy.orm import attributes, mapper, relationship, backref, configure_mappers, create_session
1+
from sqlalchemy import ForeignKey, Integer, String, Text, desc
2+
from sqlalchemy.orm import backref, configure_mappers, mapper, relationship
63
from sqlalchemy.testing import fixtures
7-
from sqlalchemy.ext.associationproxy import association_proxy
4+
from sqlalchemy.testing.schema import Column, Table
85

96
__all__ = ()
107

118

129
class FixtureTest(fixtures.MappedTest):
13-
"""A MappedTest pre-configured with a common set of fixtures.
10+
"""A MappedTest pre-configured with a common set of fixtures."""
1411

15-
"""
16-
17-
run_define_tables = 'once'
18-
run_setup_classes = 'once'
19-
run_setup_mappers = 'each'
20-
run_inserts = 'each'
21-
run_deletes = 'each'
12+
run_define_tables = "once"
13+
run_setup_classes = "once"
14+
run_setup_mappers = "each"
15+
run_inserts = "each"
16+
run_deletes = "each"
2217

2318
@classmethod
2419
def setup_classes(cls):
@@ -37,114 +32,138 @@ class Address(Base):
3732
class Thing(Base):
3833
pass
3934

40-
4135
@classmethod
4236
def setup_mappers(cls):
4337
User, users = cls.classes.User, cls.tables.users
4438
UserInfo, user_infos = cls.classes.UserInfo, cls.tables.user_infos
4539
Address, addresses = cls.classes.Address, cls.tables.addresses
4640
Thing, things = cls.classes.Thing, cls.tables.things
4741

48-
mapper(User, users, properties={
49-
'addresses': relationship(
50-
Address,
51-
backref=backref('user', lazy="bulk"),
52-
lazy="bulk",
53-
order_by=[desc(addresses.c.email_address)]
54-
),
55-
'children': relationship(User, backref=backref('parent', remote_side=[users.c.id], lazy="bulk"), lazy="bulk"),
56-
'user_info': relationship(UserInfo, lazy="bulk", backref=backref('user', lazy="bulk"), uselist=False),
57-
'things': relationship(Thing, secondary=cls.tables.user_to_things, lazy="bulk"),
58-
})
42+
mapper(
43+
User,
44+
users,
45+
properties={
46+
"addresses": relationship(
47+
Address,
48+
backref=backref("user", lazy="bulk"),
49+
lazy="bulk",
50+
order_by=[desc(addresses.c.email_address)],
51+
),
52+
"children": relationship(
53+
User,
54+
backref=backref("parent", remote_side=[users.c.id], lazy="bulk"),
55+
lazy="bulk",
56+
),
57+
"user_info": relationship(
58+
UserInfo,
59+
lazy="bulk",
60+
backref=backref("user", lazy="bulk"),
61+
uselist=False,
62+
),
63+
"things": relationship(
64+
Thing, secondary=cls.tables.user_to_things, lazy="bulk"
65+
),
66+
},
67+
)
5968
mapper(Address, addresses)
6069
mapper(UserInfo, user_infos)
61-
mapper(Thing, things, properties={
62-
'users': relationship(User, secondary=cls.tables.user_to_things, lazy="bulk"),
63-
})
70+
mapper(
71+
Thing,
72+
things,
73+
properties={
74+
"users": relationship(
75+
User,
76+
secondary=cls.tables.user_to_things,
77+
lazy="bulk",
78+
overlaps="things",
79+
),
80+
},
81+
)
6482

6583
configure_mappers()
6684

6785
@classmethod
6886
def define_tables(cls, metadata):
69-
Table('users', metadata,
70-
Column('id', Integer, primary_key=True,
71-
test_needs_autoincrement=True),
72-
Column('name', String(30), nullable=False),
73-
Column('parent_id', None, ForeignKey('users.id')),
74-
test_needs_acid=True,
75-
test_needs_fk=True)
76-
77-
Table('user_infos', metadata,
78-
Column('id', Integer, primary_key=True,
79-
test_needs_autoincrement=True),
80-
Column('details', Text),
81-
Column('user_id', None, ForeignKey('users.id')),
82-
test_needs_acid=True,
83-
test_needs_fk=True)
84-
85-
Table('addresses', metadata,
86-
Column('id', Integer, primary_key=True,
87-
test_needs_autoincrement=True),
88-
Column('user_id', None, ForeignKey('users.id')),
89-
Column('email_address', String(50), nullable=False),
90-
test_needs_acid=True,
91-
test_needs_fk=True)
92-
93-
Table('things', metadata,
94-
Column('id', Integer, primary_key=True,
95-
test_needs_autoincrement=True),
96-
Column('name', String(30), nullable=False),
97-
test_needs_acid=True,
98-
test_needs_fk=True)
99-
100-
Table('user_to_things', metadata,
101-
Column('user_id', None, ForeignKey('users.id')),
102-
Column('thing_id', None, ForeignKey('things.id')),
103-
test_needs_acid=True,
104-
test_needs_fk=True)
87+
Table(
88+
"users",
89+
metadata,
90+
Column("id", Integer, primary_key=True, test_needs_autoincrement=True),
91+
Column("name", String(30), nullable=False),
92+
Column("parent_id", None, ForeignKey("users.id")),
93+
test_needs_acid=True,
94+
test_needs_fk=True,
95+
)
10596

97+
Table(
98+
"user_infos",
99+
metadata,
100+
Column("id", Integer, primary_key=True, test_needs_autoincrement=True),
101+
Column("details", Text),
102+
Column("user_id", None, ForeignKey("users.id")),
103+
test_needs_acid=True,
104+
test_needs_fk=True,
105+
)
106+
107+
Table(
108+
"addresses",
109+
metadata,
110+
Column("id", Integer, primary_key=True, test_needs_autoincrement=True),
111+
Column("user_id", None, ForeignKey("users.id")),
112+
Column("email_address", String(50), nullable=False),
113+
test_needs_acid=True,
114+
test_needs_fk=True,
115+
)
116+
117+
Table(
118+
"things",
119+
metadata,
120+
Column("id", Integer, primary_key=True, test_needs_autoincrement=True),
121+
Column("name", String(30), nullable=False),
122+
test_needs_acid=True,
123+
test_needs_fk=True,
124+
)
125+
126+
Table(
127+
"user_to_things",
128+
metadata,
129+
Column("user_id", None, ForeignKey("users.id")),
130+
Column("thing_id", None, ForeignKey("things.id")),
131+
test_needs_acid=True,
132+
test_needs_fk=True,
133+
)
106134

107135
@classmethod
108136
def fixtures(cls):
109-
return dict(
110-
users=(
111-
('id', 'name', 'parent_id'),
112-
(7, 'jack', None),
113-
(8, 'jack jr', 7),
114-
(9, 'fred', 7),
115-
(10, 'jack jr jr', 8),
137+
return {
138+
"users": (
139+
("id", "name", "parent_id"),
140+
(7, "jack", None),
141+
(8, "jack jr", 7),
142+
(9, "fred", 7),
143+
(10, "jack jr jr", 8),
116144
),
117-
118-
user_infos=(
119-
('id', 'user_id', 'details'),
120-
(1, 7, 'is cool'),
121-
(2, 8, 'is not cool'),
122-
(3, 10, 'is moderately cool'),
145+
"user_infos": (
146+
("id", "user_id", "details"),
147+
(1, 7, "is cool"),
148+
(2, 8, "is not cool"),
149+
(3, 10, "is moderately cool"),
123150
),
124-
125-
addresses=(
126-
('id', 'user_id', 'email_address'),
151+
"addresses": (
152+
("id", "user_id", "email_address"),
127153
(1, 7, "[email protected]"),
128154
(2, 8, "[email protected]"),
129155
(3, 8, "[email protected]"),
130156
(4, 8, "[email protected]"),
131-
157+
(5, 9, "[email protected]"),
132158
),
133-
134-
things=(
135-
('id', 'name'),
136-
(1, 'dog'),
137-
(2, 'lamp'),
138-
(3, 'chair'),
139-
),
140-
141-
user_to_things=(
142-
('user_id', 'thing_id'),
159+
"things": (("id", "name"), (1, "dog"), (2, "lamp"), (3, "chair")),
160+
"user_to_things": (
161+
("user_id", "thing_id"),
143162
(7, 1),
144-
(8, 1), # include a duplicate intentionally
145163
(8, 1),
164+
(8, 1), # include a duplicate intentionally
146165
(10, 2),
147166
(9, 2),
148167
(10, 3),
149168
),
150-
)
169+
}

0 commit comments

Comments
 (0)