Add support for get_or_none?

671 views
Skip to first unread message

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 13, 2014, 12:45:31 PM3/13/14
to django-d...@googlegroups.com
Seems this issue was brought up several years ago, though the thread was later hijacked for other functionality and get_or_none fizzled out.

In Django 1.6 there were convenience methods added for .first(), for the same principle of not having to catch an IndexError (or in this case, a DoesNotExist error);

This seems to be wanted by several users, as seen here;

Seems to be quite an easy fix, just needs a proper patch.

Any thoughts?

Cal

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 13, 2014, 1:02:15 PM3/13/14
to django-d...@googlegroups.com
I should just mention, the accepted answer in the SO thread is not the fix I'm proposing.

I'm thinking something like;

Cal

Shai Berger

unread,
Mar 13, 2014, 1:05:08 PM3/13/14
to django-d...@googlegroups.com

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 13, 2014, 1:26:21 PM3/13/14
to django-d...@googlegroups.com
Just read through all those threads/tickets, here's my conclusion.

#2659 was rejected 8 years ago [1] on the basis that it's a "feature creep", and that it "doesn't offer anything revolutionary". However the same could be said for .first() and .last(), yet those were accepted.

#11352 was rejected by luke plant 2 years ago [4] based on the suggested implementation in that ticket, which is not the same implementation as what I'm proposing this time round. The design of `get_object_or_none` being added into shortcuts is not a good approach, and was right to be rejected.

#17546 was rejected 2 years ago [3] on the basis that #2659 and #11352 were rejected, both of which I've addressed above.

First argument - `first()` and `.last()` have been added, yet the principle behind why they were added is the same as `.get_or_none()`. 
Second argument - The implementation being suggested in this thread is not the same as what has been suggested in the three rejected tickets.
Third argument - Thread [2] had mostly positive feedback, but there was no BDFL decision specifically on `get_or_none`.

If I'm missing something here, please let me know.

Cal




Shai.

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/201403131905.09028.shai%40platonix.com.
For more options, visit https://groups.google.com/d/optout.

Sean Bleier

unread,
Mar 13, 2014, 1:41:43 PM3/13/14
to django-d...@googlegroups.com
I like the implementation referenced in the SO thread, but I would just point out that `.get_or_none()` should accept both *args and **kwargs. `Q` objects can be passed in as arguments to `.get()` and `.filter()`, so it's only natural to allow that for `.get_or_none()`.


Cal Leeming [Simplicity Media Ltd]

unread,
Mar 13, 2014, 1:48:16 PM3/13/14
to django-d...@googlegroups.com
Sorry yes, you're quite right.

To be clear - the proposed solution in this thread is to make `.get_or_none()` work exactly the same as `.get()`, with the only exception that None is returned in place of DoesNotExist. All other logic (args, kwargs, exceptions etc) stay exactly the same.

Cal


Josh Smeaton

unread,
Mar 13, 2014, 5:34:18 PM3/13/14
to django-d...@googlegroups.com
+1 on get_or_none. It seems to be a pattern that comes up quite a lot in user code, and I know I've had use for it lots of times. Cal, are you thinking of having a loose function get_or_none(qs, args, kwargs), or implementing it directly on the manager? I think it'd make sense to implement on the manager, but the name doesn't "fit" with the other methods available, so perhaps it'd be better to match it up with get_or_create as a simple shortcut.

Josh

dude

unread,
Mar 13, 2014, 7:48:33 PM3/13/14
to django-d...@googlegroups.com
+1, cool idea!

everybody write a lot of code for try/except block. this feature can help shorten code.

14 марта 2014 г., в 4:34, Josh Smeaton <josh.s...@gmail.com> написал(а):

Shai Berger

unread,
Mar 14, 2014, 8:41:43 AM3/14/14
to django-d...@googlegroups.com
On Thursday 13 March 2014 14:34:18 Josh Smeaton wrote:
> +1 on get_or_none. It seems to be a pattern that comes up quite a lot in
> user code, and I know I've had use for it lots of times.

