django-searchkit 1.0__py3-none-any.whl → 1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. build/lib/build/lib/build/lib/example/example/__init__.py +0 -0
  2. build/lib/build/lib/build/lib/example/example/admin.py +16 -0
  3. build/lib/build/lib/build/lib/example/example/asgi.py +16 -0
  4. build/lib/build/lib/build/lib/example/example/management/__init__.py +0 -0
  5. build/lib/build/lib/build/lib/example/example/management/commands/__init__.py +0 -0
  6. build/lib/build/lib/build/lib/example/example/management/commands/createtestdata.py +62 -0
  7. build/lib/build/lib/build/lib/example/example/migrations/0001_initial.py +48 -0
  8. build/lib/build/lib/build/lib/example/example/migrations/__init__.py +0 -0
  9. build/lib/build/lib/build/lib/example/example/models.py +38 -0
  10. build/lib/build/lib/build/lib/example/example/settings.py +125 -0
  11. build/lib/build/lib/build/lib/example/example/urls.py +23 -0
  12. build/lib/build/lib/build/lib/example/example/wsgi.py +16 -0
  13. build/lib/build/lib/build/lib/searchkit/__init__.py +0 -0
  14. build/lib/build/lib/build/lib/searchkit/__version__.py +16 -0
  15. build/lib/build/lib/build/lib/searchkit/admin.py +30 -0
  16. build/lib/build/lib/build/lib/searchkit/apps.py +6 -0
  17. build/lib/build/lib/build/lib/searchkit/filters.py +28 -0
  18. build/lib/build/lib/build/lib/searchkit/forms/__init__.py +5 -0
  19. build/lib/build/lib/build/lib/searchkit/forms/fields.py +55 -0
  20. build/lib/build/lib/build/lib/searchkit/forms/search.py +62 -0
  21. build/lib/build/lib/build/lib/searchkit/forms/searchkit.py +154 -0
  22. build/lib/build/lib/build/lib/searchkit/forms/utils.py +178 -0
  23. build/lib/build/lib/build/lib/searchkit/migrations/0001_initial.py +30 -0
  24. build/lib/build/lib/build/lib/searchkit/migrations/0002_rename_searchkitsearch_search.py +18 -0
  25. build/lib/build/lib/build/lib/searchkit/migrations/__init__.py +0 -0
  26. build/lib/build/lib/build/lib/searchkit/models.py +21 -0
  27. build/lib/build/lib/build/lib/searchkit/templatetags/__init__.py +0 -0
  28. build/lib/build/lib/build/lib/searchkit/templatetags/searchkit.py +20 -0
  29. build/lib/build/lib/build/lib/searchkit/tests.py +400 -0
  30. build/lib/build/lib/build/lib/searchkit/urls.py +7 -0
  31. build/lib/build/lib/build/lib/searchkit/views.py +23 -0
  32. build/lib/build/lib/example/example/__init__.py +0 -0
  33. build/lib/build/lib/example/example/admin.py +16 -0
  34. build/lib/build/lib/example/example/asgi.py +16 -0
  35. build/lib/build/lib/example/example/management/__init__.py +0 -0
  36. build/lib/build/lib/example/example/management/commands/__init__.py +0 -0
  37. build/lib/build/lib/example/example/management/commands/createtestdata.py +62 -0
  38. build/lib/build/lib/example/example/migrations/0001_initial.py +48 -0
  39. build/lib/build/lib/example/example/migrations/__init__.py +0 -0
  40. build/lib/build/lib/example/example/models.py +38 -0
  41. build/lib/build/lib/example/example/settings.py +125 -0
  42. build/lib/build/lib/example/example/urls.py +23 -0
  43. build/lib/build/lib/example/example/wsgi.py +16 -0
  44. build/lib/build/lib/searchkit/__init__.py +0 -0
  45. build/lib/build/lib/searchkit/__version__.py +16 -0
  46. build/lib/build/lib/searchkit/admin.py +30 -0
  47. build/lib/build/lib/searchkit/apps.py +6 -0
  48. build/lib/build/lib/searchkit/filters.py +28 -0
  49. build/lib/build/lib/searchkit/forms/__init__.py +5 -0
  50. build/lib/build/lib/searchkit/forms/fields.py +55 -0
  51. build/lib/build/lib/searchkit/forms/search.py +62 -0
  52. build/lib/build/lib/searchkit/forms/searchkit.py +154 -0
  53. build/lib/build/lib/searchkit/forms/utils.py +178 -0
  54. build/lib/build/lib/searchkit/migrations/0001_initial.py +30 -0
  55. build/lib/build/lib/searchkit/migrations/0002_rename_searchkitsearch_search.py +18 -0
  56. build/lib/build/lib/searchkit/migrations/__init__.py +0 -0
  57. build/lib/build/lib/searchkit/models.py +21 -0
  58. build/lib/build/lib/searchkit/templatetags/__init__.py +0 -0
  59. build/lib/build/lib/searchkit/templatetags/searchkit.py +20 -0
  60. build/lib/build/lib/searchkit/tests.py +400 -0
  61. build/lib/build/lib/searchkit/urls.py +7 -0
  62. build/lib/build/lib/searchkit/views.py +23 -0
  63. build/lib/example/example/admin.py +1 -1
  64. build/lib/searchkit/__version__.py +1 -1
  65. build/lib/searchkit/admin.py +4 -4
  66. build/lib/searchkit/filters.py +10 -13
  67. build/lib/searchkit/forms/__init__.py +5 -3
  68. build/lib/searchkit/forms/search.py +37 -17
  69. build/lib/searchkit/forms/searchkit.py +60 -95
  70. build/lib/searchkit/forms/utils.py +44 -15
  71. build/lib/searchkit/migrations/0002_rename_searchkitsearch_search.py +18 -0
  72. build/lib/searchkit/models.py +2 -4
  73. build/lib/searchkit/templatetags/searchkit.py +0 -27
  74. build/lib/searchkit/tests.py +200 -50
  75. build/lib/searchkit/urls.py +1 -2
  76. build/lib/searchkit/views.py +11 -18
  77. {django_searchkit-1.0.dist-info → django_searchkit-1.2.dist-info}/METADATA +9 -24
  78. django_searchkit-1.2.dist-info/RECORD +130 -0
  79. example/example/admin.py +1 -1
  80. searchkit/__version__.py +1 -1
  81. searchkit/admin.py +4 -4
  82. searchkit/filters.py +10 -13
  83. searchkit/forms/__init__.py +5 -3
  84. searchkit/forms/search.py +37 -17
  85. searchkit/forms/searchkit.py +60 -95
  86. searchkit/forms/utils.py +44 -15
  87. searchkit/migrations/0002_rename_searchkitsearch_search.py +18 -0
  88. searchkit/models.py +2 -4
  89. searchkit/templatetags/searchkit.py +0 -27
  90. searchkit/tests.py +200 -50
  91. searchkit/urls.py +1 -2
  92. searchkit/views.py +11 -18
  93. django_searchkit-1.0.dist-info/RECORD +0 -66
  94. {django_searchkit-1.0.dist-info → django_searchkit-1.2.dist-info}/WHEEL +0 -0
  95. {django_searchkit-1.0.dist-info → django_searchkit-1.2.dist-info}/licenses/LICENCE +0 -0
  96. {django_searchkit-1.0.dist-info → django_searchkit-1.2.dist-info}/top_level.txt +0 -0
  97. {django_searchkit-1.0.dist-info → django_searchkit-1.2.dist-info}/zip-safe +0 -0
