Skip to content

Commit 70fddbe

Browse files
authored
Merge pull request #282 from fantix/t281-none-as-none
Fixes #281, added none_as_none transitive feature
2 parents abfba20 + ace5ef2 commit 70fddbe

File tree

4 files changed

+66
-15
lines changed

4 files changed

+66
-15
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ exclude_lines =
1212
def __repr__
1313
raise NotImplementedError
1414
except ImportError
15+
if gino.__version__ >= '0.8.0':

gino/crud.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ def distinct(cls, *columns):
678678
"""
679679
return cls.load().distinct(*columns)
680680

681+
@classmethod
682+
def none_as_none(cls, enabled=True):
683+
return cls.load().none_as_none(enabled)
684+
681685
@classmethod
682686
def alias(cls, *args, **kwargs):
683687
"""

gino/loader.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import warnings
2+
13
from sqlalchemy import select
24
from sqlalchemy.schema import Column
35

@@ -46,6 +48,9 @@ def __getattr__(self, item):
4648
return getattr(self.query, item)
4749

4850

51+
_none = object()
52+
53+
4954
class ModelLoader(Loader):
5055
def __init__(self, model, *column_names, **extras):
5156
self.model = model
@@ -57,12 +62,20 @@ def __init__(self, model, *column_names, **extras):
5762
self.extras = dict((key, self.get(value))
5863
for key, value in extras.items())
5964
self.on_clause = None
60-
61-
def _do_load(self, row):
65+
self._none_as_none = None
66+
67+
def _do_load(self, row, *, none_as_none=None):
68+
if none_as_none is None:
69+
none_as_none = self._none_as_none
70+
if none_as_none is None:
71+
warnings.warn(
72+
'The none_as_none feature will be enabled by default in 0.8',
73+
DeprecationWarning)
74+
values = dict((c.name, row[c]) for c in self.columns if c in row)
75+
if none_as_none and all((v is None) for v in values.values()):
76+
return None
6277
rv = self.model()
63-
for c in self.columns:
64-
if c in row:
65-
rv.__values__[c.name] = row[c]
78+
rv.__values__.update(values)
6679
return rv
6780

6881
def do_load(self, row, context):
@@ -72,22 +85,23 @@ def do_load(self, row, context):
7285
context = {}
7386
ctx = context.setdefault(self._distinct, {})
7487
key = tuple(row[col] for col in self._distinct)
75-
if key == (None,) * len(key):
76-
return None, None
77-
rv = ctx.get(key)
78-
if rv is None:
79-
rv = self._do_load(row)
88+
rv = ctx.get(key, _none)
89+
if rv is _none:
90+
rv = self._do_load(row, none_as_none=True)
8091
ctx[key] = rv
8192
else:
8293
distinct = False
8394
else:
8495
rv = self._do_load(row)
8596

86-
for key, value in self.extras.items():
87-
value, distinct_ = value.do_load(row, context)
88-
if distinct_ is not None:
89-
setattr(rv, key, value)
90-
return rv, distinct
97+
if rv is None:
98+
return None, None
99+
else:
100+
for key, value in self.extras.items():
101+
value, distinct_ = value.do_load(row, context)
102+
if distinct_ is not None:
103+
setattr(rv, key, value)
104+
return rv, distinct
91105

92106
def get_columns(self):
93107
yield from self.columns
@@ -118,6 +132,14 @@ def distinct(self, *columns):
118132
self._distinct = columns
119133
return self
120134

135+
def none_as_none(self, enabled=True):
136+
if not enabled:
137+
warnings.warn(
138+
'The none_as_none feature will be always enabled in 0.9',
139+
PendingDeprecationWarning)
140+
self._none_as_none = enabled
141+
return self
142+
121143

122144
class AliasLoader(ModelLoader):
123145
def __init__(self, alias, *column_names, **extras):

tests/test_loader.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,27 @@ async def test_tuple_loader_279(user):
238238
assert len(row) == 2
239239
async for row in query.gino.load(TupleLoader((User, Team))).iterate():
240240
assert len(row) == 2
241+
242+
243+
async def test_none_as_none_281(user):
244+
import gino
245+
246+
if gino.__version__ < '0.9':
247+
query = Team.outerjoin(User).select()
248+
loader = Team, User.none_as_none()
249+
assert any(row[1] is None
250+
for row in await query.gino.load(loader).all())
251+
252+
loader = Team.distinct(Team.id).load(add_member=User.none_as_none())
253+
assert any(not team.members
254+
for team in await query.gino.load(loader).all())
255+
256+
if gino.__version__ >= '0.8.0':
257+
query = Team.outerjoin(User).select()
258+
loader = Team, User
259+
assert any(row[1] is None
260+
for row in await query.gino.load(loader).all())
261+
262+
loader = Team.distinct(Team.id).load(add_member=User)
263+
assert any(not team.members
264+
for team in await query.gino.load(loader).all())

0 commit comments

Comments
 (0)