Since 1.6, you should just be using first(). Compared to the
try-get-except-DoesNotExist-return-None pattern, it is only missing the
validation that there is, indeed, at most one objet matching the criteria. In
a large majority of the cases I've seen, that validation is nice to have if it
comes for free, but not worth a special effort, because it is taken care of by
database constraints (in a significant part, the criteria just select by pk).

I would be happy to have a validating first(), but the current proposition, as
far as I understand, just overlaps existing API almost completely. I wouldn't
veto it, but a strong -0 from me.

Shai.

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 14, 2014, 11:01:52 AM3/14/14
to django-d...@googlegroups.com
At present, I'd propose implementing it on the manager - the same as `.get()`

I would agree the naming convention does seem out of place, but at the same time, no suitable alternative jumps to mind straight away (any suggestions??)

Cal

Josh Smeaton

unread,
Mar 14, 2014, 11:08:50 AM3/14/14
to django-d...@googlegroups.com
Shai has changed my mind. Most uses of the get or none pattern that I've used could be replaced by .first(), since it's unlikely there will be multiple objects with the kind of query you'd be using with a get. I really dislike the get_or_create shortcut syntax, and I don't think a good name for get_or_none exists for the manager method.

.get(or=None) (of some description) would be my preference, but even that is ugly and confuses the existing API with "special" keywords that aren't actually a filter.

So, I take back my +1.

Josh

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 14, 2014, 11:09:56 AM3/14/14
to django-d...@googlegroups.com
On Fri, Mar 14, 2014 at 12:41 PM, Shai Berger <sh...@platonix.com> wrote:
On Thursday 13 March 2014 14:34:18 Josh Smeaton wrote:
> +1 on get_or_none. It seems to be a pattern that comes up quite a lot in
> user code, and I know I've had use for it lots of times.

Since 1.6, you should just be using first(). Compared to the
try-get-except-DoesNotExist-return-None pattern, it is only missing the
validation that there is, indeed, at most one objet matching the criteria. In
a large majority of the cases I've seen, that validation is nice to have if it
comes for free, but not worth a special effort, because it is taken care of by
database constraints (in a significant part, the criteria just select by pk).

Picking the first item available (on the assumption that it is the only item, without doing an assertion check) can lead to unexpected behaviour in an application. On that basis, I would argue there is a valid use case separate from `first()`
 

I would be happy to have a validating first(),

Adding `MultipleObjectsReturned()` validation into `first()` probably isn't going to fly for two reasons. The concept of first()/last() would break if you could not have multiple objects, and changing would break backwards compatibility significantly. As such I would be -1 for changing `first()`
 
but the current proposition, as
far as I understand, just overlaps existing API almost completely. I wouldn't
veto it, but a strong -0 from me.

Shai.

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 14, 2014, 11:15:19 AM3/14/14
to django-d...@googlegroups.com
On Fri, Mar 14, 2014 at 3:08 PM, Josh Smeaton <josh.s...@gmail.com> wrote:
Shai has changed my mind. Most uses of the get or none pattern that I've used could be replaced by .first(), since it's unlikely there will be multiple objects with the kind of query you'd be using with a get.

The keyword here is `unlikely`. As mentioned in the other email, using `first()` on the assumption that there is only one item, without doing an assertion check, can lead to unexpected behaviour.

The use case for `first()` is not the same as the use case for `get_or_none()`, but the principle of why they should be there is the same.
 
I really dislike the get_or_create shortcut syntax, and I don't think a good name for get_or_none exists for the manager method.

I would agree the name `get_or_none` does feel a little odd, but still struggling to think of an alternative.
 

.get(or=None) (of some description) would be my preference, but even that is ugly and confuses the existing API with "special" keywords that aren't actually a filter.

I would be strong -1 on having a special keyword.
 

Michael Manfre

