Update django version in requirements.txt
[pgweb/local.git] / dep / django-selectable / selectable / base.py
blobbc74204ce5190fa50e495003e272194d39a19b6e
1 "Base classes for lookup creation."
2 from __future__ import unicode_literals
4 import operator
5 import re
6 from functools import reduce
8 from django.conf import settings
9 from django.core.paginator import Paginator, InvalidPage, EmptyPage
10 from django.core.urlresolvers import reverse
11 from django.http import JsonResponse
12 from django.db.models import Q, Model
13 from django.utils.encoding import smart_text
14 from django.utils.html import conditional_escape
15 from django.utils.translation import ugettext as _
17 from selectable.forms import BaseLookupForm
20 __all__ = (
21 'LookupBase',
22 'ModelLookup',
26 class LookupBase(object):
27 "Base class for all django-selectable lookups."
29 form = BaseLookupForm
30 response = JsonResponse
32 def _name(cls):
33 app_name = cls.__module__.split('.')[-2].lower()
34 class_name = cls.__name__.lower()
35 name = '%s-%s' % (app_name, class_name)
36 return name
37 name = classmethod(_name)
39 def _url(cls):
40 return reverse('selectable-lookup', args=[cls.name()])
41 url = classmethod(_url)
43 def get_query(self, request, term):
44 return []
46 def get_item_label(self, item):
47 return smart_text(item)
49 def get_item_id(self, item):
50 return smart_text(item)
52 def get_item_value(self, item):
53 return smart_text(item)
55 def get_item(self, value):
56 return value
58 def create_item(self, value):
59 raise NotImplemented()
61 def format_item(self, item):
62 "Construct result dictionary for the match item."
63 result = {
64 'id': self.get_item_id(item),
65 'value': self.get_item_value(item),
66 'label': self.get_item_label(item),
68 for key in settings.SELECTABLE_ESCAPED_KEYS:
69 if key in result:
70 result[key] = conditional_escape(result[key])
71 return result
73 def paginate_results(self, results, options):
74 "Return a django.core.paginator.Page of results."
75 limit = options.get('limit', settings.SELECTABLE_MAX_LIMIT)
76 paginator = Paginator(results, limit)
77 page = options.get('page', 1)
78 try:
79 results = paginator.page(page)
80 except (EmptyPage, InvalidPage):
81 results = paginator.page(paginator.num_pages)
82 return results
84 def results(self, request):
85 "Match results to given term and return the serialized HttpResponse."
86 results = {}
87 form = self.form(request.GET)
88 if form.is_valid():
89 options = form.cleaned_data
90 term = options.get('term', '')
91 raw_data = self.get_query(request, term)
92 results = self.format_results(raw_data, options)
93 return self.response(results)
95 def format_results(self, raw_data, options):
96 '''
97 Returns a python structure that later gets serialized.
98 raw_data
99 full list of objects matching the search term
100 options
101 a dictionary of the given options
103 page_data = self.paginate_results(raw_data, options)
104 results = {}
105 meta = options.copy()
106 meta['more'] = _('Show more results')
107 if page_data and page_data.has_next():
108 meta['next_page'] = page_data.next_page_number()
109 if page_data and page_data.has_previous():
110 meta['prev_page'] = page_data.previous_page_number()
111 results['data'] = [self.format_item(item) for item in page_data.object_list]
112 results['meta'] = meta
113 return results
116 class ModelLookup(LookupBase):
117 "Lookup class for easily defining lookups based on Django models."
119 model = None
120 filters = {}
121 search_fields = ()
123 def get_query(self, request, term):
124 qs = self.get_queryset()
125 if term:
126 search_filters = []
127 if self.search_fields:
128 for field in self.search_fields:
129 search_filters.append(Q(**{field: term}))
130 qs = qs.filter(reduce(operator.or_, search_filters))
131 return qs
133 def get_queryset(self):
134 qs = self.model._default_manager.get_queryset()
135 if self.filters:
136 qs = qs.filter(**self.filters)
137 return qs
139 def get_item_id(self, item):
140 return item.pk
142 def get_item(self, value):
143 item = None
144 if value:
145 value = value.pk if isinstance(value, Model) else value
146 try:
147 item = self.get_queryset().get(pk=value)
148 except (ValueError, self.model.DoesNotExist):
149 item = None
150 return item
152 def create_item(self, value):
153 data = {}
154 if self.search_fields:
155 field_name = re.sub(r'__\w+$', '', self.search_fields[0])
156 if field_name:
157 data = {field_name: value}
158 return self.model(**data)