Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Related filter negation has unexpected behavior #217

Open
2 of 4 tasks
rpkilby opened this issue Jan 11, 2018 · 2 comments
Open
2 of 4 tasks

Related filter negation has unexpected behavior #217

rpkilby opened this issue Jan 11, 2018 · 2 comments

Comments

@rpkilby
Copy link
Collaborator

rpkilby commented Jan 11, 2018

This builds off of the thoughts in #54 and #99, but there may be some potential confusion when it comes to related filter negation. Using the Blog/Entry example in #99, what would users expect to happen with the following filter?

GET /blogs?entry__headline__contains!=Lennon&entry__pub_date__year!=2008

Users might reasonably expect a list of blogs that don't have an entry about Lennon published in 2008.

Blog.objects.exclude(
    entry__in=Entry.objects
                   .filter(headline__contains='Lennon')
                   .filter(pub_date__year=2008))

Due to how related filtersets are processed, the following is produced instead. This would return the set of blogs that have any entries that aren't about Lennon published in 2008. eg, a blog could have discussed Lennon in 2008 and lentils in 2009, but it would still be returned in the results.

Blog.objects.filter(
    entry__in=Entry.objects
                   .exclude(headline__contains='Lennon')
                   .exclude(pub_date__year=2008))

To produce the first query, users will most likely need to use the complex filter backend. I haven't tested it, but the following unencoded filter should work:

GET /blogs?filters=~(entry__headline__contains=Lennon&entry__pub_date__year=2008)

TODO:

  • Test queryset assumptions
  • Tests behavior of filterset
  • Tests behavior of complex filter backend
  • Document approach to exclusion across a to-many relationship
@rpkilby
Copy link
Collaborator Author

rpkilby commented Jan 12, 2018

The complex query negation does not work as expected, however it would be possible to implement this using the QuerySet.difference method.

@rpkilby rpkilby changed the title Document related filter negation Related filter negation has unexpected behavior Jul 14, 2018
@rpkilby
Copy link
Collaborator Author

rpkilby commented Jul 14, 2018

The latter of the two queries is functionally useless. If the parent relationship has any child element that isn't excluded, it will be included in the results. A parent would only ever be excluded if all of its children were excluded.

My initial reaction was to capture the exclusion markers and then flag that relationship to be excluded at the root filterset, however, this would also affect regular filters. e.g., it would not be possible to do:
related__field_a=1&related__field_b!=2, as the marker would effectively negate both related fields.

A few workarounds:

  • Group all exclusion filters into one queryset and all regular filters into another queryset, then AND them together. Related exclusion would need to be documented as having different behavior from regular field exclusion. This is potentially confusing to users, but probably the right behavior.
  • Document that related filter exclusion may have unexpected behavior, and that they should use the complex query backend with the difference method, a la the example in the OP. The downside here is that related exclusion would require more setup and encoding the query params per the complex backend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant