Added tag 0-5-20090711p1 for changeset 03aa33e8600b
"""Form Widget classes specific to the Django admin site."""import copyfrom django import formsfrom django.forms.widgets import RadioFieldRendererfrom django.forms.util import flatattfrom django.utils.text import truncate_wordsfrom django.utils.translation import ugettext as _from django.utils.safestring import mark_safefrom django.utils.encoding import force_unicodefrom django.conf import settingsclass FilteredSelectMultiple(forms.SelectMultiple): """ A SelectMultiple with a JavaScript filter interface. Note that the resulting JavaScript assumes that the jsi18n catalog has been loaded in the page """ class Media: js = (settings.ADMIN_MEDIA_PREFIX + "js/core.js", settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js", settings.ADMIN_MEDIA_PREFIX + "js/SelectFilter2.js") 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'<script type="text/javascript">addEvent(window, "load", function(e) {') # TODO: "id_" is hard-coded here. This should instead use the correct # API to determine the ID dynamically. output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\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'<p class="datetime">%s %s<br />%s %s</p>' % \ (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))class AdminRadioFieldRenderer(RadioFieldRenderer): def render(self): """Outputs a <ul> for this set of radio fields.""" return mark_safe(u'<ul%s>\n%s\n</ul>' % ( flatatt(self.attrs), u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])) )class AdminRadioSelect(forms.RadioSelect): renderer = AdminRadioFieldRendererclass 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 and hasattr(value, "url"): output.append('%s <a target="_blank" href="%s">%s</a> <br />%s ' % \ (_('Currently:'), value.url, 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 <select> box. """ def __init__(self, rel, attrs=None): self.rel = rel super(ForeignKeyRawIdWidget, self).__init__(attrs) def render(self, name, value, attrs=None): if attrs is None: attrs = {} related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower()) params = self.url_parameters() if params: url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) else: url = '' if not attrs.has_key('class'): attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook. output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] # TODO: "id_" is hard-coded here. This should instead use the correct # API to determine the ID dynamically. output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \ (related_url, url, name)) output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup'))) if value: output.append(self.label_for_value(value)) return mark_safe(u''.join(output)) def base_url_parameters(self): params = {} if self.rel.limit_choices_to: items = [] for k, v in self.rel.limit_choices_to.items(): if isinstance(v, list): v = [str(x) for x in v] else: v = str(v) items.append((k, ','.join(v))) params.update(dict(items)) return params def url_parameters(self): from django.contrib.admin.views.main import TO_FIELD_VAR params = self.base_url_parameters() params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) return params def label_for_value(self, value): key = self.rel.get_related_field().name obj = self.rel.to.objects.get(**{key: value}) return ' <strong>%s</strong>' % truncate_words(obj, 14)class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): """ A Widget for displaying ManyToMany ids in the "raw_id" interface rather than in a <select multiple> 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 url_parameters(self): return self.base_url_parameters() 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 Falseclass 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 _media(self): return self.widget.media media = property(_media) 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'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \ (related_url, name)) output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another'))) 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_)class AdminTextareaWidget(forms.Textarea): def __init__(self, attrs=None): final_attrs = {'class': 'vLargeTextField'} if attrs is not None: final_attrs.update(attrs) super(AdminTextareaWidget, self).__init__(attrs=final_attrs)class AdminTextInputWidget(forms.TextInput): def __init__(self, attrs=None): final_attrs = {'class': 'vTextField'} if attrs is not None: final_attrs.update(attrs) super(AdminTextInputWidget, self).__init__(attrs=final_attrs)class AdminURLFieldWidget(forms.TextInput): def __init__(self, attrs=None): final_attrs = {'class': 'vURLField'} if attrs is not None: final_attrs.update(attrs) super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)class AdminIntegerFieldWidget(forms.TextInput): def __init__(self, attrs=None): final_attrs = {'class': 'vIntegerField'} if attrs is not None: final_attrs.update(attrs) super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput): def __init__(self, attrs=None): final_attrs = {'class': 'vCommaSeparatedIntegerField'} if attrs is not None: final_attrs.update(attrs) super(AdminCommaSeparatedIntegerFieldWidget, self).__init__(attrs=final_attrs)