Source code for autocomplete_light.autocomplete.generic

from __future__ import unicode_literals

from autocomplete_light.fields import GenericModelChoiceField
import six

from ..settings import DEFAULT_SEARCH_FIELDS
from .model import AutocompleteModel

__all__ = ['AutocompleteGeneric']


class AutocompleteGenericMetaClass(type):
    def __new__(cls, name, bases, attrs):
        new_class = super(AutocompleteGenericMetaClass, cls).__new__(cls, name,
                bases, attrs)

        if attrs.get('__module__',
                '').startswith('autocomplete_light.autocomplete'):
            # We are defining one of our own classes.
            return new_class

        if not new_class.search_fields:
            default = DEFAULT_SEARCH_FIELDS
            new_class.search_fields = [default] * len(new_class.choices)

        return new_class


[docs]class AutocompleteGeneric(six.with_metaclass(AutocompleteGenericMetaClass, AutocompleteModel)): """ :py:class:`~.model.AutocompleteModel` extension which considers choices as a **list of querysets**, and composes a choice value with both the content type pk and the actual model pk. .. py:attribute:: choices A list of querysets. Example:: choices = ( User.objects.all(), Group.objects.all(), ) .. py:attribute:: search_fields A list of lists of fields to search in, configurable like on ModelAdmin.search_fields. The first list of fields will be used for the first queryset in choices and so on. Example:: search_fields = ( ('email', '^name'), # Used for User.objects.all() ('name',) # User for Group.objects.all() ) AutocompleteGeneric inherits from :py:class:`.model.AutocompleteModel` and supports :py:attr:`~.model.AutocompleteModel.limit_choices` and :py:attr:`~.model.AutocompleteModel.split_words` exactly like AutocompleteModel. However :py:attr:`~.model.AutocompleteModel.order_by` is not supported (yet) in AutocompleteGeneric. """ choices = None search_fields = None
[docs] def choice_value(self, choice): """ Rely on :py:class:`~autocomplete_light.generic.GenericModelChoiceField` to return a string containing the content type id and object id of the result. """ field = GenericModelChoiceField() return field.prepare_value(choice)
[docs] def validate_values(self): """ Ensure that every choice is part of a queryset in :py:attr:`choices`. """ assert self.choices, 'autocomplete.choices should be a queryset list' for value in self.values: if not isinstance(value, six.string_types): return False try: content_type_id, object_id = value.split('-', 1) except ValueError: return False from django.contrib.contenttypes.models import ContentType try: content_type = ContentType.objects.get_for_id(content_type_id) except ContentType.DoesNotExist: return False model_class = content_type.model_class() found = False for queryset in self.choices: if queryset.model != model_class: continue if queryset.filter(pk=object_id).count() == 1: found = True else: return False if not found: # maybe a user would cheat by using a forbidden ctype id ! return False return True
[docs] def choices_for_request(self): """ Return a list of choices from every queryset in :py:attr:`choices`. """ assert self.choices, 'autocomplete.choices should be a queryset list' q = self.request.GET.get('q', '') request_choices = [] querysets_left = len(self.choices) i = 0 for queryset in self.choices: conditions = self._choices_for_request_conditions(q, self.search_fields[i]) limit = ((self.limit_choices - len(request_choices)) / querysets_left) for choice in queryset.filter(conditions)[:limit]: request_choices.append(choice) querysets_left -= 1 i += 1 return request_choices
[docs] def choices_for_values(self): """ Values which are not found in any querysets of :py:attr:`choices` are ignored. """ values_choices = [] for queryset in self.choices: from django.contrib.contenttypes.models import ContentType ctype = ContentType.objects.get_for_model( queryset.model, for_concrete_model=False).pk try: ids = [x.split('-')[1] for x in self.values if x is not None and int(x.split('-')[0]) == ctype] except ValueError: continue for choice in queryset.filter(pk__in=ids): values_choices.append(choice) return values_choices