@@ -1,19 +1,29 @@
1
+ from urllib.parse import urlencode
1
2
  from django.test import TestCase
2
3
  from django.contrib.contenttypes.models import ContentType
3
- from django import forms
4
+ from django.contrib.auth.models import User
5
+ from django.urls import reverse
4
6
  from example.models import ModelA
7
+ from example.management.commands.createtestdata import Command as CreateTestData
5
8
  from searchkit.forms.utils import FIELD_PLAN
6
9
  from searchkit.forms.utils import SUPPORTED_FIELDS
7
- from searchkit.forms.utils import SUPPORTED_RELATIONS
8
- from searchkit.forms import SearchkitSearchForm
9
- from searchkit.forms import SearchkitForm
10
- from searchkit.forms import SearchkitFormSet
10
+ from searchkit.forms.utils import ModelTree
11
+ from searchkit.forms import SearchForm
12
+ from searchkit.forms import SearchkitModelForm
13
+ from searchkit.forms import BaseSearchkitFormSet
14
+ from searchkit.forms import searchkit_formset_factory
15
+ from searchkit.models import Search
16
+ from searchkit import __version__
17
+
18
+
19
+ SearchkitFormSet = searchkit_formset_factory(model=ModelA)
20
+ SearchkitForm = SearchkitFormSet.form
11
21
 
12
22
 
13
23
  INITIAL_DATA = [
14
24
  dict(
15
25
  field='model_b__chars',
16
- operator='exact',
26
+ operator='contains',
17
27
  value='anytext',
18
28
  ),
19
29
  dict(
@@ -23,12 +33,12 @@ INITIAL_DATA = [
23
33
  ),
24
34
  dict(
25
35
  field='float',
26
- operator='exact',
36
+ operator='gt',
27
37
  value='0.3',
28
38
  ),
29
39
  dict(
30
40
  field='decimal',
31
- operator='exact',
41
+ operator='lte',
32
42
  value='1.23',
33
43
  ),
34
44
  dict(
@@ -43,20 +53,29 @@ INITIAL_DATA = [
43
53
  )
44
54
  ]
45
55
 
46
- add_prefix = lambda i: SearchkitFormSet(model=ModelA).add_prefix(i)
56
+ add_prefix = lambda i: SearchkitFormSet().add_prefix(i)
57
+ contenttype = ContentType.objects.get_for_model(ModelA)
47
58
  DEFAULT_PREFIX = SearchkitFormSet.get_default_prefix()
48
- FORM_DATA = {
49
- 'name': 'test search', # The name of the search.
50
- f'{DEFAULT_PREFIX}-TOTAL_FORMS': '6', # Data for the managment form.
51
- f'{DEFAULT_PREFIX}-INITIAL_FORMS': '1', # Data for the managment form.
52
- f'{DEFAULT_PREFIX}-contenttype': f'{ContentType.objects.get_for_model(ModelA).pk}',
53
- f'{add_prefix(1)}-value_0': '1', # Data for the range operator.
54
- f'{add_prefix(1)}-value_1': '123', # Data for the range operator.
55
- }
56
- for i, data in enumerate(INITIAL_DATA, 0):
57
- prefix = SearchkitFormSet(model=ModelA).add_prefix(i)
58
- for key, value in data.items():
59
- FORM_DATA.update({f'{prefix}-{key}': value})
59
+
60
+ def get_form_data(initial_data=INITIAL_DATA):
61
+ count = len(initial_data)
62
+ data = {
63
+ 'name': 'test search', # The name of the search.
64
+ 'searchkit_model': f'{contenttype.pk}', # Data for the searchkit-model form.
65
+ f'{DEFAULT_PREFIX}-TOTAL_FORMS': f'{count}', # Data for the managment form.
66
+ f'{DEFAULT_PREFIX}-INITIAL_FORMS': f'{count}', # Data for the managment form.
67
+ }
68
+ for i, d in enumerate(initial_data):
69
+ prefix = SearchkitFormSet().add_prefix(i)
70
+ for key, value in d.items():
71
+ if isinstance(value, list):
72
+ for i, v in enumerate(value):
73
+ data.update({f'{prefix}-{key}_{i}': v})
74
+ else:
75
+ data.update({f'{prefix}-{key}': value})
76
+ return data
77
+
78
+ FORM_DATA = get_form_data()
60
79
 
61
80
 
62
81
  class CheckFormMixin:
@@ -70,22 +89,19 @@ class CheckFormMixin:
70
89
  self.assertIn('value', form.fields)
71
90
  self.assertEqual(len(form.fields), 3)
72
91
 
73
- # Check choices of the model_field.
92
+ # Check field choices for the model.
74
93
  form_model_field = form.fields['field']
75
94
  self.assertTrue(form_model_field.choices)
76
95
  options = [c[0] for c in form_model_field.choices]
77
- for model_field in ModelA._meta.fields:
78
- if isinstance(model_field, tuple(SUPPORTED_FIELDS)):
79
- self.assertIn(model_field.name, options)
80
-
81
- # Check choices for relational lookups.
82
- for model_field in ModelA._meta.fields:
83
- if isinstance(model_field, tuple(SUPPORTED_RELATIONS)):
84
- remote_fields = model_field.remote_field.model._meta.fields
85
- for remote_field in remote_fields:
86
- if isinstance(model_field, tuple(SUPPORTED_FIELDS)):
87
- lookup_path = f'{model_field.name}__{remote_field.name}'
88
- self.assertIn(lookup_path, options)
96
+ tree = ModelTree(ModelA)
97
+ for node in tree.iterate():
98
+ for model_field in node.model._meta.fields:
99
+ if not any(isinstance(model_field, f) for f in SUPPORTED_FIELDS):
100
+ continue
101
+ if node.is_root:
102
+ self.assertIn(model_field.name, options)
103
+ else:
104
+ self.assertIn(f'{node.field_path}__{model_field.name}', options)
89
105
 
90
106
  # Check the field_plan choosen based on the model_field.
91
107
  field_plan = next(iter([p for t, p in FIELD_PLAN.items() if t(form.model_field)]))
@@ -102,7 +118,7 @@ class CheckFormMixin:
102
118
  class SearchkitFormTestCase(CheckFormMixin, TestCase):
103
119
 
104
120
  def test_blank_searchkitform(self):
105
- form = SearchkitForm(ModelA, prefix=add_prefix(0))
121
+ form = SearchkitForm(prefix=add_prefix(0))
106
122
  self.check_form(form)
107
123
 
108
124
  # Form should not be bound or valid.
@@ -113,7 +129,7 @@ class SearchkitFormTestCase(CheckFormMixin, TestCase):
113
129
  data = {
114
130
  f'{add_prefix(0)}-field': 'foobar',
115
131
  }
116
- form = SearchkitForm(ModelA, data, prefix=add_prefix(0))
132
+ form = SearchkitForm(data, prefix=add_prefix(0))
117
133
  self.check_form(form)
118
134
 
119
135
  # Form should be invalid.
@@ -127,7 +143,7 @@ class SearchkitFormTestCase(CheckFormMixin, TestCase):
127
143
  data = {
128
144
  f'{add_prefix(0)}-field': 'integer',
129
145
  }
130
- form = SearchkitForm(ModelA, data, prefix=add_prefix(0))
146
+ form = SearchkitForm(data, prefix=add_prefix(0))
131
147
  self.check_form(form)
132
148
 
133
149
  # Form should be invalid since no value data is provieded.
@@ -138,7 +154,7 @@ class SearchkitFormTestCase(CheckFormMixin, TestCase):
138
154
  f'{add_prefix(0)}-field': 'integer',
139
155
  f'{add_prefix(0)}-operator': 'foobar',
140
156
  }
141
- form = SearchkitForm(ModelA, data, prefix=add_prefix(0))
157
+ form = SearchkitForm(data, prefix=add_prefix(0))
142
158
  self.check_form(form)
143
159
 
144
160
  # Form should be invalid.
@@ -153,7 +169,7 @@ class SearchkitFormTestCase(CheckFormMixin, TestCase):
153
169
  f'{add_prefix(0)}-field': 'integer',
154
170
  f'{add_prefix(0)}-operator': 'exact',
155
171
  }
