Opened 4 years ago

Closed 4 years ago

#31783 closed Bug (fixed)

Filtering on a field named `negate` raises a TypeError

Reported by: Aaron Kirkbride Owned by: Hasan Ramezani
Component: Database layer (models, ORM) Version: 3.0
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

Filtering on a model with a field named negate raises a TypeError.

For example:

class Foo(models.Model):
    negate = models.BooleanField()

Foo.objects.filter(negate=True)

raises TypeError: _filter_or_exclude() got multiple values for argument 'negate'

negate is not documented as a reserved argument for .filter(). I'm currently using .filter(negate__exact=True) as a workaround.

Change History (8)

comment:1 by Simon Charette, 4 years ago

Easy pickings: set
Triage Stage: UnreviewedAccepted

We should either document this limitation or change _filter_or_exclude and friends signature from (negate, *args, **kwargs) to (negate, args, kwargs).

I think the second approach is favourable as there's not much benefits in using arguments unpacking in these private methods as long as the public methods filter and exclude preserve their signature.

Last edited 4 years ago by Simon Charette (previous) (diff)

comment:2 by Simon Charette, 4 years ago

Aaron, would you be interested in submitting a PR with the changes below plus a regression test in tests/query/tests.py?

  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 07d6ffd4ca..d655ede8d9 100644
    a b class QuerySet:  
    204204    def query(self):
    205205        if self._deferred_filter:
    206206            negate, args, kwargs = self._deferred_filter
    207             self._filter_or_exclude_inplace(negate, *args, **kwargs)
     207            self._filter_or_exclude_inplace(negate, args, kwargs)
    208208            self._deferred_filter = None
    209209        return self._query
    210210
    class QuerySet:  
    939939        set.
    940940        """
    941941        self._not_support_combined_queries('filter')
    942         return self._filter_or_exclude(False, *args, **kwargs)
     942        return self._filter_or_exclude(False, args, kwargs)
    943943
    944944    def exclude(self, *args, **kwargs):
    945945        """
    class QuerySet:  
    947947        set.
    948948        """
    949949        self._not_support_combined_queries('exclude')
    950         return self._filter_or_exclude(True, *args, **kwargs)
     950        return self._filter_or_exclude(True, args, kwargs)
    951951
    952     def _filter_or_exclude(self, negate, *args, **kwargs):
     952    def _filter_or_exclude(self, negate, args, kwargs):
    953953        if args or kwargs:
    954954            assert not self.query.is_sliced, \
    955955                "Cannot filter a query once a slice has been taken."
    class QuerySet:  
    959959            self._defer_next_filter = False
    960960            clone._deferred_filter = negate, args, kwargs
    961961        else:
    962             clone._filter_or_exclude_inplace(negate, *args, **kwargs)
     962            clone._filter_or_exclude_inplace(negate, args, kwargs)
    963963        return clone
    964964
    965     def _filter_or_exclude_inplace(self, negate, *args, **kwargs):
     965    def _filter_or_exclude_inplace(self, negate, args, kwargs):
    966966        if negate:
    967967            self._query.add_q(~Q(*args, **kwargs))
    968968        else:
    class QuerySet:  
    983983            clone.query.add_q(filter_obj)
    984984            return clone
    985985        else:
    986             return self._filter_or_exclude(False, **filter_obj)
     986            return self._filter_or_exclude(False, args=(), kwargs=filter_obj)
    987987
    988988    def _combinator_query(self, combinator, *other_qs, all=False):
    989989        # Clone the query to inherit the select list and everything

in reply to:  2 comment:3 by Aaron Kirkbride, 4 years ago

Replying to Simon Charette:

Aaron, would you be interested in submitting a PR with the changes below plus a regression test in tests/query/tests.py?

Sure! I can do that this evening. Thanks for the patch :)

Last edited 4 years ago by Aaron Kirkbride (previous) (diff)

comment:4 by Aaron Kirkbride, 4 years ago

Owner: changed from nobody to Aaron Kirkbride
Status: newassigned

comment:5 by Hasan Ramezani, 4 years ago

Has patch: set
Owner: changed from Aaron Kirkbride to Hasan Ramezani
Last edited 4 years ago by Mariusz Felisiak (previous) (diff)

in reply to:  5 comment:6 by Aaron Kirkbride, 4 years ago

Replying to Hasan Ramezani:

Thanks Hasan for creating the PR :) Sorry I didn't get round to this earlier.

comment:7 by Simon Charette, 4 years ago

Triage Stage: AcceptedReady for checkin

comment:8 by Mariusz Felisiak <felisiak.mariusz@…>, 4 years ago

Resolution: fixed
Status: assignedclosed

In 9c9a3fe1:

Fixed #31783 -- Fixed crash when filtering againts "negate" field.

Thanks Simon Charette for the initial patch.

Note: See TracTickets for help on using tickets.
Back to Top