mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-03-17 05:50:23 +00:00
173 lines
5.9 KiB
Python
173 lines
5.9 KiB
Python
class AutoViewFieldMixin(object):
|
|
"""Registers itself with AutoResponseView."""
|
|
def __init__(self, *args, **kwargs):
|
|
name = self.__class__.__name__
|
|
from .util import register_field
|
|
if name not in ['AutoViewFieldMixin', 'AutoModelSelect2Field']:
|
|
id_ = register_field("%s.%s" % (self.__module__, name), self)
|
|
self.widget.field_id = id_
|
|
super(AutoViewFieldMixin, self).__init__(*args, **kwargs)
|
|
|
|
def security_check(self, request, *args, **kwargs):
|
|
return True
|
|
|
|
def get_results(self, request, term, page, context):
|
|
raise NotImplemented
|
|
|
|
import copy
|
|
|
|
from django import forms
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.utils.encoding import smart_unicode
|
|
from django.db.models import Q
|
|
from django.core.validators import EMPTY_VALUES
|
|
|
|
from .widgets import Select2Widget, Select2MultipleWidget,\
|
|
HeavySelect2Widget, HeavySelect2MultipleWidget, AutoHeavySelect2Widget
|
|
from .views import NO_ERR_RESP
|
|
|
|
class ModelResultJsonMixin(object):
|
|
|
|
def __init__(self, **kwargs):
|
|
if self.queryset is None:
|
|
raise ValueError('queryset is required.')
|
|
|
|
if not self.search_fields:
|
|
raise ValueError('search_fields is required.')
|
|
|
|
self.max_results = getattr(self, 'max_results', None)
|
|
self.to_field_name = getattr(self, 'to_field_name', 'pk')
|
|
|
|
super(ModelResultJsonMixin, self).__init__(**kwargs)
|
|
|
|
def label_from_instance(self, obj):
|
|
return smart_unicode(obj)
|
|
|
|
def prepare_qs_params(self, request, search_term, search_fields):
|
|
q = None
|
|
for field in search_fields:
|
|
kwargs = {}
|
|
kwargs[field] = search_term
|
|
if q is None:
|
|
q = Q(**kwargs)
|
|
else:
|
|
q = q | Q(**kwargs)
|
|
return {'or': [q], 'and': {},}
|
|
|
|
def get_results(self, request, term, page, context):
|
|
qs = copy.deepcopy(self.queryset)
|
|
params = self.prepare_qs_params(request, term, self.search_fields)
|
|
|
|
if self.max_results:
|
|
min_ = (page - 1) * self.max_results
|
|
max_ = min_ + self.max_results + 1 # fetching one extra row to check if it has more rows.
|
|
res = list(qs.filter(*params['or'], **params['and'])[min_:max_])
|
|
has_more = len(res) == (max_ - min_)
|
|
if has_more:
|
|
res = res[:-1]
|
|
else:
|
|
res = list(qs.filter(*params['or'], **params['and']))
|
|
has_more = False
|
|
|
|
res = [ (getattr(obj, self.to_field_name), self.label_from_instance(obj), ) for obj in res ]
|
|
return (NO_ERR_RESP, has_more, res, )
|
|
|
|
class ModelValueMixin(object):
|
|
default_error_messages = {
|
|
'invalid_choice': _(u'Select a valid choice. That choice is not one of'
|
|
u' the available choices.'),
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
if self.queryset is None:
|
|
raise ValueError('queryset is required.')
|
|
|
|
self.to_field_name = getattr(self, 'to_field_name', 'pk')
|
|
|
|
super(ModelValueMixin, self).__init__(**kwargs)
|
|
|
|
def to_python(self, value):
|
|
if value in EMPTY_VALUES:
|
|
return None
|
|
try:
|
|
key = self.to_field_name
|
|
value = self.queryset.get(**{key: value})
|
|
except (ValueError, self.queryset.model.DoesNotExist):
|
|
raise ValidationError(self.error_messages['invalid_choice'])
|
|
return value
|
|
|
|
class Select2ChoiceField(forms.ChoiceField):
|
|
widget = Select2Widget
|
|
|
|
class Select2MultipleChoiceField(forms.ChoiceField):
|
|
widget = Select2MultipleWidget
|
|
|
|
class HeavySelect2FieldBase(forms.Field):
|
|
def __init__(self, **kwargs):
|
|
data_view = kwargs.pop('data_view', None)
|
|
kargs = {}
|
|
|
|
if data_view is not None:
|
|
kargs['widget'] = self.widget(data_view=data_view)
|
|
elif kwargs.get('widget', None) is None:
|
|
raise ValueError('data_view is required else you need to provide your own widget instance.')
|
|
|
|
kargs.update(kwargs)
|
|
super(HeavySelect2FieldBase, self).__init__(**kargs)
|
|
|
|
class HeavySelect2ChoiceField(HeavySelect2FieldBase):
|
|
widget = HeavySelect2Widget
|
|
|
|
class HeavySelect2MultipleChoiceField(HeavySelect2FieldBase):
|
|
widget = HeavySelect2MultipleWidget
|
|
|
|
class AutoSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, HeavySelect2ChoiceField):
|
|
"""
|
|
This needs to be subclassed. The first instance of a class (sub-class) is used to serve all incoming
|
|
json query requests for that type (class).
|
|
"""
|
|
|
|
widget = AutoHeavySelect2Widget
|
|
|
|
def __init__(self, **kwargs):
|
|
self.data_view = "django_select2_central_json"
|
|
kwargs['data_view'] = self.data_view
|
|
super(AutoSelect2Field, self).__init__(**kwargs)
|
|
|
|
class AutoModelSelect2Field(ModelResultJsonMixin, AutoViewFieldMixin, ModelValueMixin, HeavySelect2ChoiceField):
|
|
"""
|
|
This needs to be subclassed. The first instance of a class (sub-class) is used to serve all incoming
|
|
json query requests for that type (class).
|
|
"""
|
|
|
|
widget = AutoHeavySelect2Widget
|
|
|
|
def __init__(self, **kwargs):
|
|
self.data_view = "django_select2_central_json"
|
|
kwargs['data_view'] = self.data_view
|
|
super(AutoModelSelect2Field, self).__init__(**kwargs)
|
|
|
|
class ModelSelect2Field(ModelValueMixin, Select2ChoiceField):
|
|
def __init__(self, **kwargs):
|
|
self.queryset = kwargs.pop('queryset', None)
|
|
self.to_field_name = kwargs.pop('to_field_name', 'pk')
|
|
|
|
choices = kwargs.pop('choices', None)
|
|
if choices is None:
|
|
choices = []
|
|
for obj in self.queryset.all():
|
|
choices.append((getattr(obj, self.to_field_name), smart_unicode(obj), ))
|
|
|
|
kwargs['choices'] = choices
|
|
|
|
super(ModelSelect2Field, self).__init__(**kwargs)
|
|
|
|
def valid_value(self, value):
|
|
val = getattr(value, self.to_field_name)
|
|
for k, v in self.choices:
|
|
if k == val:
|
|
return True
|
|
return False
|
|
|
|
|
|
|