156
- form = SearchkitForm(ModelA, data, prefix=add_prefix(0))
172
+ form = SearchkitForm(data, prefix=add_prefix(0))
157
173
  self.check_form(form)
158
174
 
159
175
  # Form should be invalid since no value data is provieded.
@@ -165,7 +181,7 @@ class SearchkitFormTestCase(CheckFormMixin, TestCase):
165
181
  f'{add_prefix(0)}-operator': 'exact',
166
182
  f'{add_prefix(0)}-value': '123',
167
183
  }
168
- form = SearchkitForm(ModelA, data, prefix=add_prefix(0))
184
+ form = SearchkitForm(data, prefix=add_prefix(0))
169
185
  self.check_form(form)
170
186
 
171
187
  # Form should be valid.
@@ -177,7 +193,7 @@ class SearchkitFormTestCase(CheckFormMixin, TestCase):
177
193
  f'{add_prefix(0)}-operator': 'exact',
178
194
  f'{add_prefix(0)}-value': 'foobar',
179
195
  }
180
- form = SearchkitForm(ModelA, data, prefix=add_prefix(0))
196
+ form = SearchkitForm(data, prefix=add_prefix(0))
181
197
  self.check_form(form)
182
198
 
183
199
  # Form should be invalid.
@@ -204,7 +220,7 @@ class SearchkitFormSetTestCase(CheckFormMixin, TestCase):
204
220
  def test_searchkit_formset_with_invalid_data(self):
205
221
  data = FORM_DATA.copy()
206
222
  del data[f'{add_prefix(0)}-value']
207
- formset = SearchkitFormSet(data, model=ModelA)
223
+ formset = SearchkitFormSet(data)
208
224
  self.assertFalse(formset.is_valid())
209
225
 
210
226
  # Check error message in html.
@@ -212,7 +228,8 @@ class SearchkitFormSetTestCase(CheckFormMixin, TestCase):
212
228
  self.assertIn(errors, formset.forms[0].errors.values())
213
229
 
214
230
  def test_searchkit_formset_with_initial_data(self):
215
- formset = SearchkitFormSet(initial=INITIAL_DATA, model=ModelA)
231
+ formset_class = searchkit_formset_factory(model=ModelA, extra=0)
232
+ formset = formset_class(initial=INITIAL_DATA)
216
233
  self.assertFalse(formset.is_bound)
217
234
  self.assertFalse(formset.is_valid())
218
235
  self.assertEqual(len(formset.forms), len(INITIAL_DATA))
@@ -223,17 +240,17 @@ class SearchkitFormSetTestCase(CheckFormMixin, TestCase):
223
240
 
224
241
  class SearchkitSearchFormTestCase(TestCase):
225
242
  def test_searchkit_search_form_without_data(self):
226
- form = SearchkitSearchForm()
243
+ form = SearchForm()
227
244
  self.assertFalse(form.is_bound)
228
245
  self.assertFalse(form.is_valid())
229
- self.assertIsInstance(form.formset, SearchkitFormSet)
246
+ self.assertIsInstance(form.formset, BaseSearchkitFormSet)
230
247
  self.assertEqual(form.formset.model, None)
231
248
 
232
249
  def test_searchkit_search_form_with_data(self):
233
- form = SearchkitSearchForm(FORM_DATA)
250
+ form = SearchForm(FORM_DATA)
234
251
  self.assertTrue(form.is_bound)
235
252
  self.assertTrue(form.is_valid())
236
- self.assertIsInstance(form.formset, SearchkitFormSet)
253
+ self.assertIsInstance(form.formset, BaseSearchkitFormSet)
237
254
  self.assertEqual(form.formset.model, ModelA)
238
255
  self.assertEqual(form.instance.data, form.formset.cleaned_data)
239
256
 
