#5763: Queryset doesn't have a "not equal" filter operator
-------------------------------------+-------------------------------------
Reporter: jdetaeye | Owner: nobody
Type: New feature | Status: reopened
Component: Database layer | Version: SVN
(models, ORM) | Resolution:
Severity: Normal | Triage Stage: Design
Keywords: qs-rf | decision needed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 1 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by asmoore82):
* status: closed => reopened
* resolution: wontfix =>
* easy: 0 => 1
Comment:
Apologies for the re-open - but I wasn't sure what would be worse, a re-
open or a new ticket for the same old discussion...
I'm going to have to take the position that the absence of a `__ne`
operator //still//
represents a functional hole in the querying API, even with consideration
of exclude()
> it doesn't make sense to have `__ne` without any other negated queries,
Ah but there are indeed other negated queries.
`filter(__gte)` and `exclude(__lt)` are **almost** equivalent.
Similarly, you can **almost** approximate `__ne` with a hokey `Q(__lt) |
Q(__gt)` contraption.
But the real issue lies within that "almost" -- this is what could be
cause for a separate ticket, but a quick and dirty `__ne` would head the
issue off altogether ;). Chaining multiple `filter()` and `exclude()`
calls on multi-valued relationships yields not-so-surprising but
nonetheless undesired results. To quote from the Django docs:
> Django has a consistent way of processing `filter()` and `exclude()`
calls. Everything inside a single `filter()` call is applied
simultaneously to filter out items matching all those requirements.
Successive `filter()` calls further restrict the set of objects, but for
multi-valued relations, they apply to any object linked to the primary
model, not necessarily those objects that were selected by an earlier
`filter()` call.
This consistency is a good thing but it combines with the lack of `__ne`
to form a problem.
Say you have blogs with entries with tags and author_counts
If you want to get all entries that are tagged 'django' with more than 2
co-authors:
{{{ Entry.objects.filter(tag__name='django', author_count__gt=2) }}}
It is similarly easy to get blogs with entries with the above criteria:
{{{ Blog.objects.filter(entry__tag__name='django',
entry__author_count__gt=2) }}}
But what if you want entries tagged 'django' that are **not** co-authored
by 2:
{{{ Entry.objects.filter(tag__name='django').exclude(author_count=2) }}}
But if you want the blogs with those entries, it can't easily be done:
{{{
Blog.objects.filter(entry__tag__name='django').exclude(entry__author_count=2)
}}}
is **not** the equivalent of the imaginary query:
{{{ Blog.objects.filter(entry__tag__name='django',
entry__author_count__ne=2) }}}
which can currently be approximated with:
{{{ Blog.objects.filter(Q(entry__author_count__lt=2) |
Q(entry__author_count__gt=2), entry__tag__name='django') }}}
You can also get the desired results with a `.extra()` call but let's not
go there :P.
This brings us to the most surprising aspect, using the negation operator
`~` on `Q()` objects that select on multi-valued relations is technically
possible but can yield the undesired results on the unsuspecting, which
actually runs sort of contrary to the Documentation quote above.
Bad Surprise!!:
{{{ Blog.objects.filter(~Q(entry__author_count=2),
entry__tag__name='django') }}}
`^`The more I look at this, the more I think it is cause for a ticket in
its own right. The fix for this would be to smarten up the `Q()` objects
so that a NOT operator on `__gt` becomes `__lte`, a NOT on `__lt` becomes
`__gte`, and so on. But you will ultimately be missing the `__ne` and
other NOT primitives to fall back on.
In other words, this ticket is a "could-go-either-way" blocker for `^`that
more important ticket. I'll do some research on that and make a ticket if
it hasn't already been addressed.
Thanks to all who make Django awesome! I'm a database newbie and loving
it!
~Adam sM
--
Ticket URL: <https://code.djangoproject.com/ticket/5763#comment:14>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en.