[Django] #17186: Inverted F expression (negation)

32 views
Skip to first unread message

Django

unread,
Nov 9, 2011, 12:51:41 PM11/9/11
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------+--------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Uncategorized | Version: SVN
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------+--------------------
By default, the expression F django, can not make something like:

{{{#!sql
UPDATE "testmodel" SET "done" = NOT "testmodel"."done";
}}}

The idea is to make updates or queries like this:

{{{#!python
TestModel.objects.update(done=~F('done'))
}}}

Taking as a reference, this model:

{{{#!python
class TestModel(models.Model):
done = models.BooleanField(default=False)
}}}

Attached is a patch that works for me, but not if it's the best way to do
it.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Nov 9, 2011, 1:22:04 PM11/9/11
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------+--------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Uncategorized | Version: SVN
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------+--------------------------------------
Changes (by niwi):

* needs_better_patch: => 0
* has_patch: 0 => 1
* needs_tests: => 0
* needs_docs: => 0


Comment:

I forgot this:

The result of the sql would look like:


{{{#!sql
UPDATE "testmodel" SET "done" = NOT "testmodel"."done";
}}}

(tested on sqlite3, postgresql and mysql)

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:1>

Django

unread,
Nov 21, 2011, 1:48:09 PM11/21/11
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 0
Has patch: 1 | Patch needs improvement: 0
Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by lukeplant):

* component: Uncategorized => Database layer (models, ORM)
* needs_tests: 0 => 1
* stage: Unreviewed => Accepted


Comment:

Seems reasonable, and I can't think of an alternative use of negating an F
object.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:2>

Django

unread,
Nov 27, 2011, 6:24:58 AM11/27/11
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 0
Has patch: 1 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by niwi):

* needs_tests: 1 => 0


Comment:

I modified a little test, to cover this modification. Attached the new
patch.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:3>

Django

unread,
Jan 18, 2012, 10:01:29 PM1/18/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 0
Has patch: 1 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------

Comment (by nate_b):

There are several problems with this patch.

For example, you should be able to negate compound expressions:

{{{#!python
>>> Demo.objects.filter(a_field=~(F('a_field')+1))
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: bad operand type for unary ~: 'ExpressionNode'
}}}

Secondly, there is no indication of negation in the string representation,
like there is for other operators:
{{{#!python
>>> print ~F('field')
(DEFAULT: )
>>> print F('field') + F('other')
(+: (DEFAULT: ), (DEFAULT: ))
}}}

Thirdly, {{{tree.Node}}} already supplies a {{{negate()}}} function and a
{{{negated}}} field. Why not use those instead?

I'm not entirely pleased with my alternative as it still doesn't make use
of {{{connector}}}, but it solves these listed problems.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:4>

Django

unread,
Jan 19, 2012, 3:10:57 PM1/19/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 0
Has patch: 1 | Patch needs improvement: 0
Needs tests: 0 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------

Comment (by niwi):

Your solution seems to me pretty good. I had not thought of fields that
are not Boolean. In fact it was an idea that surely someone could have
implemented better than me. (just this case).

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:5>

Django

unread,
Jan 27, 2012, 5:42:21 PM1/27/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 1
Has patch: 1 | Patch needs improvement: 1
Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by lrekucki):

* needs_better_patch: 0 => 1

* needs_tests: 0 => 1

* needs_docs: 0 => 1


Comment:

As a new feature this needs documentation. Also:


* What's the use case for negation on non-boolean values? How does this
behave on different DBs (the tests should check that).
* No tests for compounds statements (should they be boolean only?).
* Why {{{~F()}}} instead of more Pythonic {{{not F()}}}. After all
{{{~True}}} is {{{-2}}} not {{{False}}}. You could use {{{not}}} for
boolean expressions and {{{~}}} for bit negation on fields (which might be
useful too!).

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:6>

Django

unread,
Jan 27, 2012, 6:00:50 PM1/27/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 1
Has patch: 1 | Patch needs improvement: 1
Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------

Comment (by lrekucki):

One more thing worth documenting, due to difference in None and NULL
semantics:

