Skip to content

Commit

Permalink
Impooved cacheing to reduce pickling issues
Browse files Browse the repository at this point in the history
Not not the entire widget gets cached nor the entire queryset.
The query and gets pickeld separately.
  • Loading branch information
codingjoe committed Oct 1, 2015
1 parent dc81c2b commit 75c8c38
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 12 deletions.
25 changes: 22 additions & 3 deletions django_select2/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,25 @@ def __init__(self, *args, **kwargs):
defaults.update(kwargs)
super(ModelSelect2Mixin, self).__init__(*args, **defaults)

def filter_queryset(self, term):
def set_to_cache(self):
"""
Add widget's attributes to Djnago's cache.
Split the queryset, to not pickle the result set.
"""
queryset = self.get_queryset()
cache.set(self._get_cache_key(), {
'queryset':
[
queryset.none(),
queryset.query,
],
'cls': self.__class__,
'search_fields': self.search_fields,
'max_results': self.max_results,
})

def filter_queryset(self, term, queryset=None):
"""
Return queryset filtered by search_fields matching the passed term.
Expand All @@ -309,15 +327,16 @@ def filter_queryset(self, term):
:return: Filtered queryset
:rtype: :class:`.django.db.models.QuerySet`
"""
qs = self.get_queryset()
if not queryset:
queryset = self.get_queryset()
search_fields = self.get_search_fields()
select = Q()
term = term.replace('\t', ' ')
term = term.replace('\n', ' ')
for t in [t for t in term.split(' ') if not t == '']:
select &= reduce(lambda x, y: x | Q(**{y: t}), search_fields,
Q(**{search_fields.pop(): t}))
return qs.filter(select).distinct()
return queryset.filter(select).distinct()

def get_queryset(self):
"""
Expand Down
12 changes: 8 additions & 4 deletions django_select2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def get(self, request, *args, **kwargs):

def get_queryset(self):
"""Get queryset from cached widget."""
return self.widget.filter_queryset(self.term)
return self.widget.filter_queryset(self.term, self.queryset)

def get_paginate_by(self, queryset):
"""Paginate response by size of widget's `max_results` parameter."""
Expand All @@ -76,7 +76,11 @@ def get_widget_or_404(self):
raise Http404('Invalid "field_id".')
else:
cache_key = '%s%s' % (settings.SELECT2_CACHE_PREFIX, key)
widget = cache.get(cache_key)
if widget is None:
widget_dict = cache.get(cache_key)
if widget_dict is None:
raise Http404('field_id not found')
return widget
qs, qs.query = widget_dict.pop('queryset')
self.queryset = qs.all()
widget_dict['queryset'] = self.queryset
widget_cls = widget_dict.pop('cls')
return widget_cls(**widget_dict)
14 changes: 9 additions & 5 deletions tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ def test_model_kwarg(self):
assert result.exists()

def test_queryset_kwarg(self):
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
genre = Genre.objects.last()
result = widget.filter_queryset(genre.title)
assert result.exists()

def test_ajax_view_registration(self, client):
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
widget.render('name', 'value')
url = reverse('django_select2-json')
genre = Genre.objects.last()
Expand All @@ -178,16 +178,20 @@ def test_ajax_view_registration(self, client):
assert genre.pk in [result['id'] for result in data['results']]

def test_render(self):
widget = ModelSelect2Widget()
widget = ModelSelect2Widget(queryset=Genre.objects.all())
widget.render('name', 'value')
cached_widget = cache.get(widget._get_cache_key())
assert isinstance(cached_widget, ModelSelect2Widget)
assert cached_widget['max_results'] == widget.max_results
assert cached_widget['search_fields'] == widget.search_fields
qs = widget.get_queryset()
assert isinstance(cached_widget['queryset'][0], qs.__class__)
assert text_type(cached_widget['queryset'][1]) == text_type(qs.query)


class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):

def test_tag_attrs(self):
widget = ModelSelect2TagWidget(queryset=Genre.objects, search_fields=['title__icontains'])
widget = ModelSelect2TagWidget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
output = widget.render('name', 'value')
assert 'data-minimum-input-length="1"' in output
assert 'data-tags="true"' in output
Expand Down

0 comments on commit 75c8c38

Please sign in to comment.