unread,
Mar 14, 2014, 11:50:49 AM3/14/14
to django-d...@googlegroups.com
On Fri, Mar 14, 2014 at 11:15 AM, Cal Leeming [Simplicity Media Ltd] <cal.l...@simplicitymedialtd.co.uk> wrote: 

.get(or=None) (of some description) would be my preference, but even that is ugly and confuses the existing API with "special" keywords that aren't actually a filter.

I would be strong -1 on having a special keyword.

Even if the special keyword is 'default'? .get(..., default=None) is a common python pattern that fits well with this usage.

Regards,
Michael Manfre

Florian Apolloner

unread,
Mar 14, 2014, 11:52:58 AM3/14/14
to django-d...@googlegroups.com

Yes, especially 'default' -- which is a perfectly valid name for a table column.

Carl Meyer

unread,
Mar 14, 2014, 11:53:46 AM3/14/14
to django-d...@googlegroups.com
On 03/14/2014 09:50 AM, Michael Manfre wrote:
> On Fri, Mar 14, 2014 at 11:15 AM, Cal Leeming [Simplicity Media Ltd]
> <cal.l...@simplicitymedialtd.co.uk
> <mailto:cal.l...@simplicitymedialtd.co.uk>> wrote:
>
>
> .get(or=None) (of some description) would be my preference, but
> even that is ugly and confuses the existing API with "special"
> keywords that aren't actually a filter.
>
>
> I would be strong -1 on having a special keyword.
>
>
> Even if the special keyword is 'default'? .get(..., default=None) is a
> common python pattern that fits well with this usage.

Backwards-incompatible (and generally problematic) for any model with a
field named 'default'.

Adding special keyword arguments to functions that currently take
arbitrary filters as kwargs is basically a non-starter.

Carl

Michael Manfre

unread,
Mar 14, 2014, 12:40:25 PM3/14/14
to django-d...@googlegroups.com
Good point. I forgot that some people would do that.


--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Shai Berger

unread,
Mar 15, 2014, 1:06:01 PM3/15/14
to django-d...@googlegroups.com
There is a family of names that would be valid -- names that cannot be used to
name fields -- and that is names that begin with dunder.

I would like to see neither get(__default=x) nor first(__only=True) -- I think
that's quite ugly -- I just want to remind us that technically, the option
exists.
> > a-6e52cc9b511a%40googlegroups.com<https://groups.google.com/d/msgid/django
> > -developers/4aa7d1e3-4fb3-429e-a95a-6e52cc9b511a%40googlegroups.com?utm_me
> > dium=email&utm_source=footer> .

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 16, 2014, 1:39:47 PM3/16/14
to django-d...@googlegroups.com
Still waiting for other people to chime in on this thread, but so far I'm not seeing any argument against the explained logic for having `.get_or_none()`. 

Comments appear to be more regarding how not to do it, rather than not doing it at all.

Ultimately this either needs someone to find a flaw in my logic (have I missed anything?), or needs a BDFL decision.

If all this needs to get approved is a patch, then I'll be happy to do so.

Cal




Gwildor Sok

unread,
Mar 17, 2014, 5:20:11 AM3/17/14
to django-d...@googlegroups.com
Actually, at the moment you can't have a column named "defaults" either if you want to use your model with the current get_or_create function, so naming a keyword argument like that is not that uncommon.

Adam Mesha

unread,
Mar 17, 2014, 5:17:08 PM3/17/14
to django-developers
And the documentation suggests that if you do have a field named defaults and you want to use it with get_or_create, you should use defaults__exact.  I don't see why a similar solution wouldn't work for other queryset methods.



For more options, visit https://groups.google.com/d/optout.



--
Adam Mesha <adam....@gmail.com>
Life is either a daring adventure, or nothing. - Helen Keller

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 20, 2014, 9:01:25 AM3/20/14
to django-d...@googlegroups.com
I'll give it a couple more days for a BDFL to gives the thumbs up/down.