{{{#!python
class A(Model):
f = NullBooleanField()

for m in A.objects.all():
m.f = not m.f; m.save() # all NULL values are now True

# while this will leave NULL values unchanged as NOT NULL is NULL
A.objects.update(f=not F('f'))
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:7>

Django

unread,
Feb 2, 2012, 9:10:43 PM2/2/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 1
Has patch: 1 | Patch needs improvement: 1
Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------

Comment (by nate_b):

Regarding use case for negation on non-boolean values: That's a good
question; I'm not sure that there is one. I provided that only as a
stupid example of lack of composability (at the db level negation on non-
boolean values can work, but that doesn't mean it is useful). If you can
propose a test, I would be glad to include it. Altogether, though, that
does raise a good question - so far, all {{{F()}}} objects are generally
composable across all operations, and I would not want to break that by
adding another operation. Not all computations in general are composable,
though; should {{{F()}}} object composability reflect that? Now my head
hurts.

Regarding "Why ~F() instead of not F()": there are two reasons. One: To
match with [https://docs.djangoproject.com/en/dev/topics/db/queries
/#complex-lookups-with-q-objects Q objects]; Two, and more importantly:
python itself
[http://docs.python.org/library/operator.html#operator.__not__ does not
provide a __not__() method for object instances]. So yes, you are
absolutely right, but I don't think it can be done.

As to your other points, well taken. I hope my updated patch is an
improvement.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:8>

Django

unread,
Feb 5, 2012, 5:10:48 AM2/5/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 1
Has patch: 1 | Patch needs improvement: 1
Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------

Comment (by lrekucki):

Replying to [comment:8 nate_b]:

> Regarding "Why ~F() instead of not F()": there are two reasons. One: To
match with [https://docs.djangoproject.com/en/dev/topics/db/queries
/#complex-lookups-with-q-objects Q objects]; Two, and more importantly:
python itself
[http://docs.python.org/library/operator.html#operator.__not__ does not
provide a __not__() method for object instances]. So yes, you are
absolutely right, but I don't think it can be done.

Ok, now I feel stupid ;)


>
> As to your other points, well taken. I hope my updated patch is an
improvement.

The patch looks good. Now we just need to work out the non-boolean cases
somehow.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:9>

Django

unread,
Feb 5, 2012, 5:19:52 AM2/5/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 1
Has patch: 1 | Patch needs improvement: 1
Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------

Comment (by zsiciarz):

Apparently various database engines work differently regarding negation of
non-boolean values. For example on Postgres each of the following queries
raises an error:

{{{
SELECT NOT 5;
SELECT NOT "5";
SELECT NOT "5abc";
}}}

while on SQLite it happily returns 0. SQLite casts the expression to a
NUMERIC value when used in a boolean context, so in each case it is
"truthy".

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:10>

Django

unread,
Feb 5, 2012, 5:40:05 AM2/5/12
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Design
Keywords: | decision needed
Has patch: 1 | Needs documentation: 1
Needs tests: 1 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by zsiciarz):

* stage: Accepted => Design decision needed


Comment:

I guess it's a DDN. Generally it is even possible to do something like
that in SQLite:


{{{
Company.objects.update(name=F("name") * F("name"))
}}}

And the name field after that is "0". This is rather a wider case, whether
the ORM should allow silly expressions like that, and not only negation of
non-booleans. It is completely DBMS-dependent and most "true" DB engines
will barf immediately.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:11>

Django

unread,
Mar 23, 2013, 9:49:27 AM3/23/13
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------

Reporter: niwi | Owner: nobody
Type: New feature | Status: new
Component: Database layer | Version: master
(models, ORM) | Resolution:

Severity: Normal | Triage Stage: Accepted
Keywords: | Needs documentation: 1
Has patch: 1 | Patch needs improvement: 1

Needs tests: 1 | UI/UX: 0
Easy pickings: 0 |
-------------------------------------+-------------------------------------
Changes (by aaugustin):

* stage: Design decision needed => Accepted


Comment:

This feature shouldn't be expected to give meaningful results on anything
other than a boolean value.

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:12>

Django

unread,
Aug 6, 2015, 12:35:50 PM8/6/15
to django-...@googlegroups.com
#17186: Inverted F expression (negation)
-------------------------------------+-------------------------------------
Reporter: niwi | Owner: nobody
Type: New feature | Status: closed

Component: Database layer | Version: master
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: | Triage Stage: Accepted

Has patch: 1 | Needs documentation: 1
Needs tests: 1 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by timgraham):

* status: new => closed
* resolution: => duplicate


Comment:

Duplicate of #16211

--
Ticket URL: <https://code.djangoproject.com/ticket/17186#comment:13>

Reply all
Reply to author
Forward
0 new messages