@@ -242,9 +259,142 @@ class SearchkitSearchFormTestCase(TestCase):
242
259
  self.assertTrue(form.instance.pk)
243
260
 
244
261
  # Using the instance data as filter rules works.
245
- filter_rules = form.instance.get_filter_rules()
262
+ filter_rules = form.instance.as_lookups()
246
263
  self.assertEqual(len(filter_rules), len(INITIAL_DATA))
247
264
  for data in INITIAL_DATA:
248
265
  self.assertIn(f"{data['field']}__{data['operator']}", filter_rules)
249
- queryset = form.formset.model.objects.filter(**filter_rules)
250
- self.assertTrue(queryset.model == ModelA)
266
+
267
+
268
+ class SearchkitModelFormTestCase(TestCase):
269
+ def test_searchkit_model_form_choices(self):
270
+ form = SearchkitModelForm()
271
+ labels = [c[1] for c in form.fields['searchkit_model'].choices]
272
+ self.assertEqual(len(labels), 3)
273
+ self.assertEqual('select a model', labels[0].lower())
274
+ self.assertEqual('example | model a', labels[1].lower())
275
+ self.assertEqual('example | model b', labels[2].lower())
276
+
277
+
278
+ class AdminBackendTest(TestCase):
279
+ @classmethod
280
+ def setUpTestData(cls):
281
+ CreateTestData().handle()
282
+
283
+ def setUp(self):
284
+ admin = User.objects.get(username='admin')
285
+ self.client.force_login(admin)
286
+
287
+ def test_search_form(self):
288
+ url = reverse('admin:searchkit_search_add')
289
+ resp = self.client.get(url)
290
+ self.assertEqual(resp.status_code, 200)
291
+ select = b'<select name="searchkit_model" class="searchkit-reload-on-change" data-total-forms="1" required id="id_searchkit_model">'
292
+ for snippet in select.split(b' '):
293
+ self.assertIn(snippet, resp.content)
294
+
295
+ def test_search_form_with_initial(self):
296
+ url = reverse('admin:searchkit_search_add') + '?searchkit_model=1'
297
+ resp = self.client.get(url)
298
+ self.assertEqual(resp.status_code, 200)
299
+ select = '<select name="searchkit_model" class="searchkit-reload-on-change" data-total-forms="1" required id="id_searchkit_model">'
300
+ for snippet in select.split(' '):
301
+ self.assertIn(snippet, str(resp.content))
302
+ self.assertIn('<option value="1" selected>', str(resp.content))
303
+ self.assertIn('name="searchkit-example-modela-0-field"', str(resp.content))
304
+
305
+ def test_add_search(self):
306
+ # Create a search object via the admin backend.
307
+ url = reverse('admin:searchkit_search_add')
308
+ data = FORM_DATA.copy()
309
+ data['_save_and_apply'] = True
310
+ resp = self.client.post(url, data, follow=True)
311
+ self.assertEqual(resp.status_code, 200)
312
+ self.assertEqual(len(Search.objects.all()), 1)
313
+
314
+ # Change it via backend.
315
+ url = reverse('admin:searchkit_search_change', args=(1,))
316
+ data['name'] = 'Changed name'
317
+ data['searchkit-example-modela-0-field'] = 'boolean'
318
+ data['searchkit-example-modela-0-operator'] = 'exact'
319
+ data['searchkit-example-modela-0-value'] = 'true'
320
+ resp = self.client.post(url, data, follow=True)
321
+ self.assertEqual(resp.status_code, 200)
322
+ self.assertEqual(Search.objects.get(pk=1).name, data['name'])
323
+
324
+ # Will the search be listed in the admin filter?
325
+ url = reverse('admin:example_modela_changelist')
326
+ resp = self.client.get(url)
327
+ self.assertEqual(resp.status_code, 200)
328
+ self.assertIn('href="?search=1"', str(resp.content))
329
+ self.assertIn(data['name'], str(resp.content))
330
+
331
+
332
+ class SearchViewTest(TestCase):
333
+
334
+ def setUp(self):
335
+ self.initial = [
336
+ dict(
337
+ field='integer',
338
+ operator='exact',
339
+ value=1,
340
+ )
341
+ ]
342
+ self.initial_range = [
343
+ dict(
344
+ field='integer',
345
+ operator='range',
346
+ value=[1,3],
347
+ )
348
+ ]
349
+
350
+ def test_search_view_invalid_data(self):
351
+ initial = self.initial.copy()
352
+ initial[0]['value'] = 'no integer'
353
+ data = get_form_data(initial)
354
+ url_params = urlencode(data)
355
+ base_url = reverse('searchkit_form')
356
+ url = f'{base_url}?{url_params}'
357
+ resp = self.client.get(url)
358
+ self.assertEqual(resp.status_code, 200)
359
+ html_error = '<li>Enter a whole number.</li>'
360
+ self.assertInHTML(html_error, str(resp.content))
361
+
362
+ def test_search_view_missing_data(self):
363
+ initial = self.initial.copy()
364
+ del(initial[0]['value'])
365
+ data = get_form_data(initial)
366
+ url_params = urlencode(data)
367
+ base_url = reverse('searchkit_form')
368
+ url = f'{base_url}?{url_params}'
369
+ resp = self.client.get(url)
370
+ self.assertEqual(resp.status_code, 200)
371
+ html_error = '<li>This field is required.</li>'
372
+ self.assertInHTML(html_error, str(resp.content))
373
+
374
+ def test_search_view_with_range_operator(self):
375
+ data = get_form_data(self.initial_range)
376
+ url_params = urlencode(data)
377
+ base_url = reverse('searchkit_form')
378
+ url = f'{base_url}?{url_params}'
379
+ resp = self.client.get(url)
380
+ self.assertEqual(resp.status_code, 200)
381
+ html = '<input type="number" name="searchkit-example-modela-0-value_1" value="3" id="id_searchkit-example-modela-0-value_1">'
382
+ self.assertInHTML(html, str(resp.content))
383
+
384
+ def test_search_view_with_model(self):
385
+ data = get_form_data(self.initial)
386
+ data['searchkit_model'] = ContentType.objects.get_for_model(ModelA).pk
387
+ url_params = urlencode(data)
388
+ base_url = reverse('searchkit_form')
389
+ url = f'{base_url}?{url_params}'
390
+ resp = self.client.get(url)
391
+ self.assertEqual(resp.status_code, 200)
392
+
393
+ def test_search_view_with_invalid_model(self):
394
+ data = get_form_data(self.initial)
395
+ data['searchkit_model'] = 9999 # Non-existing content type.
396
+ url_params = urlencode(data)
397
+ base_url = reverse('searchkit_form')
398
+ url = f'{base_url}?{url_params}'
399
+ resp = self.client.get(url)
400
+ self.assertEqual(resp.status_code, 400)
@@ -3,6 +3,5 @@ from .views import SearchkitAjaxView
3
3
 
