mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-03-27 10:20:24 +00:00
Why are you validating that term is filled? If I want to suggest values immediately after opening select2 input, I specify select2_options {"minimumResultsForSearch": 0, "minimumInputLength": 0,}.
Why isn't this valid case? Maybe I'm missing something, but see it very useful to suggest, for example, last used values immediately after opening select2 field.
Thanks!
194 lines
6.7 KiB
Python
194 lines
6.7 KiB
Python
import json
|
|
|
|
from django.http import HttpResponse
|
|
from django.views.generic import View
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import Http404
|
|
|
|
from .util import get_field, is_valid_id
|
|
|
|
NO_ERR_RESP = 'nil'
|
|
"""
|
|
Equals to 'nil' constant.
|
|
|
|
Use this in :py:meth:`.Select2View.get_results` to mean no error, instead of hardcoding 'nil' value.
|
|
"""
|
|
|
|
class JSONResponseMixin(object):
|
|
"""
|
|
A mixin that can be used to render a JSON response.
|
|
"""
|
|
response_class = HttpResponse
|
|
|
|
def render_to_response(self, context, **response_kwargs):
|
|
"""
|
|
Returns a JSON response, transforming 'context' to make the payload.
|
|
"""
|
|
response_kwargs['content_type'] = 'application/json'
|
|
return self.response_class(
|
|
self.convert_context_to_json(context),
|
|
**response_kwargs
|
|
)
|
|
|
|
def convert_context_to_json(self, context):
|
|
"Convert the context dictionary into a JSON object"
|
|
return json.dumps(context)
|
|
|
|
class Select2View(JSONResponseMixin, View):
|
|
"""
|
|
Base view which is designed to respond with JSON to Ajax queries from heavy widgets/fields.
|
|
|
|
Although the widgets won't enforce the type of data_view it gets, but it is recommended to
|
|
sub-class this view instead of creating a Django view from scratch.
|
|
|
|
.. note:: Only `GET <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3>`_ Http requests are supported.
|
|
"""
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
try:
|
|
self.check_all_permissions(request, *args, **kwargs)
|
|
except Exception, e:
|
|
return self.respond_with_exception(e)
|
|
return super(Select2View, self).dispatch(request, *args, **kwargs)
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if request.method == 'GET':
|
|
term = request.GET.get('term', None)
|
|
if term is None:
|
|
return self.render_to_response(self._results_to_context(('missing term', False, [], )))
|
|
|
|
try:
|
|
page = int(request.GET.get('page', None))
|
|
if page <= 0:
|
|
page = -1
|
|
except ValueError:
|
|
page = -1
|
|
if page == -1:
|
|
return self.render_to_response(self._results_to_context(('bad page no.', False, [], )))
|
|
context = request.GET.get('context', None)
|
|
else:
|
|
return self.render_to_response(self._results_to_context(('not a get request', False, [], )))
|
|
|
|
return self.render_to_response(
|
|
self._results_to_context(
|
|
self.get_results(request, term, page, context)
|
|
)
|
|
)
|
|
|
|
def respond_with_exception(self, e):
|
|
"""
|
|
:param e: Exception object.
|
|
:type e: Exception
|
|
:return: Response with status code of 404 if e is ``Http404`` object,
|
|
else 400.
|
|
:rtype: HttpResponse
|
|
"""
|
|
if isinstance(e, Http404):
|
|
status = 404
|
|
else:
|
|
status = getattr(e, 'status_code', 400)
|
|
return self.render_to_response(
|
|
self._results_to_context((str(e), False, [],)),
|
|
status=status
|
|
)
|
|
|
|
def _results_to_context(self, output):
|
|
err, has_more, results = output
|
|
res = []
|
|
if err == NO_ERR_RESP:
|
|
for result in results:
|
|
id_, text = result[:2]
|
|
if len(result)>2:
|
|
extra_data = result[2]
|
|
else:
|
|
extra_data = {}
|
|
res.append(dict(id=id_, text=text, **extra_data))
|
|
return {
|
|
'err': err,
|
|
'more': has_more,
|
|
'results': res,
|
|
}
|
|
|
|
def check_all_permissions(self, request, *args, **kwargs):
|
|
"""
|
|
Sub-classes can use this to raise exception on permission check failures,
|
|
or these checks can be placed in ``urls.py``, e.g. ``login_required(SelectClass.as_view())``.
|
|
|
|
:param request: The Ajax request object.
|
|
:type request: :py:class:`django.http.HttpRequest`
|
|
|
|
:param args: The ``*args`` passed to :py:meth:`django.views.generic.View.dispatch`.
|
|
:param kwargs: The ``**kwargs`` passed to :py:meth:`django.views.generic.View.dispatch`.
|
|
|
|
.. warning:: Sub-classes should override this. You really do not want random people making
|
|
Http reqeusts to your server, be able to get access to sensitive information.
|
|
"""
|
|
pass
|
|
|
|
def get_results(self, request, term, page, context):
|
|
"""
|
|
Returns the result for the given search ``term``.
|
|
|
|
:param request: The Ajax request object.
|
|
:type request: :py:class:`django.http.HttpRequest`
|
|
|
|
:param term: The search term.
|
|
:type term: :py:obj:`str`
|
|
|
|
:param page: The page number. If in your last response you had signalled that there are more results,
|
|
then when user scrolls more a new Ajax request would be sent for the same term but with next page
|
|
number. (Page number starts at 1)
|
|
:type page: :py:obj:`int`
|
|
|
|
:param context: Can be anything which persists across the lifecycle of queries for the same search term.
|
|
It is reset to ``None`` when the term changes.
|
|
|
|
.. note:: Currently this is not used by ``heavy_data.js``.
|
|
:type context: :py:obj:`str` or None
|
|
|
|
Expected output is of the form::
|
|
|
|
(err, has_more, [results])
|
|
|
|
Where ``results = [(id1, text1), (id2, text2), ...]``
|
|
|
|
For example::
|
|
|
|
('nil', False,
|
|
[
|
|
(1, 'Value label1'),
|
|
(20, 'Value label2'),
|
|
])
|
|
|
|
When everything is fine then the `err` must be 'nil'.
|
|
`has_more` should be true if there are more rows.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
|
|
class AutoResponseView(Select2View):
|
|
"""
|
|
A central view meant to respond to Ajax queries for all Heavy widgets/fields.
|
|
Although it is not mandatory to use, but is immensely helpful.
|
|
|
|
.. tip:: Fields which want to use this view must sub-class :py:class:`~.widgets.AutoViewFieldMixin`.
|
|
"""
|
|
def check_all_permissions(self, request, *args, **kwargs):
|
|
id_ = request.GET.get('field_id', None)
|
|
if id_ is None or not is_valid_id(id_):
|
|
raise Http404('field_id not found or is invalid')
|
|
field = get_field(id_)
|
|
if field is None:
|
|
raise Http404('field_id not found')
|
|
|
|
if not field.security_check(request, *args, **kwargs):
|
|
raise PermissionDenied('permission denied')
|
|
|
|
request.__django_select2_local = field
|
|
|
|
def get_results(self, request, term, page, context):
|
|
field = request.__django_select2_local
|
|
del request.__django_select2_local
|
|
return field.get_results(request, term, page, context)
|
|
|
|
|