Issue: SelectQuery.first()
Author: maebert
State: closed
Created: 2013-01-25 02:17:25

Return the first result of a Query or `None` if the result doesn't contain any row. This is analogous to SQLAlchemy and python-mongodb and eases portability for third-party modules wanting to support peewee.


This is similar to what the `.get()` method does [link](http://peewee.readthedocs.org/en/latest/peewee/api.html#Model.get) / [src](https://github.com/coleifer/peewee/blob/master/peewee.py#L1328) except instead of raising an exception it returns `None`.  I don't see how this is any better -- i assume you still have an if statement somewhere to see if you have a model or `None`.


Sorry Charles, I wasn't very clear about this. This is not any better or worse than `.get()`, it's just an alternative that improves compatibility to and portability from SQLAlchemy based modules such as `flask-security`. So basically, when porting code that works with SQLAlchemy to work with peewee, whenever the code sats

``` python
return User.query.filter_by(name="admin").first()
```

the Peewee equivalent is 

``` python
try:
    return User.filter(name="admin").get()
except User.DoesNotExist:
    return None
```

or rather, since `filter` is deprecated in 2.0,

``` python
try:
    return User.filter(User.name == "admin").get()
except User.DoesNotExist:
    return None
```

worsening code ports in cases like this:

``` python
# code excerpt from flask-security working with SQLAlchemy
def find_role(self, **kwargs):
    return self.user_role.query.filter_by(**kwargs).first()
```

Introducing short-hands like `.first()` (and maybe not deprecating the for shorter queries incredibly useful `filter` syntax) simply improves portability and thus greater support for peewee.


OK, you convinced me on `first`: 486a08ce6c1592653fba196cee8f7cb523d28d19


Cool, thanks a lot! After a quick and dirty test though I think the query is slightly faster if limiting to one result before executing (doing `res = self.limit(1).execute()`). My [test setup](https://gist.github.com/4638612) was a local SQLite db, 10000 rows, query matched 100 rows, I timed creating one object after the DB was set up and connected. It's not big, about 10ns per query (based on 1mio repetitions), but that's using a local DB already, so for requests over a network it might yield a little improvement.


Yes .first() is not implemented as efficiently as .get() -- but that is on purpose.  It's name is .first(), implying there might be a second.  Additionally, multiple calls to first() will not result in the query being evaluated multiple times, which is _not_ the case with .get().  If you want, you can always put the limit before the call to first:

``` python

Something.select().limit(1).first()
```

or just write a wrapper

``` python

def first(query):
    return query.limit(1).first()
```

As to filter(), yes I agree it is easy to use but it is not flexible.  We have to add hacks like django's double-underscore to specify operations other than `eq`, and trying to biuld a query tree results in hacks like `DQ`.

For a bit more of my rationale, see http://charlesleifer.com/blog/shortcomings-in-the-django-orm-and-a-look-at-peewee-a-lightweight-alternative/


Fair call. Thanks for implementing this!