If no reply, then I'll make a patch and open a ticket.

Cal


Florian Apolloner

unread,
Mar 20, 2014, 9:31:32 AM3/20/14
to django-d...@googlegroups.com
On Thursday, March 20, 2014 2:01:25 PM UTC+1, Cal Leeming [Simplicity Media Ltd] wrote:
I'll give it a couple more days for a BDFL to gives the thumbs up/down.
 
Well, we don't have BDFLs anymore and Shai already said he is -0 on it, count me in with a relatively strong -0 too. I'd be a bit more open to it if you could a better name. That said I generally agree with Shai about the validation, eg this should be handled by the database constraints already -- otherwise selecting via get doesn't make much sense imo.

cheers,
Florian

Cheng Chi

unread,
Mar 25, 2014, 3:20:19 AM3/25/14
to django-d...@googlegroups.com
What about name it fetch()?

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 25, 2014, 9:26:06 AM3/25/14
to django-d...@googlegroups.com
That's actually not a bad idea at all, much better than get_or_none().

Cal


--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Russell Keith-Magee

unread,
Mar 25, 2014, 7:40:16 PM3/25/14
to Django Developers
Count me as a -0 as well. I simply don't see the problem with catching exceptions. Changing the name doesn't modify my objections.

I'm probably a true neutral 0 on a django.shortcuts method mirroring get_object_or_404 (which, BTW, is what the way-back-in-2007 thread was proposing). I still don't see the point, but at least it's in a shortcuts module, and clearly labelled as such, not a method on a manager duplicating existing functionality.

Yours,
Russ Magee %-)

Anssi Kääriäinen

unread,
Mar 26, 2014, 4:26:10 AM3/26/14
to django-d...@googlegroups.com
I'd like to solve this with a way to add methods to QuerySets. This would be useful for those who want get_or_none() or other similar shortcut methods which Django doesn't provide. But more importantly this would let third party apps to provide queryset methods.

If this was possible, then one could do something like this:

@register_qs_method
def get_or_none(self, *args, **kwargs):
    try:
         return self.get(*args, **kwargs)
    except ObjectDoesNotExist:
         return None

I don't know how ugly the magic needed for this would be. Quickly thinking the @register_qs_method doesn't need to do much else than assignment to QuerySet.__dict__. How to add the same method to dynamically created managers might be harder.

 - Anssi
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Russell Keith-Magee

unread,
Mar 26, 2014, 4:46:28 AM3/26/14
to Django Developers
On Wed, Mar 26, 2014 at 4:26 PM, Anssi Kääriäinen <anssi.ka...@thl.fi> wrote:
I'd like to solve this with a way to add methods to QuerySets. This would be useful for those who want get_or_none() or other similar shortcut methods which Django doesn't provide. But more importantly this would let third party apps to provide queryset methods.

If this was possible, then one could do something like this:

@register_qs_method
def get_or_none(self, *args, **kwargs):
    try:
         return self.get(*args, **kwargs)
    except ObjectDoesNotExist:
         return None


Isn't this already possible by defining a custom queryset? And even easier in 1.7 with the introduction of .as_manager()?

The only thing the custom Queryset approach doesn't allow is to monkey patch the get_or_none method into *all* managers by default - but frankly, I see that as a good thing. 

Yours,
Russ Magee %-)

Anssi Kääriäinen

unread,
Mar 26, 2014, 5:05:55 AM3/26/14
to django-d...@googlegroups.com
On 03/26/2014 10:46 AM, Russell Keith-Magee wrote:
> Isn't this already possible by defining a custom queryset? And even
> easier in 1.7 with the introduction of .as_manager()?
>
> The only thing the custom Queryset approach doesn't allow is to monkey
> patch the get_or_none method into *all* managers by default - but
> frankly, I see that as a good thing.
Yeah, this should be possible.

