Calling of int constructor in QuerySet

98 views
Skip to first unread message

belorn mandos

unread,
Feb 12, 2016, 6:58:11 PM2/12/16
to Django developers (Contributions to Django itself)
When working with QuerySets and IntegerFields, I saw that comparison to
a string with no digits in it throws a ValueError exception when I did

...objects.filter(org_number = criteria)

or

...objects.filter(org_number__exact = criteria)

However, when using iexact, it did not throw an exception, so

...objects.filter(org_number__iexact = criteria)

worked fine. Of course, a case-insensitive function should not be used
for the purpose of avoiding a casting issue so I initially assumed that
this must be an odd implementation detail that one can't rely on. Python
however does permit int and string comparison (1 == "foo") without it
throwing a ValueError, so which of the two behaviors is doing the right
thing here? Is "__exact" being too strict with its input, or is __iexact
too relaxed?

The question is not purely philosophic, since ignoring type casting
makes my current code a simple and short Q query with

multiple OR arguments in it.

/Björn Påhlsson

Josh Smeaton

unread,
Feb 13, 2016, 5:54:55 AM2/13/16
to Django developers (Contributions to Django itself)
The iexact lookup shouldn't even be defined for number types should it? I'm assuming it is due to backwards compatibility (from 1.7 when custom lookups landed), but what would that even mean? If we're not able to remove iexact lookup from number types then I would agree that it should also throw an error, because there's no reason I can see to ever iexact compare a number to an empty string unless there's a problem in the calling code.

Shai Berger

unread,
Feb 13, 2016, 3:17:34 PM2/13/16
to django-d...@googlegroups.com
On Saturday 13 February 2016 12:54:55 Josh Smeaton wrote:
> The iexact lookup shouldn't even be defined for number types should it? I'm
> assuming it is due to backwards compatibility (from 1.7 when custom lookups
> landed), but what would that even mean? If we're not able to remove iexact
> lookup from number types then I would agree that it should also throw an
> error, because there's no reason I can see to ever iexact compare a number
> to an empty string unless there's a problem in the calling code.
>

Right. Any __iexact comparison applied to an integer field is more likely a bug
than some kind of workaround as the OP described. +1 to making it an error.

On the deeper issue:

> On Saturday, 13 February 2016 10:58:11 UTC+11, belorn mandos wrote:
> > ...Python
> > however does permit int and string comparison (1 == "foo") without it
> > throwing a ValueError, so which of the two behaviors is doing the right
> > thing here?

Python allows comparison between ints and strs, but never considers them
equal:

>>> 1=="1"
False

SQL, on the other hand, is weakly typed:


postgres=# select 1 = '1';
?column?
----------
t
(1 row)

And that's on postgres, the best of them, which will let you make things more
type-safe if you ask for it explicitly:

postgres=# select 1::integer = '1'::varchar;
ERROR: operator does not exist: integer = character varying
LINE 1: select 1::integer = '1'::varchar;
^
HINT: No operator matches the given name and argument type(s). You might
need to add explicit type casts.

(that "^" sign points at the "=" symbol if you use a fixed-size font).

So, I think the current implementation is correct.

> > The question is not purely philosophic, since ignoring type casting
> > makes my current code a simple and short Q query with
> > multiple OR arguments in it.

Strong typing is good for you. I suspect that adding lines that look like

val = int(s) if s else None

will make your code clearer and safer against unexpected inputs than what you
have currently, and certainly cleaner than using __iexact.

Shai.

belorn mandos

unread,
Feb 13, 2016, 10:52:32 PM2/13/16
to Django developers (Contributions to Django itself)


On Saturday, February 13, 2016 at 9:17:34 PM UTC+1, Shai Berger wrote:

        val = int(s) if s else None

will make your code clearer and safer against unexpected inputs than what you
have currently, and certainly cleaner than using __iexact.

I was thinking in the line of  wrapping the Q on the condition of isnumeric:

        (Q(org_name=criteria) | (Q(org_number=int(criteria)) if criteria.isnumeric() else False)

/Björn Påhlsson

Shai Berger

unread,
Feb 14, 2016, 2:00:06 AM2/14/16
to django-d...@googlegroups.com
I'm not sure `False` can be used as a Q object, and you also need to take care
that criteria is strip()'ed of whitespace; but this is going deep into
django-users territory.

HTH,
Shai.
Reply all
Reply to author
Forward
0 new messages