Skip to content

Prevent deepcopying of filter's parent #172

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

Merged
merged 2 commits into from
Jun 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions rest_framework_filters/filterset.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,13 @@ def expand_filters(self):

# include exclusion keys
if exclude_name in self.data:
f = copy.deepcopy(f)
f.exclude = not f.exclude
requested_filters[exclude_name] = f
# deepcopy the *base* filter to prevent copying of model & parent
f_copy = copy.deepcopy(self.base_filters[filter_name])
f_copy.parent = f.parent
f_copy.model = f.model
f_copy.exclude = not f.exclude

requested_filters[exclude_name] = f_copy

# include filters from related subsets
if isinstance(f, filters.RelatedFilter) and filter_name in related_data:
Expand Down
35 changes: 34 additions & 1 deletion tests/test_filterset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from __future__ import absolute_import
from __future__ import unicode_literals

import sys

from django.test import TestCase
from django.utils import six

Expand All @@ -11,7 +13,7 @@
from django_filters.filters import BaseInFilter

from .testapp.models import (
User, Note, Post, Person, Tag, BlogPost,
Note, Post, Person, Tag, BlogPost,
)

from .testapp.filters import (
Expand All @@ -24,6 +26,21 @@
BlogPostOverrideFilter,
)

from rest_framework.views import APIView
from rest_framework.test import APIRequestFactory
factory = APIRequestFactory()


class limit_recursion:
def __init__(self):
self.original_limit = sys.getrecursionlimit()

def __enter__(self):
sys.setrecursionlimit(100)

def __exit__(self, *args):
sys.setrecursionlimit(self.original_limit)


class LookupsFilterTests(TestCase):
"""
Expand Down Expand Up @@ -429,3 +446,19 @@ def test_related_exclusion_results(self):

self.assertEqual(len(results), 1)
self.assertEqual(results[0], 'Post 2')

def test_exclude_and_request_interaction(self):
# See: https://github.com/philipn/django-rest-framework-filters/issues/171
request = APIView().initialize_request(factory.get('/?tags__name__contains!=Tag'))
filterset = BlogPostFilter(request.query_params, request=request, queryset=BlogPost.objects.all())

try:
with limit_recursion():
qs = filterset.qs
except RuntimeError:
self.fail('Recursion limit reached')

results = [r.title for r in qs]

self.assertEqual(len(results), 1)
self.assertEqual(results[0], 'Post 2')