Skip to content

Commit ccbe750

Browse files
author
Ryan P Kilby
authored
Add test for issue 118 (#119)
* Move filtering tests * Add test for #118
1 parent 4846672 commit ccbe750

File tree

3 files changed

+353
-344
lines changed

3 files changed

+353
-344
lines changed

rest_framework_filters/compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
import django
3+
4+
5+
def set_many(instance, field, value):
6+
if django.VERSION < (1, 10):
7+
setattr(instance, field, value)
8+
else:
9+
field = getattr(instance, field)
10+
field.set(value)

tests/test_filtering.py

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
2+
from __future__ import absolute_import
3+
from __future__ import unicode_literals
4+
5+
from django.test import TestCase
6+
7+
from rest_framework_filters.compat import set_many
8+
from rest_framework_filters import FilterSet, filters
9+
10+
from .testapp.models import (
11+
User, Note, Post, Cover, A, B, C, Person, Tag, BlogPost,
12+
)
13+
14+
from .testapp.filters import (
15+
PersonFilter,
16+
PostFilter,
17+
BlogPostFilter,
18+
CoverFilterWithRelated,
19+
NoteFilterWithAll,
20+
NoteFilterWithRelated,
21+
NoteFilterWithRelatedDifferentName,
22+
NoteFilterWithRelatedAll,
23+
NoteFilterWithRelatedAllDifferentFilterName,
24+
CFilter,
25+
)
26+
27+
28+
class AllLookupsFilterTests(TestCase):
29+
30+
@classmethod
31+
def setUpTestData(cls):
32+
#######################
33+
# Create users
34+
#######################
35+
user1 = User.objects.create(username="user1", email="user1@example.org")
36+
user2 = User.objects.create(username="user2", email="user2@example.org")
37+
38+
#######################
39+
# Create notes
40+
#######################
41+
Note.objects.create(title="Test 1", content="Test content 1", author=user1)
42+
Note.objects.create(title="Test 2", content="Test content 2", author=user1)
43+
Note.objects.create(title="Hello Test 3", content="Test content 3", author=user1)
44+
Note.objects.create(title="Hello Test 4", content="Test content 4", author=user2)
45+
46+
def test_alllookupsfilter(self):
47+
# Test __iendswith
48+
GET = {'title__iendswith': '2'}
49+
f = NoteFilterWithAll(GET, queryset=Note.objects.all())
50+
self.assertEqual(len(list(f.qs)), 1)
51+
self.assertEqual(list(f.qs)[0].title, "Test 2")
52+
53+
# Test __contains
54+
GET = {'title__contains': 'Test'}
55+
f = NoteFilterWithAll(GET, queryset=Note.objects.all())
56+
self.assertEqual(len(list(f.qs)), 4)
57+
58+
# Test that the default exact filter works
59+
GET = {'title': 'Hello Test 3'}
60+
f = NoteFilterWithAll(GET, queryset=Note.objects.all())
61+
self.assertEqual(len(list(f.qs)), 1)
62+
self.assertEqual(list(f.qs)[0].title, "Hello Test 3")
63+
64+
def test_alllookups_filter_with_mixin(self):
65+
# Mixin FilterSets should not error when no model is provided. See:
66+
# https://github.com/philipn/django-rest-framework-filters/issues/82
67+
class Mixin(FilterSet):
68+
title = filters.AllLookupsFilter()
69+
70+
class Actual(Mixin):
71+
class Meta:
72+
model = Note
73+
74+
GET = {'title__contains': 'Test'}
75+
f = Actual(GET, queryset=Note.objects.all())
76+
self.assertEqual(len(list(f.qs)), 4)
77+
78+
79+
class RelatedFilterTests(TestCase):
80+
81+
@classmethod
82+
def setUpTestData(cls):
83+
#######################
84+
# Create users
85+
#######################
86+
user1 = User.objects.create(username="user1", email="user1@example.org")
87+
user2 = User.objects.create(username="user2", email="user2@example.org")
88+
89+
#######################
90+
# Create notes
91+
#######################
92+
note1 = Note.objects.create(title="Test 1", content="Test content 1", author=user1)
93+
note2 = Note.objects.create(title="Test 2", content="Test content 2", author=user1)
94+
Note.objects.create(title="Hello Test 3", content="Test content 3", author=user1)
95+
note4 = Note.objects.create(title="Hello Test 4", content="Test content 4", author=user2)
96+
97+
#######################
98+
# Create posts
99+
#######################
100+
post1 = Post.objects.create(note=note1, content="Test content in post 1")
101+
Post.objects.create(note=note2, content="Test content in post 2")
102+
post3 = Post.objects.create(note=note4, content="Test content in post 3")
103+
104+
#######################
105+
# Create covers
106+
#######################
107+
Cover.objects.create(post=post1, comment="Cover 1")
108+
Cover.objects.create(post=post3, comment="Cover 2")
109+
110+
# #######################
111+
# # Create pages
112+
# #######################
113+
# page1 = Page.objects.create(title="First page", content="First first.")
114+
# Page.objects.create(title="Second page", content="Second second.", previous_page=page1)
115+
116+
################################
117+
# ManyToMany
118+
################################
119+
t1 = Tag.objects.create(name="park")
120+
Tag.objects.create(name="lake")
121+
t3 = Tag.objects.create(name="house")
122+
123+
blogpost = BlogPost.objects.create(title="First post", content="First")
124+
set_many(blogpost, 'tags', [t1, t3])
125+
126+
blogpost = BlogPost.objects.create(title="Second post", content="Secon")
127+
set_many(blogpost, 'tags', [t3])
128+
129+
################################
130+
# Recursive relations
131+
################################
132+
a = A.objects.create(title="A1")
133+
b = B.objects.create(name="B1")
134+
c = C.objects.create(title="C1")
135+
136+
c.a = a
137+
c.save()
138+
139+
a.b = b
140+
a.save()
141+
142+
A.objects.create(title="A2")
143+
C.objects.create(title="C2")
144+
C.objects.create(title="C3")
145+
146+
john = Person.objects.create(name="John")
147+
Person.objects.create(name="Mark", best_friend=john)
148+
149+
def test_relatedfilter(self):
150+
# Test that the default exact filter works
151+
GET = {'author': User.objects.get(username='user2').pk}
152+
f = NoteFilterWithRelated(GET, queryset=Note.objects.all())
153+
self.assertEqual(len(list(f.qs)), 1)
154+
self.assertEqual(list(f.qs)[0].title, "Hello Test 4")
155+
156+
# Test the username filter on the related UserFilter set.
157+
GET = {'author__username': 'user2'}
158+
f = NoteFilterWithRelated(GET, queryset=Note.objects.all())
159+
self.assertEqual(len(list(f.qs)), 1)
160+
self.assertEqual(list(f.qs)[0].title, "Hello Test 4")
161+
162+
def test_relatedfilter_for_related_alllookups(self):
163+
# ensure that filters work for AllLookupsFilter across a RelatedFilter.
164+
165+
# Test that the default exact filter works
166+
GET = {'author': User.objects.get(username='user2').pk}
167+
f = NoteFilterWithRelatedAll(GET, queryset=Note.objects.all())
168+
self.assertEqual(len(list(f.qs)), 1)
169+
note = list(f.qs)[0]
170+
self.assertEqual(note.title, "Hello Test 4")
171+
172+
# Test the username filter on the related UserFilter set.
173+
GET = {'author__username': 'user2'}
174+
f = NoteFilterWithRelatedAll(GET, queryset=Note.objects.all())
175+
self.assertEqual(len(list(f.qs)), 1)
176+
self.assertEqual(list(f.qs)[0].title, "Hello Test 4")
177+
178+
GET = {'author__username__endswith': '2'}
179+
f = NoteFilterWithRelatedAll(GET, queryset=Note.objects.all())
180+
self.assertEqual(len(list(f.qs)), 1)
181+
self.assertEqual(list(f.qs)[0].title, "Hello Test 4")
182+
183+
GET = {'author__username__endswith': '1'}
184+
f = NoteFilterWithRelatedAll(GET, queryset=Note.objects.all())
185+
self.assertEqual(len(list(f.qs)), 3)
186+
187+
GET = {'author__username__contains': 'user'}
188+
f = NoteFilterWithRelatedAll(GET, queryset=Note.objects.all())
189+
self.assertEqual(len(list(f.qs)), 4)
190+
191+
def test_relatedfilter_for_related_alllookups_and_different_filter_name(self):
192+
# Test that the default exact filter works
193+
GET = {
194+
'writer': User.objects.get(username='user2').pk,
195+
}
196+
f = NoteFilterWithRelatedAllDifferentFilterName(GET, queryset=Note.objects.all())
197+
self.assertEqual(len(list(f.qs)), 1)
198+
note = list(f.qs)[0]
199+
self.assertEqual(note.title, "Hello Test 4")
200+
201+
# Test the username filter on the related UserFilter set.
202+
GET = {'writer__username': 'user2'}
203+
f = NoteFilterWithRelatedAllDifferentFilterName(GET, queryset=Note.objects.all())
204+
self.assertEqual(len(list(f.qs)), 1)
205+
self.assertEqual(list(f.qs)[0].title, "Hello Test 4")
206+
207+
GET = {'writer__username__endswith': '2'}
208+
f = NoteFilterWithRelatedAllDifferentFilterName(GET, queryset=Note.objects.all())
209+
self.assertEqual(len(list(f.qs)), 1)
210+
self.assertEqual(list(f.qs)[0].title, "Hello Test 4")
211+
212+
GET = {'writer__username__endswith': '1'}
213+
f = NoteFilterWithRelatedAllDifferentFilterName(GET, queryset=Note.objects.all())
214+
self.assertEqual(len(list(f.qs)), 3)
215+
216+
GET = {'writer__username__contains': 'user'}
217+
f = NoteFilterWithRelatedAllDifferentFilterName(GET, queryset=Note.objects.all())
218+
self.assertEqual(len(list(f.qs)), 4)
219+
220+
def test_relatedfilter_different_name(self):
221+
# Test the name filter on the related UserFilter set.
222+
GET = {
223+
'author__name': 'user2',
224+
}
225+
f = NoteFilterWithRelatedDifferentName(GET, queryset=Note.objects.all())
226+
self.assertEqual(len(list(f.qs)), 1)
227+
note = list(f.qs)[0]
228+
self.assertEqual(note.title, "Hello Test 4")
229+
230+
def test_double_relation_filter(self):
231+
GET = {
232+
'note__author__username__endswith': 'user2'
233+
}
234+
f = PostFilter(GET, queryset=Post.objects.all())
235+
self.assertEqual(len(list(f.qs)), 1)
236+
post = list(f.qs)[0]
237+
self.assertEqual(post.content, "Test content in post 3")
238+
239+
def test_triple_relation_filter(self):
240+
GET = {
241+
'post__note__author__username__endswith': 'user2'
242+
}
243+
f = CoverFilterWithRelated(GET, queryset=Cover.objects.all())
244+
self.assertEqual(len(list(f.qs)), 1)
245+
cover = list(f.qs)[0]
246+
self.assertEqual(cover.comment, "Cover 2")
247+
248+
def test_indirect_recursive_relation(self):
249+
GET = {
250+
'a__b__name__endswith': '1'
251+
}
252+
f = CFilter(GET, queryset=C.objects.all())
253+
self.assertEqual(len(list(f.qs)), 1)
254+
c = list(f.qs)[0]
255+
self.assertEqual(c.title, "C1")
256+
257+
def test_direct_recursive_relation(self):
258+
GET = {
259+
'best_friend__name__endswith': 'hn'
260+
}
261+
f = PersonFilter(GET, queryset=Person.objects.all())
262+
self.assertEqual(len(list(f.qs)), 1)
263+
p = list(f.qs)[0]
264+
self.assertEqual(p.name, "Mark")
265+
266+
def test_m2m_relation(self):
267+
GET = {
268+
'tags__name__endswith': 'ark',
269+
}
270+
f = BlogPostFilter(GET, queryset=BlogPost.objects.all())
271+
self.assertEqual(len(list(f.qs)), 1)
272+
p = list(f.qs)[0]
273+
self.assertEqual(p.title, "First post")
274+
275+
GET = {
276+
'tags__name__endswith': 'ouse',
277+
}
278+
f = BlogPostFilter(GET, queryset=BlogPost.objects.all())
279+
self.assertEqual(len(list(f.qs)), 2)
280+
titles = set([person.title for person in f.qs])
281+
self.assertEqual(titles, set(["First post", "Second post"]))
282+
283+
def test_nonexistent_related_field(self):
284+
"""
285+
Invalid filter keys (including those on related filters) are invalid
286+
and should be ignored.
287+
288+
Related: https://github.com/philipn/django-rest-framework-filters/issues/58
289+
"""
290+
GET = {
291+
'author__nonexistent': 'foobar',
292+
}
293+
f = NoteFilterWithRelated(GET, queryset=Note.objects.all())
294+
self.assertEqual(len(list(f.qs)), 4)
295+
296+
GET = {
297+
'author__nonexistent__isnull': 'foobar',
298+
}
299+
f = NoteFilterWithRelated(GET, queryset=Note.objects.all())
300+
self.assertEqual(len(list(f.qs)), 4)
301+
302+
def test_related_filters_caching(self):
303+
filters = PostFilter.related_filters
304+
305+
self.assertEqual(len(filters), 1)
306+
self.assertIn('note', filters)
307+
self.assertIn('_related_filters', PostFilter.__dict__)
308+
309+
# subset should not use parent's cached related filters.
310+
PostSubset = PostFilter.get_subset(['title'])
311+
self.assertNotIn('_related_filters', PostSubset.__dict__)
312+
313+
filters = PostSubset.related_filters
314+
self.assertIn('_related_filters', PostFilter.__dict__)
315+
316+
self.assertEqual(len(filters), 0)
317+
318+
# ensure subsets don't interact
319+
PostSubset = PostFilter.get_subset(['note'])
320+
self.assertNotIn('_related_filters', PostSubset.__dict__)
321+
322+
filters = PostSubset.related_filters
323+
self.assertIn('_related_filters', PostFilter.__dict__)
324+
325+
self.assertEqual(len(filters), 1)

0 commit comments

Comments
 (0)