4
4
 
5
5
  urlpatterns = [
6
- path("searchkit/form/", SearchkitAjaxView.as_view(), name="searchkit_form"),
7
- path("searchkit/form/<slug:app_label>/<slug:model_name>/", SearchkitAjaxView.as_view(), name="searchkit_form_model"),
6
+ path("searchkit/", SearchkitAjaxView.as_view(), name="searchkit_form"),
8
7
  ]
@@ -1,9 +1,11 @@
1
1
  from django.utils.translation import gettext_lazy as _
2
2
  from django.http import HttpResponse
3
- from django.http import Http404
3
+ from django.http import Http404, HttpResponseBadRequest
4
4
  from django.apps import apps
5
+ from django.contrib.contenttypes.models import ContentType
5
6
  from django.views.generic import View
6
- from .forms import SearchkitFormSet
7
+ from .forms import SearchkitModelForm
8
+ from .forms import searchkit_formset_factory
7
9
 
8
10
 
9
11
  # FIXME: Check permissions and authentication.
@@ -11,20 +13,11 @@ class SearchkitAjaxView(View):
11
13
  """
12
14
  Reload the formset via ajax.
13
15
  """
14
- def get_model(self, **kwargs):
15
- """
16
- Get the model from the URL parameters.
17
- """
18
- if all(k in kwargs for k in ('app_label', 'model_name')):
19
- app_label, model_name = kwargs['app_label'], kwargs['model_name']
20
- try:
21
- return apps.get_model(app_label=app_label, model_name=model_name)
22
- except LookupError:
23
- raise Http404(_('Model %s.%s not found') % (app_label, model_name))
24
- else:
25
- return None
26
-
27
16
  def get(self, request, **kwargs):
28
- model = self.get_model(**kwargs)
29
- formset = SearchkitFormSet(data=request.GET, model=model)
30
- return HttpResponse(formset.render())
17
+ model_form = SearchkitModelForm(data=self.request.GET)
18
+ if model_form.is_valid():
19
+ model = model_form.cleaned_data['searchkit_model'].model_class()
20
+ formset = searchkit_formset_factory(model=model)(data=request.GET)
21
+ return HttpResponse(formset.render())
22
+ else:
23
+ return HttpResponseBadRequest(_('Invalid searchkit-model-form.'))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-searchkit
3
- Version: 1.0
3
+ Version: 1.2
4
4
  Summary: Finally a real searchkit for django!
5
5
  Home-page: https://github.com/thomst/django-searchkit
6
6
  Author: Thomas Leichtfuß
@@ -9,8 +9,6 @@ License: BSD License
9
9
  Platform: OS Independent
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Framework :: Django
12
- Classifier: Framework :: Django :: 3.1
13
- Classifier: Framework :: Django :: 3.2
14
12
  Classifier: Framework :: Django :: 4.0
15
13
  Classifier: Framework :: Django :: 4.1
16
14
  Classifier: Framework :: Django :: 4.2
@@ -22,8 +20,6 @@ Classifier: License :: OSI Approved :: BSD License
22
20
  Classifier: Operating System :: OS Independent
23
21
  Classifier: Programming Language :: Python
24
22
  Classifier: Programming Language :: Python :: 3
25
- Classifier: Programming Language :: Python :: 3.7
26
- Classifier: Programming Language :: Python :: 3.8
27
23
  Classifier: Programming Language :: Python :: 3.9
28
24
  Classifier: Programming Language :: Python :: 3.10
29
25
  Classifier: Programming Language :: Python :: 3.11
@@ -32,8 +28,9 @@ Classifier: Topic :: Software Development
32
28
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
33
29
  Description-Content-Type: text/markdown
34
30
  License-File: LICENCE
35
- Requires-Dist: Django>=3.1
31
+ Requires-Dist: Django>=4.0
36
32
  Requires-Dist: django-picklefield>=2.0
33
+ Requires-Dist: django-modeltree
37
34
  Dynamic: author
38
35
  Dynamic: author-email
39
36
  Dynamic: classifier
@@ -50,8 +47,8 @@ Dynamic: summary
50
47
 
