Source code for autocomplete_light.widgets

from django.utils import simplejson
from django import forms
from django.forms.util import flatatt
from django.utils import safestring
from django.template.loader import render_to_string

__all__ = ['AutocompleteWidget']


[docs]class AutocompleteWidget(forms.SelectMultiple): """ Widget suitable for ModelChoiceField and ModelMultipleChoiceField. Example usage:: from django import forms import autocomplete_light from models import Author class AuthorsForm(forms.Form): lead_author = forms.ModelChoiceField(Author.objects.all(), widget= autocomplete_light.AutocompleteWidget( 'AuthorChannel', max_items=1)) contributors = forms.ModelMultipleChoiceField(Author.objects.all(), widget=autocomplete_light.AutocompleteWidget('AuthorChannel')) """ payload = {} def __init__(self, channel_name, *args, **kwargs): """ AutocompleteWidget constructor decorates SelectMultiple constructor Arguments: channel_name -- the name of the channel that this widget should use. Keyword arguments are passed to javascript via data attributes of the autocomplete wrapper element: max_items The number of items that this autocomplete allows. If set to 0, then it allows any number of selected items like a multiple select, well suited for ManyToMany relations or any kind of ModelMultipleChoiceField. If set to 3 for example, then it will only allow 3 selected items. It should be set to 1 if the widget is for a ModelChoiceField or ForeignKey, in that case it would be like a normal select. Default is 0. min_characters The minimum number of characters before the autocomplete box shows up. If set to 2 for example, then the autocomplete box will show up when the input receives the second character, for example 'ae'. If set to 0, then the autocomplete box will show up as soon as the input is focused, even if it's empty, behaving like a normal select. Default is 0. bootstrap The name of the bootstrap kind. By default, deck.js will only initialize decks for wrappers that have data-bootstrap="normal". If you want to implement your own bootstrapping logic in javascript, then you set bootstrap to anything that is not "normal". By default, its value is copied from channel.bootstrap. placeholder The initial value of the autocomplete input field. It can be something like 'type your search here'. By default, it is copied from channel.placeholder. payload A dict of data that will be exported to JSON, and parsed into the Deck instance in javascript. It allows to pass variables from Python to Javascript. """ self.channel_name = channel_name from autocomplete_light import registry self.channel = registry[channel_name]() self.payload.update(kwargs.pop('payload', {})) self.max_items = kwargs.pop('max_items', 0) self.min_characters = kwargs.pop('min_characters', 0) self.bootstrap = kwargs.pop('bootstrap', self.channel.bootstrap) self.placeholder = kwargs.pop('placeholder', self.channel.placeholder) super(AutocompleteWidget, self).__init__(*args, **kwargs)
[docs] def render(self, name, value, attrs=None): """ Render the autocomplete widget. It will try two templates, like django admin: - autocomplete_light/channelname/widget.html - autocomplete_light/widget.html Note that it will not pass 'value' to the template, because 'value' might be a list of model ids in the case of ModelMultipleChoiceField, or a model id in the case of ModelChoiceField. To keep things simple, it will just pass a list, 'values', to the template context. """ final_attrs = self.build_attrs(attrs) self.html_id = final_attrs.pop('id', name) if value and not isinstance(value, (list, tuple)): values = [value] else: values = value if values and not self.channel.are_valid(values): raise forms.ValidationError('%s cannot validate %s' % ( self.channel_name, values)) self.payload.update(self.as_dict()) self.payload['channel'] = self.channel.as_dict() channel_name = self.channel_name.lower() return safestring.mark_safe(render_to_string([ 'autocomplete_light/%s/widget.html' % channel_name, 'autocomplete_light/widget.html', ], { 'widget': self, 'name': name, 'values': values, 'channel': self.channel, 'results': self.channel.get_results(values or []), 'json_payload': safestring.mark_safe(simplejson.dumps( self.payload)), 'extra_attrs': safestring.mark_safe(flatatt(final_attrs)), } ))
def as_dict(self): return { 'max_items': self.max_items, 'min_characters': self.min_characters, 'bootstrap': self.bootstrap, # cast to unicode as it might be a gettext proxy 'placeholder': unicode(self.placeholder), } # we might want to split up in two widgets for that ... is it necessary ? # apparently not yet, but maybe at next django release
[docs] def value_from_datadict(self, data, files, name): """Route to Select if max_items is 1, else route to SelectMultiple""" if self.max_items == 1: return forms.Select.value_from_datadict(self, data, files, name) else: return forms.SelectMultiple.value_from_datadict(self, data, files, name)
def _has_changed(self, initial, data): """Route to Select if max_items is 1, else route to SelectMultiple""" if self.max_items == 1: return forms.Select._has_changed(self, initial, data) else: return forms.SelectMultiple._has_changed(self, initial, data)

Project Versions