Skip to content

Commit 7b744fa

Browse files
authored
Merge pull request #729 from wwwjfy/t717_empty_model
fix #717, return empty objects instead of None
2 parents 70e4bc1 + bbfb0ec commit 7b744fa

File tree

4 files changed

+37
-38
lines changed

4 files changed

+37
-38
lines changed

HISTORY.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ GINO 1.1
1111
* Added baked query feature (#478 #659 #667)
1212
* Added ``Query.gino.execution_options`` shortcut (#659)
1313
* Added ``@db.declared_attr(with_table=True)`` (#659)
14+
* [Breaking] empty object instead of ``None`` being returned for objects with values of all selected columns are None (#729)
1415

1516

1617
GINO 1.0

mysql_tests/test_loader.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -368,23 +368,12 @@ async def test_tuple_loader_279(user):
368368

369369

370370
async def test_none_as_none_281(user):
371-
import gino
371+
query = Team.outerjoin(User).select()
372+
loader = Team, User
373+
assert any(row[1].id is None for row in await query.gino.load(loader).all())
372374

373-
if gino.__version__ < "0.9":
374-
query = Team.outerjoin(User).select()
375-
loader = Team, User.none_as_none()
376-
assert any(row[1] is None for row in await query.gino.load(loader).all())
377-
378-
loader = Team.distinct(Team.id).load(add_member=User.none_as_none())
379-
assert any(not team.members for team in await query.gino.load(loader).all())
380-
381-
if gino.__version__ >= "0.8.0":
382-
query = Team.outerjoin(User).select()
383-
loader = Team, User
384-
assert any(row[1] is None for row in await query.gino.load(loader).all())
385-
386-
loader = Team.distinct(Team.id).load(add_member=User)
387-
assert any(not team.members for team in await query.gino.load(loader).all())
375+
loader = Team.distinct(Team.id).load(add_member=User)
376+
assert any(not team.members for team in await query.gino.load(loader).all())
388377

389378

390379
async def test_model_in_query(user):
@@ -400,3 +389,10 @@ async def test_model_in_query(user):
400389
assert users[0] != user
401390
assert users[0].id == user.id
402391
assert users[0].nickname == user.nickname
392+
393+
394+
async def test_empty_model(user):
395+
u = await User.query.with_only_columns([User.realname]).gino.first()
396+
assert u is not None
397+
assert u.id is None
398+
assert u.realname is None

src/gino/loader.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def __getattr__(self, item):
123123

124124

125125
_none = object()
126+
_none_as_none = object()
126127

127128

128129
def _get_column(model, column_or_name) -> Column:
@@ -212,9 +213,11 @@ def __init__(self, model, *columns, **extras):
212213
self.extras = dict((key, self.get(value)) for key, value in extras.items())
213214
self.on_clause = None
214215

215-
def _do_load(self, row):
216+
def _do_load(self, row, none_as_none):
217+
# none_as_none indicates that in the case of every column of the object is
218+
# None, whether a None or empty instance of the model should be returned.
216219
values = dict((c.name, row[c]) for c in self.columns if c in row)
217-
if all((v is None) for v in values.values()):
220+
if none_as_none and all((v is None) for v in values.values()):
218221
return None
219222
rv = self.model()
220223
for c in self.columns:
@@ -233,26 +236,29 @@ def do_load(self, row, context):
233236
result is distinct.
234237
"""
235238

239+
if context is None:
240+
context = {}
236241
distinct = True
237242
if self._distinct:
238-
if context is None:
239-
context = {}
240243
ctx = context.setdefault(self._distinct, {})
241244
key = tuple(row[col] for col in self._distinct)
242245
rv = ctx.get(key, _none)
243246
if rv is _none:
244-
rv = self._do_load(row)
247+
rv = self._do_load(row, context.get(_none_as_none, False))
245248
ctx[key] = rv
246249
else:
247250
distinct = False
248251
else:
249-
rv = self._do_load(row)
252+
rv = self._do_load(row, context.get(_none_as_none, False))
250253

251254
if rv is None:
252255
return None, None
253256
else:
254257
for key, value in self.extras.items():
258+
context.setdefault(_none_as_none, True)
255259
value, distinct_ = value.do_load(row, context)
260+
# _none_as_none should not be propagated to parents
261+
context.pop(_none_as_none, 0)
256262
if distinct_ is None:
257263
continue
258264

tests/test_loader.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -368,23 +368,12 @@ async def test_tuple_loader_279(user):
368368

369369

370370
async def test_none_as_none_281(user):
371-
import gino
371+
query = Team.outerjoin(User).select()
372+
loader = Team, User
373+
assert any(row[1].id is None for row in await query.gino.load(loader).all())
372374

373-
if gino.__version__ < "0.9":
374-
query = Team.outerjoin(User).select()
375-
loader = Team, User.none_as_none()
376-
assert any(row[1] is None for row in await query.gino.load(loader).all())
377-
378-
loader = Team.distinct(Team.id).load(add_member=User.none_as_none())
379-
assert any(not team.members for team in await query.gino.load(loader).all())
380-
381-
if gino.__version__ >= "0.8.0":
382-
query = Team.outerjoin(User).select()
383-
loader = Team, User
384-
assert any(row[1] is None for row in await query.gino.load(loader).all())
385-
386-
loader = Team.distinct(Team.id).load(add_member=User)
387-
assert any(not team.members for team in await query.gino.load(loader).all())
375+
loader = Team.distinct(Team.id).load(add_member=User)
376+
assert any(not team.members for team in await query.gino.load(loader).all())
388377

389378

390379
async def test_model_in_query(user):
@@ -400,3 +389,10 @@ async def test_model_in_query(user):
400389
assert users[0] != user
401390
assert users[0].id == user.id
402391
assert users[0].nickname == user.nickname
392+
393+
394+
async def test_empty_model(user):
395+
u = await User.query.with_only_columns([User.realname]).gino.first()
396+
assert u is not None
397+
assert u.id is None
398+
assert u.realname is None

0 commit comments

Comments
 (0)