51
48
  [<img src="https://github.com/thomst/django-searchkit/actions/workflows/ci.yml/badge.svg">](https://github.com/thomst/django-searchkit/)
52
49
  [<img src="https://coveralls.io/repos/github/thomst/django-searchkit/badge.svg?branch=main">](https://coveralls.io/github/thomst/django-searchkit?branch=main)
53
- [<img src="https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-blue">](https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-blue)
54
- [<img src="https://img.shields.io/badge/django-3.1%20%7C%203.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-orange">](https://img.shields.io/badge/django-3.1%20%7C%203.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-orange)
50
+ [<img src="https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11-blue">](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11-blue)
51
+ [<img src="https://img.shields.io/badge/django-4.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-orange">](https://img.shields.io/badge/django-4.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-orange)
55
52
 
56
53
 
57
54
  ## Description
@@ -99,20 +96,8 @@ class MyModelAdmin(admin.ModelAdmin):
99
96
  ## Usage
100
97
 
101
98
  1. Open the admin changelist of your Model.
102
- 2. Click "Add filter" on the Searchkit filter.
103
- 3. Choose the Model you want to filter.
99
+ 2. Click the "Add filter" button of the Searchkit filter.
100
+ 3. Give your Filter a name.
104
101
  4. Configure as many filter rules as you want.
105
- 5. Click "Save and apply"
106
-
107
-
108
- ## TODO
109
-
110
- - Limit the choices of the model field by models that should be searchable.
111
- - Add an apply button to the search edit page to be able to use a search without
112
- saving it.
113
- - Coming from the search edit page the filtering should be done by an id__in url
114
- parameter, not by an search parameter as it is used by the searchkit filter.
115
- - Preselect the right model in the model field when coming from a models
116
- changelist by the "Add filter" button.
117
- - Add a public field for searches and only offer public searches in the
118
- searchkit filter.
102
+ 5. Click "Save and apply".
103
+ 6. Reuse your filter whenever you want using the Searchkit filter section.
@@ -0,0 +1,130 @@
1
+ build/lib/build/lib/build/lib/example/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ build/lib/build/lib/build/lib/example/example/admin.py,sha256=eTZ5mCA1ToGouqYgIf-6xDl2nvYAz7T9lU1Bnu_gP9c,462
3
+ build/lib/build/lib/build/lib/example/example/asgi.py,sha256=mqLnzcCH9X-8UhDo5ZhcM8tF3jtALEGSjsBmphbkLcs,391
4
+ build/lib/build/lib/build/lib/example/example/models.py,sha256=fB9X782cX01Yg2uIiPCVgrS8C7DkgG9U-9mxGtX3P7Q,1097
5
+ build/lib/build/lib/build/lib/example/example/settings.py,sha256=du9bWbAceSvDkYeNYOc_3KxKL1p1se93TdAueT2RQ_E,3256
6
+ build/lib/build/lib/build/lib/example/example/urls.py,sha256=D8LxjiTdJSA87AVh3BzpVo5E2aC30hlp20HshPDrfvU,800
7
+ build/lib/build/lib/build/lib/example/example/wsgi.py,sha256=OAXHHeNQoj7FBDc5NMET39i_kIGpFrcdNDdY1thrh58,391
8
+ build/lib/build/lib/build/lib/example/example/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ build/lib/build/lib/build/lib/example/example/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ build/lib/build/lib/build/lib/example/example/management/commands/createtestdata.py,sha256=IVtyUqak133h9Ua-Ag_GP8mw3dUdiDYAa2tGmLkKluU,2737
11
+ build/lib/build/lib/build/lib/example/example/migrations/0001_initial.py,sha256=62tOi17oHGuqqLmSAYWLep6AMkxCJF6Tp13odghS7gI,2014
12
+ build/lib/build/lib/build/lib/example/example/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ build/lib/build/lib/build/lib/searchkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ build/lib/build/lib/build/lib/searchkit/__version__.py,sha256=g6zhPyD9UelvcYrhtmQjIk-7_RH9Bu2W8YLlyW3M5OM,661
15
+ build/lib/build/lib/build/lib/searchkit/admin.py,sha256=URZgMKnfKlDqOgmQXJHu7mFGrjwkElpunAUbp2_9JcA,1182
16
+ build/lib/build/lib/build/lib/searchkit/apps.py,sha256=17m6wPu5N59Dq4MLoWaQd0aNHEzMT4z4emQD632GIMA,150
17
+ build/lib/build/lib/build/lib/searchkit/filters.py,sha256=l8756ftyURUpK3KSkl__TDP5KpqaapxjYMaAjh729ps,1098
18
+ build/lib/build/lib/build/lib/searchkit/models.py,sha256=0LEe0z3jOTqAGrqNGknDDY1L1anaG3kCcQ_Zm_7dl9I,800
19
+ build/lib/build/lib/build/lib/searchkit/tests.py,sha256=qm8rvKC8aDnaspIXwzCsDwMkfXMR4uOHzqDLWhdv2dA,14849
20
+ build/lib/build/lib/build/lib/searchkit/urls.py,sha256=Mhrq4CUOhrh28ZhRocSP2AYnfWFW2c_eJyMR81q5fps,162
21
+ build/lib/build/lib/build/lib/searchkit/views.py,sha256=ECSwsge2pD0OJLHdHwpOdWS7XiItUX2NFCO8FSjBigE,919
22
+ build/lib/build/lib/build/lib/searchkit/forms/__init__.py,sha256=FBggDa1owkYrceIU3cqNhqsMB49RJWZRAkA4OfvUMcM,207
23
+ build/lib/build/lib/build/lib/searchkit/forms/fields.py,sha256=TpFZdAyWKaMhOoZFTuy5ODNrW6RLy289Lk1fcMSGJ20,1621
24
+ build/lib/build/lib/build/lib/searchkit/forms/search.py,sha256=mca67piLDqKUhXzaKIOztk5Gg8V1XL21n27aIffH88A,2330
25
+ build/lib/build/lib/build/lib/searchkit/forms/searchkit.py,sha256=pg3vw6dMdtaHja-g1Yh72XJkLp7I48nXht9zxWbGW9Y,6022
26
+ build/lib/build/lib/build/lib/searchkit/forms/utils.py,sha256=m8vwB-3AoYuMx0QxlJTwSWbDHBG8MyCxFmsyK6xyai8,5221
27
+ build/lib/build/lib/build/lib/searchkit/migrations/0001_initial.py,sha256=cP886X1UMrDTf-KxfHfuoQ4b1T1lhdNJyVNOGMNyWNw,1093
28
+ build/lib/build/lib/build/lib/searchkit/migrations/0002_rename_searchkitsearch_search.py,sha256=RHXShizf689AdPb2Ca-hONWmwZbB2Fs5DQhXlEPE-f0,389
29
+ build/lib/build/lib/build/lib/searchkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ build/lib/build/lib/build/lib/searchkit/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ build/lib/build/lib/build/lib/searchkit/templatetags/searchkit.py,sha256=HpEGTGYi_enDh_bgTgWdVbZftNuZqRt4J9N5A0Nc1AM,581
32
+ build/lib/build/lib/example/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ build/lib/build/lib/example/example/admin.py,sha256=eTZ5mCA1ToGouqYgIf-6xDl2nvYAz7T9lU1Bnu_gP9c,462
34
+ build/lib/build/lib/example/example/asgi.py,sha256=mqLnzcCH9X-8UhDo5ZhcM8tF3jtALEGSjsBmphbkLcs,391
35
+ build/lib/build/lib/example/example/models.py,sha256=fB9X782cX01Yg2uIiPCVgrS8C7DkgG9U-9mxGtX3P7Q,1097
36
+ build/lib/build/lib/example/example/settings.py,sha256=du9bWbAceSvDkYeNYOc_3KxKL1p1se93TdAueT2RQ_E,3256
37
+ build/lib/build/lib/example/example/urls.py,sha256=D8LxjiTdJSA87AVh3BzpVo5E2aC30hlp20HshPDrfvU,800
38
+ build/lib/build/lib/example/example/wsgi.py,sha256=OAXHHeNQoj7FBDc5NMET39i_kIGpFrcdNDdY1thrh58,391
39
+ build/lib/build/lib/example/example/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ build/lib/build/lib/example/example/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ build/lib/build/lib/example/example/management/commands/createtestdata.py,sha256=IVtyUqak133h9Ua-Ag_GP8mw3dUdiDYAa2tGmLkKluU,2737
42
+ build/lib/build/lib/example/example/migrations/0001_initial.py,sha256=62tOi17oHGuqqLmSAYWLep6AMkxCJF6Tp13odghS7gI,2014
43
+ build/lib/build/lib/example/example/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ build/lib/build/lib/searchkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
+ build/lib/build/lib/searchkit/__version__.py,sha256=g6zhPyD9UelvcYrhtmQjIk-7_RH9Bu2W8YLlyW3M5OM,661
46
+ build/lib/build/lib/searchkit/admin.py,sha256=URZgMKnfKlDqOgmQXJHu7mFGrjwkElpunAUbp2_9JcA,1182
47
+ build/lib/build/lib/searchkit/apps.py,sha256=17m6wPu5N59Dq4MLoWaQd0aNHEzMT4z4emQD632GIMA,150
48
+ build/lib/build/lib/searchkit/filters.py,sha256=l8756ftyURUpK3KSkl__TDP5KpqaapxjYMaAjh729ps,1098
49
+ build/lib/build/lib/searchkit/models.py,sha256=0LEe0z3jOTqAGrqNGknDDY1L1anaG3kCcQ_Zm_7dl9I,800
50
+ build/lib/build/lib/searchkit/tests.py,sha256=qm8rvKC8aDnaspIXwzCsDwMkfXMR4uOHzqDLWhdv2dA,14849
51
+ build/lib/build/lib/searchkit/urls.py,sha256=Mhrq4CUOhrh28ZhRocSP2AYnfWFW2c_eJyMR81q5fps,162
52
+ build/lib/build/lib/searchkit/views.py,sha256=ECSwsge2pD0OJLHdHwpOdWS7XiItUX2NFCO8FSjBigE,919
53
+ build/lib/build/lib/searchkit/forms/__init__.py,sha256=FBggDa1owkYrceIU3cqNhqsMB49RJWZRAkA4OfvUMcM,207
54
+ build/lib/build/lib/searchkit/forms/fields.py,sha256=TpFZdAyWKaMhOoZFTuy5ODNrW6RLy289Lk1fcMSGJ20,1621
55
+ build/lib/build/lib/searchkit/forms/search.py,sha256=mca67piLDqKUhXzaKIOztk5Gg8V1XL21n27aIffH88A,2330
56
+ build/lib/build/lib/searchkit/forms/searchkit.py,sha256=pg3vw6dMdtaHja-g1Yh72XJkLp7I48nXht9zxWbGW9Y,6022
57
+ build/lib/build/lib/searchkit/forms/utils.py,sha256=m8vwB-3AoYuMx0QxlJTwSWbDHBG8MyCxFmsyK6xyai8,5221
58
+ build/lib/build/lib/searchkit/migrations/0001_initial.py,sha256=cP886X1UMrDTf-KxfHfuoQ4b1T1lhdNJyVNOGMNyWNw,1093
59
+ build/lib/build/lib/searchkit/migrations/0002_rename_searchkitsearch_search.py,sha256=RHXShizf689AdPb2Ca-hONWmwZbB2Fs5DQhXlEPE-f0,389
60
+ build/lib/build/lib/searchkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ build/lib/build/lib/searchkit/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ build/lib/build/lib/searchkit/templatetags/searchkit.py,sha256=HpEGTGYi_enDh_bgTgWdVbZftNuZqRt4J9N5A0Nc1AM,581
63
+ build/lib/example/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
+ build/lib/example/example/admin.py,sha256=KDjYyHPEqNdv7Xe8yx0VuWdx-bDn7qKKHM5ZYhFwdU0,445
65
+ build/lib/example/example/asgi.py,sha256=mqLnzcCH9X-8UhDo5ZhcM8tF3jtALEGSjsBmphbkLcs,391
66
+ build/lib/example/example/models.py,sha256=fB9X782cX01Yg2uIiPCVgrS8C7DkgG9U-9mxGtX3P7Q,1097
67
+ build/lib/example/example/settings.py,sha256=du9bWbAceSvDkYeNYOc_3KxKL1p1se93TdAueT2RQ_E,3256
68
+ build/lib/example/example/urls.py,sha256=D8LxjiTdJSA87AVh3BzpVo5E2aC30hlp20HshPDrfvU,800
69
+ build/lib/example/example/wsgi.py,sha256=OAXHHeNQoj7FBDc5NMET39i_kIGpFrcdNDdY1thrh58,391
70
+ build/lib/example/example/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ build/lib/example/example/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
+ build/lib/example/example/management/commands/createtestdata.py,sha256=IVtyUqak133h9Ua-Ag_GP8mw3dUdiDYAa2tGmLkKluU,2737
73
+ build/lib/example/example/migrations/0001_initial.py,sha256=62tOi17oHGuqqLmSAYWLep6AMkxCJF6Tp13odghS7gI,2014
74
+ build/lib/example/example/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
+ build/lib/searchkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
+ build/lib/searchkit/__version__.py,sha256=g6zhPyD9UelvcYrhtmQjIk-7_RH9Bu2W8YLlyW3M5OM,661
77
+ build/lib/searchkit/admin.py,sha256=URZgMKnfKlDqOgmQXJHu7mFGrjwkElpunAUbp2_9JcA,1182
78
+ build/lib/searchkit/apps.py,sha256=17m6wPu5N59Dq4MLoWaQd0aNHEzMT4z4emQD632GIMA,150
79
+ build/lib/searchkit/filters.py,sha256=l8756ftyURUpK3KSkl__TDP5KpqaapxjYMaAjh729ps,1098
80
+ build/lib/searchkit/models.py,sha256=0LEe0z3jOTqAGrqNGknDDY1L1anaG3kCcQ_Zm_7dl9I,800
81
+ build/lib/searchkit/tests.py,sha256=qm8rvKC8aDnaspIXwzCsDwMkfXMR4uOHzqDLWhdv2dA,14849
82
+ build/lib/searchkit/urls.py,sha256=Mhrq4CUOhrh28ZhRocSP2AYnfWFW2c_eJyMR81q5fps,162
83
+ build/lib/searchkit/views.py,sha256=ECSwsge2pD0OJLHdHwpOdWS7XiItUX2NFCO8FSjBigE,919
84
+ build/lib/searchkit/forms/__init__.py,sha256=FBggDa1owkYrceIU3cqNhqsMB49RJWZRAkA4OfvUMcM,207
85
+ build/lib/searchkit/forms/fields.py,sha256=TpFZdAyWKaMhOoZFTuy5ODNrW6RLy289Lk1fcMSGJ20,1621
86
+ build/lib/searchkit/forms/search.py,sha256=mca67piLDqKUhXzaKIOztk5Gg8V1XL21n27aIffH88A,2330
87
+ build/lib/searchkit/forms/searchkit.py,sha256=pg3vw6dMdtaHja-g1Yh72XJkLp7I48nXht9zxWbGW9Y,6022
88
+ build/lib/searchkit/forms/utils.py,sha256=m8vwB-3AoYuMx0QxlJTwSWbDHBG8MyCxFmsyK6xyai8,5221
89
+ build/lib/searchkit/migrations/0001_initial.py,sha256=cP886X1UMrDTf-KxfHfuoQ4b1T1lhdNJyVNOGMNyWNw,1093
90
+ build/lib/searchkit/migrations/0002_rename_searchkitsearch_search.py,sha256=RHXShizf689AdPb2Ca-hONWmwZbB2Fs5DQhXlEPE-f0,389
91
+ build/lib/searchkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
+ build/lib/searchkit/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
+ build/lib/searchkit/templatetags/searchkit.py,sha256=HpEGTGYi_enDh_bgTgWdVbZftNuZqRt4J9N5A0Nc1AM,581
94
+ django_searchkit-1.2.dist-info/licenses/LICENCE,sha256=oqmlRYPA5GHG2T1yp8fPFGQdOO-NnJPdGITeFiJYWLg,1521
95
+ example/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
+ example/example/admin.py,sha256=KDjYyHPEqNdv7Xe8yx0VuWdx-bDn7qKKHM5ZYhFwdU0,445
97
+ example/example/asgi.py,sha256=mqLnzcCH9X-8UhDo5ZhcM8tF3jtALEGSjsBmphbkLcs,391
98
+ example/example/models.py,sha256=fB9X782cX01Yg2uIiPCVgrS8C7DkgG9U-9mxGtX3P7Q,1097
99
+ example/example/settings.py,sha256=du9bWbAceSvDkYeNYOc_3KxKL1p1se93TdAueT2RQ_E,3256
100
+ example/example/urls.py,sha256=D8LxjiTdJSA87AVh3BzpVo5E2aC30hlp20HshPDrfvU,800
101
+ example/example/wsgi.py,sha256=OAXHHeNQoj7FBDc5NMET39i_kIGpFrcdNDdY1thrh58,391
102
+ example/example/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
+ example/example/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
+ example/example/management/commands/createtestdata.py,sha256=IVtyUqak133h9Ua-Ag_GP8mw3dUdiDYAa2tGmLkKluU,2737
105
+ example/example/migrations/0001_initial.py,sha256=62tOi17oHGuqqLmSAYWLep6AMkxCJF6Tp13odghS7gI,2014
106
+ example/example/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
+ searchkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
+ searchkit/__version__.py,sha256=g6zhPyD9UelvcYrhtmQjIk-7_RH9Bu2W8YLlyW3M5OM,661
109
+ searchkit/admin.py,sha256=URZgMKnfKlDqOgmQXJHu7mFGrjwkElpunAUbp2_9JcA,1182
110
+ searchkit/apps.py,sha256=17m6wPu5N59Dq4MLoWaQd0aNHEzMT4z4emQD632GIMA,150
111
+ searchkit/filters.py,sha256=l8756ftyURUpK3KSkl__TDP5KpqaapxjYMaAjh729ps,1098
112
+ searchkit/models.py,sha256=0LEe0z3jOTqAGrqNGknDDY1L1anaG3kCcQ_Zm_7dl9I,800
113
+ searchkit/tests.py,sha256=qm8rvKC8aDnaspIXwzCsDwMkfXMR4uOHzqDLWhdv2dA,14849
114
+ searchkit/urls.py,sha256=Mhrq4CUOhrh28ZhRocSP2AYnfWFW2c_eJyMR81q5fps,162
115
+ searchkit/views.py,sha256=ECSwsge2pD0OJLHdHwpOdWS7XiItUX2NFCO8FSjBigE,919
116
+ searchkit/forms/__init__.py,sha256=FBggDa1owkYrceIU3cqNhqsMB49RJWZRAkA4OfvUMcM,207
117
+ searchkit/forms/fields.py,sha256=TpFZdAyWKaMhOoZFTuy5ODNrW6RLy289Lk1fcMSGJ20,1621
118
+ searchkit/forms/search.py,sha256=mca67piLDqKUhXzaKIOztk5Gg8V1XL21n27aIffH88A,2330
119
+ searchkit/forms/searchkit.py,sha256=pg3vw6dMdtaHja-g1Yh72XJkLp7I48nXht9zxWbGW9Y,6022
120
+ searchkit/forms/utils.py,sha256=m8vwB-3AoYuMx0QxlJTwSWbDHBG8MyCxFmsyK6xyai8,5221
121
+ searchkit/migrations/0001_initial.py,sha256=cP886X1UMrDTf-KxfHfuoQ4b1T1lhdNJyVNOGMNyWNw,1093
122
+ searchkit/migrations/0002_rename_searchkitsearch_search.py,sha256=RHXShizf689AdPb2Ca-hONWmwZbB2Fs5DQhXlEPE-f0,389
123
+ searchkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
124
+ searchkit/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
125
+ searchkit/templatetags/searchkit.py,sha256=HpEGTGYi_enDh_bgTgWdVbZftNuZqRt4J9N5A0Nc1AM,581
126
+ django_searchkit-1.2.dist-info/METADATA,sha256=uKoqf1iEWc63SpPhb3cRFIvfY9_J3ziOoPgazA8Dn_w,3325
127
+ django_searchkit-1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
128
+ django_searchkit-1.2.dist-info/top_level.txt,sha256=-4gF42VIaG-ckn2rb2wFa7LhxIZymHBsYVedNOr_NIY,29
129
+ django_searchkit-1.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
130
+ django_searchkit-1.2.dist-info/RECORD,,
example/example/admin.py CHANGED
@@ -7,7 +7,7 @@ from .models import ModelB
7
7
  @admin.register(ModelA)
8
8
  class ModelAAdmin(admin.ModelAdmin):
9
9
  list_display = [f.name for f in ModelA._meta.fields]
10
- list_filter = [SearchkitFilter, 'chars_choices']
10
+ list_filter = [SearchkitFilter]
11
11
 
12
12
 
13
13
  @admin.register(ModelB)
searchkit/__version__.py CHANGED
@@ -13,4 +13,4 @@ Version 0.x should be considered a development version with an unstable API,
13
13
  and backwards compatibility is not guaranteed for minor versions.
14
14
  """
15
15
 
16
- __version__ = "1.0"
16
+ __version__ = "1.2"