I was thinking of solving the problem when one has two different base
classes for a model. Creating automatically a queryset + manager for the
model could be a nice addition. Something like using GIS + django-mptt
simultaneously for a model, and then both of those automatically adding
their queryset methods to the model's queryset. But, maybe this problem
isn't worth solving (or more accurately, it is better solved explicitly
by the user by providing a custom queryset inheriting from both
GeoQuerySet and MPTTQuerySet).

- Anssi

Curtis Maloney

unread,
Mar 26, 2014, 5:18:02 AM3/26/14
to django-d...@googlegroups.com
You could (ab)use MethodType to add a new method to the class, but you'd want to make sure the Manager was using a custom QuerySet first [or force it to if it weren't] so as not to alter it for all default Managers.

Just my 0.02 units of currency.

--
C



Cal Leeming [Simplicity Media Ltd]

unread,
Mar 26, 2014, 8:19:47 AM3/26/14
to django-d...@googlegroups.com
On Tue, Mar 25, 2014 at 11:40 PM, Russell Keith-Magee <rus...@keith-magee.com> wrote:

On Thu, Mar 20, 2014 at 9:31 PM, Florian Apolloner <f.apo...@gmail.com> wrote:
On Thursday, March 20, 2014 2:01:25 PM UTC+1, Cal Leeming [Simplicity Media Ltd] wrote:
I'll give it a couple more days for a BDFL to gives the thumbs up/down.
 
Well, we don't have BDFLs anymore and Shai already said he is -0 on it, count me in with a relatively strong -0 too. I'd be a bit more open to it if you could a better name. That said I generally agree with Shai about the validation, eg this should be handled by the database constraints already -- otherwise selecting via get doesn't make much sense imo.
 
Count me as a -0 as well. I simply don't see the problem with catching exceptions. Changing the name doesn't modify my objections.

Then why did first() [1] get added? The only difference is that first() adds a reference to [0], but it still saves the equal amount of code and is still there for the same reasons of convenience, no?

 

I'm probably a true neutral 0 on a django.shortcuts method mirroring get_object_or_404 (which, BTW, is what the way-back-in-2007 thread was proposing). I still don't see the point, but at least it's in a shortcuts module, and clearly labelled as such, not a method on a manager duplicating existing functionality.

Yours,
Russ Magee %-)

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Wim Feijen

unread,
Mar 26, 2014, 11:24:11 AM3/26/14
to django-d...@googlegroups.com
Hi Cal,

When we proposed to add get_or_none() , we actually ended up adding a first() -method. The primary motives were to have a clearer name and to stay in line with other existing api's.

In my opinion, get_or_none is too much a duplicate of first and I am therefore -1 of it being added. 

For those interested, I will copy the message of BDFL Jacob here:

Hi! 

After thinking a bit, I'm +1 on the idea, and I think 
`Queryset.first()` is the right name (which implies we should probably 
have a `last()` for completion). 

This mirrors the name used by a few other ORM/collection APIs that I 
think have really nice APIS: 


If someone produces a patch I'll review it and commit it. 

Jacob 

This was written as a response to Anssi's message:

For the record, I still do like this idea, specifically .get_or_none().

It seems there is significant support for this idea. I do think this method is Pythonic. There are cases where no match from get() isn't exceptional, and in such cases try-except just feels wrong.

The counter argument is that this is API bloat, and this will set an example for more API bloat.

In the end this is a decision with almost no technical considerations and a lot of "good taste" considerations. So, this seems like BDFL area.

If there are no signs from BDFL that .get_or_none() is acceptable, then lets bury this one. If it is acceptable, then I am willing to do all the work to get this committed.

 - Anssi

The whole thread is here:

Regards, Wim

Paulo Poiati

unread,
Mar 26, 2014, 1:47:35 PM3/26/14
to django-d...@googlegroups.com, Wim Feijen
Well, I think this kind of feature is in the same category of my request: https://groups.google.com/forum/#!topic/django-developers/N6xazCaJC_E. I haven’t received so many feedbacks though.

