""" Form Widget classes specific to the Django admin site. """ import copy from django import forms from django.forms.widgets import RadioFieldRenderer from django.forms.util import flatatt from django.utils.text import truncate_words from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe from django.utils.encoding import force_unicode from django.conf import settings class FilteredSelectMultiple(forms.SelectMultiple): """ A SelectMultiple with a JavaScript filter interface. Note that the resulting JavaScript assumes that the SelectFilter2.js library and its dependencies have been loaded in the HTML page. """ def __init__(self, verbose_name, is_stacked, attrs=None, choices=()): self.verbose_name = verbose_name self.is_stacked = is_stacked super(FilteredSelectMultiple, self).__init__(attrs, choices) def render(self, name, value, attrs=None, choices=()): output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)] output.append(u'\n' % \ (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX)) return mark_safe(u''.join(output)) class AdminDateWidget(forms.TextInput): class Media: js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") def __init__(self, attrs={}): super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}) class AdminTimeWidget(forms.TextInput): class Media: js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js", settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js") def __init__(self, attrs={}): super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}) class AdminSplitDateTime(forms.SplitDateTimeWidget): """ A SplitDateTime Widget that has some admin-specific styling. """ def __init__(self, attrs=None): widgets = [AdminDateWidget, AdminTimeWidget] # Note that we're calling MultiWidget, not SplitDateTimeWidget, because # we want to define widgets. forms.MultiWidget.__init__(self, widgets, attrs) def format_output(self, rendered_widgets): return mark_safe(u'

%s %s
%s %s

' % \ (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1])) class AdminRadioFieldRenderer(RadioFieldRenderer): def render(self): """Outputs a ' % ( flatatt(self.attrs), u'\n'.join([u'
  • %s
  • ' % force_unicode(w) for w in self])) ) class AdminRadioSelect(forms.RadioSelect): renderer = AdminRadioFieldRenderer class AdminFileWidget(forms.FileInput): """ A FileField Widget that shows its current value if it has one. """ def __init__(self, attrs={}): super(AdminFileWidget, self).__init__(attrs) def render(self, name, value, attrs=None): output = [] if value: output.append('%s %s
    %s ' % \ (_('Currently:'), settings.MEDIA_URL, value, value, _('Change:'))) output.append(super(AdminFileWidget, self).render(name, value, attrs)) return mark_safe(u''.join(output)) class ForeignKeyRawIdWidget(forms.TextInput): """ A Widget for displaying ForeignKeys in the "raw_id" interface rather than in a box. """ def __init__(self, rel, attrs=None): super(ManyToManyRawIdWidget, self).__init__(rel, attrs) def render(self, name, value, attrs=None): attrs['class'] = 'vManyToManyRawIdAdminField' if value: value = ','.join([str(v) for v in value]) else: value = '' return super(ManyToManyRawIdWidget, self).render(name, value, attrs) def label_for_value(self, value): return '' def value_from_datadict(self, data, files, name): value = data.get(name, None) if value and ',' in value: return data[name].split(',') if value: return [value] return None def _has_changed(self, initial, data): if initial is None: initial = [] if data is None: data = [] if len(initial) != len(data): return True for pk1, pk2 in zip(initial, data): if force_unicode(pk1) != force_unicode(pk2): return True return False class RelatedFieldWidgetWrapper(forms.Widget): """ This class is a wrapper to a given widget to add the add icon for the admin interface. """ def __init__(self, widget, rel, admin_site): self.is_hidden = widget.is_hidden self.needs_multipart_form = widget.needs_multipart_form self.attrs = widget.attrs self.choices = widget.choices self.widget = widget self.rel = rel # so we can check if the related object is registered with this AdminSite self.admin_site = admin_site def __deepcopy__(self, memo): obj = copy.copy(self) obj.widget = copy.deepcopy(self.widget, memo) obj.attrs = self.widget.attrs memo[id(self)] = obj return obj def render(self, name, value, *args, **kwargs): rel_to = self.rel.to related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower()) self.widget.choices = self.choices output = [self.widget.render(name, value, *args, **kwargs)] if rel_to in self.admin_site._registry: # If the related object has an admin interface: # TODO: "id_" is hard-coded here. This should instead use the correct # API to determine the ID dynamically. output.append(u' ' % \ (related_url, name)) output.append(u'Add Another' % settings.ADMIN_MEDIA_PREFIX) return mark_safe(u''.join(output)) def build_attrs(self, extra_attrs=None, **kwargs): "Helper function for building an attribute dictionary." self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs) return self.attrs def value_from_datadict(self, data, files, name): return self.widget.value_from_datadict(data, files, name) def _has_changed(self, initial, data): return self.widget._has_changed(initial, data) def id_for_label(self, id_): return self.widget.id_for_label(id_)