From: Jason Kirtland Date: Wed, 14 May 2008 20:44:16 +0000 (+0000) Subject: Query.one() raises either NoResultFound or MultipleResultsFound, [ticket:1034] X-Git-Tag: rel_0_5beta1~80 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=81b1df8fe1b8cc66f61a430ca50bfcb7362ab9c1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Query.one() raises either NoResultFound or MultipleResultsFound, [ticket:1034] --- diff --git a/doc/build/content/ormtutorial.txt b/doc/build/content/ormtutorial.txt index b9b598e64e..411bf64ca1 100644 --- a/doc/build/content/ormtutorial.txt +++ b/doc/build/content/ormtutorial.txt @@ -460,7 +460,19 @@ The `all()`, `one()`, and `first()` methods of `Query` immediately issue SQL and WHERE users.name LIKE ? ORDER BY users.id LIMIT 2 OFFSET 0 ['%ed'] - {stop}Multiple rows returned for one() + {stop}Multiple rows were found for one() + + {python} + {sql}>>> try: + ... user = query.filter(User.id == 99).one() + ... except Exception, e: + ... print e + SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password + FROM users + WHERE users.name LIKE ? AND users.id = ? ORDER BY users.id + LIMIT 2 OFFSET 0 + ['%ed', 99] + {stop}No row was found for one() ### Using Literal SQL {@naqme=literal} diff --git a/lib/sqlalchemy/orm/exc.py b/lib/sqlalchemy/orm/exc.py index 2d1d2b108e..ea7efd3fb8 100644 --- a/lib/sqlalchemy/orm/exc.py +++ b/lib/sqlalchemy/orm/exc.py @@ -24,6 +24,11 @@ class ObjectDeletedError(sa_exc.InvalidRequestError): class UnmappedColumnError(sa_exc.InvalidRequestError): """Mapping operation was requested on an unknown column.""" +class NoResultFound(sa_exc.InvalidRequestError): + """A database result was required but none was found.""" + +class MultipleResultsFound(sa_exc.InvalidRequestError): + """A single database result was required but more than one were found.""" # Legacy compat until 0.6. sa_exc.ConcurrentModificationError = ConcurrentModificationError diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 608f3f7344..47d00964d4 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1045,22 +1045,29 @@ class Query(object): return None def one(self): - """Return the first result, raising an exception unless exactly one row exists. + """Return exactly one result or raise an exception. + + Raises ``sqlalchemy.orm.NoResultError`` if the query selects no rows. + Raisees ``sqlalchemy.orm.MultipleResultsError`` if multiple rows are + selected. This results in an execution of the underlying query. """ if self._statement: - raise exceptions.InvalidRequestError("one() not available when from_statement() is used; use `first()` instead.") - + raise exceptions.InvalidRequestError( + "one() not available when from_statement() is used; " + "use `first()` instead.") + ret = list(self[0:2]) if len(ret) == 1: return ret[0] elif len(ret) == 0: - raise sa_exc.InvalidRequestError('No rows returned for one()') + raise orm_exc.NoResultFound("No row was found for one()") else: - raise sa_exc.InvalidRequestError('Multiple rows returned for one()') + raise orm_exc.MultipleResultsFound( + "Multiple rows were found for one()") def __iter__(self): context = self._compile_context() diff --git a/test/orm/query.py b/test/orm/query.py index eb7a0f3d37..d2550427c2 100644 --- a/test/orm/query.py +++ b/test/orm/query.py @@ -10,6 +10,9 @@ from testlib import * from orm import _base from testlib import engines from testlib.fixtures import * +from testlib import sa, testing +from testlib.testing import eq_ +from orm import _fixtures from sqlalchemy.orm.util import join, outerjoin, with_parent @@ -365,7 +368,7 @@ class FilterTest(QueryTest): assert [] == create_session().query(User)[0:0] - def test_onefilter(self): + def test_one_filter(self): assert [User(id=8), User(id=9)] == create_session().query(User).filter(User.name.endswith('ed')).all() def test_contains(self): @@ -1436,6 +1439,58 @@ class MixedEntitiesTest(QueryTest): sess.clear() +class ImmediateTest(_fixtures.FixtureTest): + run_inserts = 'once' + run_deletes = None + + @testing.resolve_artifact_names + def setup_mappers(self): + mapper(Address, addresses) + + mapper(User, users, properties=dict( + addresses=relation(Address))) + + @testing.resolve_artifact_names + def test_one(self): + sess = create_session() + + self.assertRaises(sa.orm.exc.NoResultFound, + sess.query(User).filter(User.id == 99).one) + + eq_(sess.query(User).filter(User.id == 7).one().id, 7) + + self.assertRaises(sa.orm.exc.MultipleResultsFound, + sess.query(User).one) + + self.assertRaises( + sa.orm.exc.NoResultFound, + sess.query(User.id, User.name).filter(User.id == 99).one) + + eq_(sess.query(User.id, User.name).filter(User.id == 7).one(), + (7, 'jack')) + + self.assertRaises(sa.orm.exc.MultipleResultsFound, + sess.query(User.id, User.name).one) + + self.assertRaises(sa.orm.exc.NoResultFound, + (sess.query(User, Address). + join(User.addresses). + filter(Address.id == 99)).one) + + eq_((sess.query(User, Address). + join(User.addresses). + filter(Address.id == 4)).one(), + (User(id=8), Address(id=4))) + + self.assertRaises(sa.orm.exc.MultipleResultsFound, + sess.query(User, Address).join(User.addresses).one) + + + @testing.future + def test_getslice(self): + assert False + + class SelectFromTest(QueryTest): keep_mappers = False