It’s all about writing less code to reach the goal. I belive too much abstraction is not a good thing for a framework, but in common cases, like link the above, I think it’s ok.

I agree with some guys here, in the case of #get_or_none, manager#first almost always fit the role. So, I’m neutral in regard of that.



— 
[]’s
Paulo Poiati
blog.paulopoiati.com

Russell Keith-Magee

unread,
Mar 26, 2014, 9:10:13 PM3/26/14
to Django Developers
On Wed, Mar 26, 2014 at 8:19 PM, Cal Leeming [Simplicity Media Ltd] <cal.l...@simplicitymedialtd.co.uk> wrote:



On Tue, Mar 25, 2014 at 11:40 PM, Russell Keith-Magee <rus...@keith-magee.com> wrote:

On Thu, Mar 20, 2014 at 9:31 PM, Florian Apolloner <f.apo...@gmail.com> wrote:
On Thursday, March 20, 2014 2:01:25 PM UTC+1, Cal Leeming [Simplicity Media Ltd] wrote:
I'll give it a couple more days for a BDFL to gives the thumbs up/down.
 
Well, we don't have BDFLs anymore and Shai already said he is -0 on it, count me in with a relatively strong -0 too. I'd be a bit more open to it if you could a better name. That said I generally agree with Shai about the validation, eg this should be handled by the database constraints already -- otherwise selecting via get doesn't make much sense imo.
 
Count me as a -0 as well. I simply don't see the problem with catching exceptions. Changing the name doesn't modify my objections.

Then why did first() [1] get added? The only difference is that first() adds a reference to [0], but it still saves the equal amount of code and is still there for the same reasons of convenience, no?

first() was primarily added as an analog for latest().

Personally, I see a significant difference between first()/latest() and get_or_none(). 

first()/latest() is a specific, common query that has been optimized: list all, order by X, give me the first one.

get_or_none() is a second version of an existing query: get(), but with a different return value. To me, this is duplication of an API, not a different query.

In my ideal world, the get(default=None) approach would be what we would do; but, as others have pointed out, default is a valid column name, so this option isn't available to us. We already have a shortcut for get_object_or_404; a matching get_object_or_none makes sense to me, and puts the API where it make sense to me - as a shortcut for someone who is repeating the "catch DoesNotExist" pattern regularly and wants an easier way.

Yours,
Russ Magee %-)

Anssi Kääriäinen

unread,
Mar 27, 2014, 12:44:22 AM3/27/14
to django-d...@googlegroups.com
On Thursday, March 27, 2014 3:10:13 AM UTC+2, Russell Keith-Magee wrote:
In my ideal world, the get(default=None) approach would be what we would do; but, as others have pointed out, default is a valid column name, so this option isn't available to us. We already have a shortcut for get_object_or_404; a matching get_object_or_none makes sense to me, and puts the API where it make sense to me - as a shortcut for someone who is repeating the "catch DoesNotExist" pattern regularly and wants an easier way.

Actually I think we could use .get(default=None). The way to do this is:
  1. Deprecate direct use of .get(default=lookup_value), instead ask users to do .get(Q(default=lookup_value)).
  2. After deprecation, add support for .get(default=default_if_no_match_value)

This is somewhat confusing API - if you have a column named default, users *will* try to do .get(default=lookup_value), no matter how much we document this special case. OTOH I don't believe having field named default is that common.

Another similar idea is to add support for .get(__default=None). One can't have a field named __default, or at least it won't work in any sane way with the ORM.

Any support for these ideas?

 - Anssi

Shai Berger

unread,
Mar 27, 2014, 5:27:53 PM3/27/14
to django-d...@googlegroups.com
Hi,

(the below is reordered)

On Wednesday 26 March 2014 21:44:22 Anssi Kääriäinen wrote:
>
> Actually I think we could use .get(default=None). The way to do this is:
> 1. Deprecate direct use of .get(default=lookup_value), instead ask users
> to do .get(Q(default=lookup_value)).
> 2. After deprecation, add support for
> .get(default=default_if_no_match_value)
>
> This is somewhat confusing API - if you have a column named default, users
> *will* try to do .get(default=lookup_value), no matter how much we document
> this special case. OTOH I don't believe having field named default is that
> common.
>

