#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 django-updates@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.