mirror of
https://github.com/postgres/pgweb.git
synced 2025-08-05 18:34:52 +00:00

Sync up to the same version we have on the commitfest app, which will also be required for eventual django 1.11 support.
159 lines
4.7 KiB
Python
159 lines
4.7 KiB
Python
"Base classes for lookup creation."
|
|
from __future__ import unicode_literals
|
|
|
|
import operator
|
|
import re
|
|
from functools import reduce
|
|
|
|
from django.conf import settings
|
|
from django.core.paginator import Paginator, InvalidPage, EmptyPage
|
|
from django.core.urlresolvers import reverse
|
|
from django.http import JsonResponse
|
|
from django.db.models import Q, Model
|
|
from django.utils.encoding import smart_text
|
|
from django.utils.html import conditional_escape
|
|
from django.utils.translation import ugettext as _
|
|
|
|
from selectable.forms import BaseLookupForm
|
|
|
|
|
|
__all__ = (
|
|
'LookupBase',
|
|
'ModelLookup',
|
|
)
|
|
|
|
|
|
class LookupBase(object):
|
|
"Base class for all django-selectable lookups."
|
|
|
|
form = BaseLookupForm
|
|
response = JsonResponse
|
|
|
|
def _name(cls):
|
|
app_name = cls.__module__.split('.')[-2].lower()
|
|
class_name = cls.__name__.lower()
|
|
name = '%s-%s' % (app_name, class_name)
|
|
return name
|
|
name = classmethod(_name)
|
|
|
|
def _url(cls):
|
|
return reverse('selectable-lookup', args=[cls.name()])
|
|
url = classmethod(_url)
|
|
|
|
def get_query(self, request, term):
|
|
return []
|
|
|
|
def get_item_label(self, item):
|
|
return smart_text(item)
|
|
|
|
def get_item_id(self, item):
|
|
return smart_text(item)
|
|
|
|
def get_item_value(self, item):
|
|
return smart_text(item)
|
|
|
|
def get_item(self, value):
|
|
return value
|
|
|
|
def create_item(self, value):
|
|
raise NotImplemented()
|
|
|
|
def format_item(self, item):
|
|
"Construct result dictionary for the match item."
|
|
result = {
|
|
'id': self.get_item_id(item),
|
|
'value': self.get_item_value(item),
|
|
'label': self.get_item_label(item),
|
|
}
|
|
for key in settings.SELECTABLE_ESCAPED_KEYS:
|
|
if key in result:
|
|
result[key] = conditional_escape(result[key])
|
|
return result
|
|
|
|
def paginate_results(self, results, options):
|
|
"Return a django.core.paginator.Page of results."
|
|
limit = options.get('limit', settings.SELECTABLE_MAX_LIMIT)
|
|
paginator = Paginator(results, limit)
|
|
page = options.get('page', 1)
|
|
try:
|
|
results = paginator.page(page)
|
|
except (EmptyPage, InvalidPage):
|
|
results = paginator.page(paginator.num_pages)
|
|
return results
|
|
|
|
def results(self, request):
|
|
"Match results to given term and return the serialized HttpResponse."
|
|
results = {}
|
|
form = self.form(request.GET)
|
|
if form.is_valid():
|
|
options = form.cleaned_data
|
|
term = options.get('term', '')
|
|
raw_data = self.get_query(request, term)
|
|
results = self.format_results(raw_data, options)
|
|
return self.response(results)
|
|
|
|
def format_results(self, raw_data, options):
|
|
'''
|
|
Returns a python structure that later gets serialized.
|
|
raw_data
|
|
full list of objects matching the search term
|
|
options
|
|
a dictionary of the given options
|
|
'''
|
|
page_data = self.paginate_results(raw_data, options)
|
|
results = {}
|
|
meta = options.copy()
|
|
meta['more'] = _('Show more results')
|
|
if page_data and page_data.has_next():
|
|
meta['next_page'] = page_data.next_page_number()
|
|
if page_data and page_data.has_previous():
|
|
meta['prev_page'] = page_data.previous_page_number()
|
|
results['data'] = [self.format_item(item) for item in page_data.object_list]
|
|
results['meta'] = meta
|
|
return results
|
|
|
|
|
|
class ModelLookup(LookupBase):
|
|
"Lookup class for easily defining lookups based on Django models."
|
|
|
|
model = None
|
|
filters = {}
|
|
search_fields = ()
|
|
|
|
def get_query(self, request, term):
|
|
qs = self.get_queryset()
|
|
if term:
|
|
search_filters = []
|
|
if self.search_fields:
|
|
for field in self.search_fields:
|
|
search_filters.append(Q(**{field: term}))
|
|
qs = qs.filter(reduce(operator.or_, search_filters))
|
|
return qs
|
|
|
|
def get_queryset(self):
|
|
qs = self.model._default_manager.get_queryset()
|
|
if self.filters:
|
|
qs = qs.filter(**self.filters)
|
|
return qs
|
|
|
|
def get_item_id(self, item):
|
|
return item.pk
|
|
|
|
def get_item(self, value):
|
|
item = None
|
|
if value:
|
|
value = value.pk if isinstance(value, Model) else value
|
|
try:
|
|
item = self.get_queryset().get(pk=value)
|
|
except (ValueError, self.model.DoesNotExist):
|
|
item = None
|
|
return item
|
|
|
|
def create_item(self, value):
|
|
data = {}
|
|
if self.search_fields:
|
|
field_name = re.sub(r'__\w+$', '', self.search_fields[0])
|
|
if field_name:
|
|
data = {field_name: value}
|
|
return self.model(**data)
|