1 "Base classes for lookup creation."
2 from __future__
import unicode_literals
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
26 class LookupBase(object):
27 "Base class for all django-selectable lookups."
30 response
= JsonResponse
33 app_name
= cls
.__module
__.split('.')[-2].lower()
34 class_name
= cls
.__name
__.lower()
35 name
= '%s-%s' % (app_name
, class_name
)
37 name
= classmethod(_name
)
40 return reverse('selectable-lookup', args
=[cls
.name()])
41 url
= classmethod(_url
)
43 def get_query(self
, request
, term
):
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
):
58 def create_item(self
, value
):
59 raise NotImplemented()
61 def format_item(self
, item
):
62 "Construct result dictionary for the match item."
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
:
70 result
[key
] = conditional_escape(result
[key
])
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)
79 results
= paginator
.page(page
)
80 except (EmptyPage
, InvalidPage
):
81 results
= paginator
.page(paginator
.num_pages
)
84 def results(self
, request
):
85 "Match results to given term and return the serialized HttpResponse."
87 form
= self
.form(request
.GET
)
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
):
97 Returns a python structure that later gets serialized.
99 full list of objects matching the search term
101 a dictionary of the given options
103 page_data
= self
.paginate_results(raw_data
, options
)
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
116 class ModelLookup(LookupBase
):
117 "Lookup class for easily defining lookups based on Django models."
123 def get_query(self
, request
, term
):
124 qs
= self
.get_queryset()
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
))
133 def get_queryset(self
):
134 qs
= self
.model
._default
_manager
.get_queryset()
136 qs
= qs
.filter(**self
.filters
)
139 def get_item_id(self
, item
):
142 def get_item(self
, value
):
145 value
= value
.pk
if isinstance(value
, Model
) else value
147 item
= self
.get_queryset().get(pk
=value
)
148 except (ValueError, self
.model
.DoesNotExist
):
152 def create_item(self
, value
):
154 if self
.search_fields
:
155 field_name
= re
.sub(r
'__\w+$', '', self
.search_fields
[0])
157 data
= {field_name
: value
}
158 return self
.model(**data
)