-0 on confusing APIs. As was noted in this thread, we already have something
similar with "defaults" on get_or_create(), but the value for that argument is
not valid for most fields (and in particular, not valid for any field in core);
so when reading code, it is immediately clear what the intention was. Here, it
will be easy to err on reading as well as writing.

> Another similar idea is to add support for .get(__default=None). One can't
> have a field named __default, or at least it won't work in any sane way
> with the ORM.
>

I find this ugly (I think I said so, but maybe I just intended to).

> On Thursday, March 27, 2014 3:10:13 AM UTC+2, Russell Keith-Magee wrote:
> > In my ideal world, the get(default=None) approach would be what we would
> > do; but, as others have pointed out, default is a valid column name, so
> > this option isn't available to us.

Actually, I think there is a sane spelling for this we could use, but it's one
that shouldn't be introduced lightly.

The idea is to use the *args, not the **kwargs. Currently, the only thing that
can go in the positional args is Q objects; we could also allow "policy
objects" (naming improvements welcome). Thus,

MyModel.objects.get(Default(None), field=value)
MyModel.objects.get(Q(...), Default(my_default_instance))

or something similar. However, this is a whole new style of arguments to
queries, so I'd be very reluctant to add it just for this use case; and I
can't easily come up with many others. The only one I could think of was query
hints (in the SQL sense), and that's not very strong.

> > We already have a shortcut for
> > get_object_or_404; a matching get_object_or_none makes sense to me, and
> > puts the API where it make sense to me - as a shortcut for someone who is
> > repeating the "catch DoesNotExist" pattern regularly and wants an easier
> > way.

That makes a lot of sense to me too.

Shai.

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 27, 2014, 5:35:46 PM3/27/14
to django-d...@googlegroups.com
On Thu, Mar 27, 2014 at 1:10 AM, Russell Keith-Magee <rus...@keith-magee.com> wrote:

On Wed, Mar 26, 2014 at 8:19 PM, Cal Leeming [Simplicity Media Ltd] <cal.l...@simplicitymedialtd.co.uk> wrote:



On Tue, Mar 25, 2014 at 11:40 PM, Russell Keith-Magee <rus...@keith-magee.com> wrote:

On Thu, Mar 20, 2014 at 9:31 PM, Florian Apolloner <f.apo...@gmail.com> wrote:
On Thursday, March 20, 2014 2:01:25 PM UTC+1, Cal Leeming [Simplicity Media Ltd] wrote:
I'll give it a couple more days for a BDFL to gives the thumbs up/down.
 
Well, we don't have BDFLs anymore and Shai already said he is -0 on it, count me in with a relatively strong -0 too. I'd be a bit more open to it if you could a better name. That said I generally agree with Shai about the validation, eg this should be handled by the database constraints already -- otherwise selecting via get doesn't make much sense imo.
 
Count me as a -0 as well. I simply don't see the problem with catching exceptions. Changing the name doesn't modify my objections.

Then why did first() [1] get added? The only difference is that first() adds a reference to [0], but it still saves the equal amount of code and is still there for the same reasons of convenience, no?

first() was primarily added as an analog for latest().

Personally, I see a significant difference between first()/latest() and get_or_none(). 

Personally, I would not class this as being significantly different enough to justify get_or_none() being less worthy.
 

first()/latest() is a specific, common query that has been optimized: list all, order by X, give me the first one. 

get_or_none() is a second version of an existing query: get(), but with a different return value. To me, this is duplication of an API, not a different query.

I do see where you are coming from with the "duplication of an existing API" argument, though in my personal opinion I would argue it's a common enough use case to justify inclusion.
 

