diff --git a/edbob/pyramid/forms/formalchemy.py b/edbob/pyramid/forms/formalchemy.py index 3addfdc..220cc61 100644 --- a/edbob/pyramid/forms/formalchemy.py +++ b/edbob/pyramid/forms/formalchemy.py @@ -29,13 +29,16 @@ from __future__ import absolute_import import datetime +import new from pyramid.renderers import render from webhelpers import paginate +from webhelpers.html import tags from webhelpers.html.builder import format_attrs from webhelpers.html.tags import literal import formalchemy +from formalchemy import fields from formalchemy.validators import accepts_none import edbob @@ -46,6 +49,7 @@ from edbob.pyramid import Session __all__ = ['AlchemyGrid', 'ChildGridField', 'PropertyField', 'EnumFieldRenderer', 'PrettyDateTimeFieldRenderer', + 'AutocompleteFieldRenderer', 'make_fieldset', 'required', 'pretty_datetime'] @@ -365,3 +369,59 @@ class PrettyDateTimeFieldRenderer(formalchemy.fields.DateTimeFieldRenderer): def render_readonly(self, **kwargs): return pretty_datetime(self.raw_value) + + +def AutocompleteFieldRenderer(service_url, display, width='300px', callback=None, **kwargs): + """ + Returns a field renderer class which leverages jQuery autocomplete to + provide a more user-friendly experience. This is typically used in place + of a ``SelectFieldRenderer`` when the data set is deemed too large for that + renderer. + + ``service_url`` is required and will ultimately be passed to the + ``jQuery.autocomplete()`` function via the ``serviceUrl`` data parameter. + + ``display`` must be either a callable which accepts an object key as its + only positional argument, or else a tuple of the form ``(Class, 'attr')``. + + ``width`` is optional but is also passed to the jQuery function. + + If ``callback`` is specified, it should be the name of a JavaScript + function within the containing page's scope. This is used in place of + event handlers for autocomplete fields. + """ + + kwargs['service_url'] = service_url + kwargs['width'] = width + kwargs['callback'] = callback + Renderer = new.classobj('AutocompleteFieldRenderer', (_AutocompleteFieldRenderer,), kwargs) + if callable(display): + Renderer.display = classmethod(display) + else: + Renderer.display = display + return Renderer + + +class _AutocompleteFieldRenderer(fields.FieldRenderer): + """ + Implementation for :class:`AutocompleteFieldRenderer` class. + """ + + def _display(self, value): + if callable(self.display): + return self.display(value) + if not value: + return '' + obj = Session.query(self.display[0]).get(value) + if not obj: + return '' + return getattr(obj, self.display[1]) + + def render(self, **kwargs): + autocompleter_name = 'autocompleter_%s' % self.name.replace('-', '_') + return formalchemy.config.engine('field_autocomplete', fieldname=self.name, + fieldvalue=self.value, display=self._display(self.value), + autocompleter_name=autocompleter_name, + service_url=self.service_url, width=self.width, + callback=self.callback, hidden=tags.hidden, text=tags.text, + **kwargs) diff --git a/edbob/pyramid/templates/forms/field_autocomplete.mako b/edbob/pyramid/templates/forms/field_autocomplete.mako new file mode 100644 index 0000000..d65673a --- /dev/null +++ b/edbob/pyramid/templates/forms/field_autocomplete.mako @@ -0,0 +1,26 @@ +