On Thursday 28 February 2013, Aymeric Augustin wrote:
>
> I would support renaming them to first / last through a deprecation path.
> The new aliases would be available immediately, the old ones would be
> removed in two versions.
>
+1
> And while we're there, I suggest to rely on the existing "ordering" meta
> attribute and to remove "get_latest_by". I suspect that in many cases
> these two attributes have the same value, and you can specify an explicit
> ordering otherwise.
Consistent with the above, +1
and as far as Wim's original question is concerned:
> Which style do you prefer?
>
> .filter(last_name__startswith='b').order_by('last_name').first() # clear
> and long
> .first(last_name__startswith='b').order_by('last_name') # first method
> has filter syntax.
> .filter(last_name__startswith='b').first('last_name') # first method has
> order_by syntax.
ordering is given by position, filtering by keyword arguments -- why not
support both?
def first (self, *ordering, **filtering):
...
My only concern is one that Anssi raised -- the check for multiple objects is
discarded, and there is no convenient way to get 0-or-1 objects (which is the
semantics of the try-get-except-DoesNotExist-return-None pattern this is
designed to replace). I don't think it has been given enough attention in the
discussion so far.
One option -- with my suggested syntax -- is that if no ordering is given,
then it means only one is expected (and, say, None could be used to say "use
default ordering"). I suspect, though, that this might be a confusing (and
conflated) interface.
Or maybe it can be saved by saying you must use one or the other but not both;
then it's "overloaded", but nothing really surprising happens. This way, None
could be used to say "No ordering -- there should be only one", which is even
more intuitive.
We get (semantically):
qset.first('last_name') ==>
qset.order_by('last_name')[0] if qset.exists() else None
qset.first(None) ==>
qset.get() if qset,exists() else None
qset.first(last_name__startswith='b') ==>
qset.filter(last_name__startswith='b').first(None)
qset.first("last_name", last_name__startswith='b') ==>
raise TypeError("first() takes either all positional args or all keywords")
qset.first() ==>
qset.first(qset.model.ordering)
Note that with this suggestion:
qset.filter(a=b).first(c)
is not equivalent to
qset.order_by(c).filter(a=b)
Because the latter checks for multiple objects and the former doesn't; this.
IMO, justifies raising a type-error when trying to use both.
Shai.