In my ideal world, the get(default=None) approach would be what we would do; but, as others have pointed out, default is a valid column name, so this option isn't available to us.

I would be a strong -1 on using any keyword argument on the existing .get() to specify the default, whatever the name.
 
We already have a shortcut for get_object_or_404; a matching get_object_or_none makes sense to me, and puts the API where it make sense to me - as a shortcut for someone who is repeating the "catch DoesNotExist" pattern regularly and wants an easier way.

For me, using shortcuts doesn't feel nice on the eyes at all, and I would continue to use the mixin rather than shortcuts, though this is a difference of opinion in taste.

Another alternative would be to add GetOrNoneMixin to the official docs, similar to dictfetchall() [1].

If the argument of "duplication of an existing API" is going to be the nail in the coffin, my next proposal would be for the aforementioned docs patch instead.

 

Yours,
Russ Magee %-)

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Aymeric Augustin

unread,
Mar 28, 2014, 4:52:58 PM3/28/14
to django-d...@googlegroups.com
Hello,

I’m not sure this thread is going anywhere and I don’t care either way.
If you’ve been reading up to this point...

… either you have too much spare time. Rather than rehashing this debate,
may I suggest triaging some tickets? That would really help!

https://docs.djangoproject.com/en/dev/internals/contributing/triaging-tickets/
https://code.djangoproject.com/query?status=!closed&stage=Unreviewed&desc=1&order=changetime

… or you really want this feature badly. I’ll just leave this here:

from django.db.models.query import QuerySet
from django.db.models.manager import Manager

def queryset_get_or_none(self, *args, **kwargs):
try:
return self.get(*args, **kwargs)
except self.model.DoesNotExist:
pass

QuerySet.get_or_none = queryset_get_or_none

def manager_get_or_none(self, *args, **kwargs):
return self.get_queryset().get_or_none(*args, **kwargs)

Manager.get_or_none = manager_get_or_none

(That kind of monkey-patching isn’t the worst because even if
Django grows a method with that name, it will behave the same.)

--
Aymeric.

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 28, 2014, 5:28:55 PM3/28/14
to django-d...@googlegroups.com
So is that a "no" to the docs patch proposal?

Cal


On Fri, Mar 28, 2014 at 8:52 PM, Aymeric Augustin <aymeric....@polytechnique.org> wrote:
Hello,

I'm not sure this thread is going anywhere and I don't care either way.
If you've been reading up to this point...

... either you have too much spare time. Rather than rehashing this debate,
... or you really want this feature badly. I'll just leave this here:


from django.db.models.query import QuerySet
from django.db.models.manager import Manager

def queryset_get_or_none(self, *args, **kwargs):
    try:
        return self.get(*args, **kwargs)
    except self.model.DoesNotExist:
        pass

QuerySet.get_or_none = queryset_get_or_none

def manager_get_or_none(self, *args, **kwargs):
    return self.get_queryset().get_or_none(*args, **kwargs)

Manager.get_or_none = manager_get_or_none

(That kind of monkey-patching isn't the worst because even if
Django grows a method with that name, it will behave the same.)

--
Aymeric.
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Aymeric Augustin

unread,
Mar 28, 2014, 6:04:17 PM3/28/14
to django-d...@googlegroups.com
On 28 mars 2014, at 22:28, Cal Leeming [Simplicity Media Ltd] <cal.l...@simplicitymedialtd.co.uk> wrote:

> So is that a "no" to the docs patch proposal?

It isn’t. Like I said, I really don’t care. If someone wants to commit something, that’s fine.

--
Aymeric.

Cal Leeming [Simplicity Media Ltd]

unread,
Mar 28, 2014, 6:10:20 PM3/28/14
to django-d...@googlegroups.com
Okay great, I'll send a docs patch in next week (assuming no one shows any disagreement), and hopefully that'll be the end of it!

Thanks to all for your feedback.

Cal



--
Aymeric.

--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
Reply all
Reply to author
Forward
0 new messages