EVOLUTION-MANAGER
Edit File: widgets.py
""" Form Widget classes specific to the Django admin site. """ from __future__ import unicode_literals import copy from django import forms from django.contrib.admin.templatetags.admin_static import static from django.core.urlresolvers import reverse from django.forms.widgets import RadioFieldRenderer from django.forms.util import flatatt from django.utils.html import escape, format_html, format_html_join, smart_urlquote from django.utils.text import Truncator from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe from django.utils.encoding import force_text from django.utils import six class 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 """ @property def media(self): js = ["core.js", "SelectBox.js", "SelectFilter2.js"] return forms.Media(js=[static("admin/js/%s" % path) for path in 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=()): if attrs is None: attrs = {} attrs['class'] = 'selectfilter' if self.is_stacked: attrs['class'] += 'stacked' output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)] output.append('<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('SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/'))) return mark_safe(''.join(output)) class AdminDateWidget(forms.DateInput): @property def media(self): js = ["calendar.js", "admin/DateTimeShortcuts.js"] return forms.Media(js=[static("admin/js/%s" % path) for path in js]) def __init__(self, attrs=None, format=None): final_attrs = {'class': 'vDateField', 'size': '10'} if attrs is not None: final_attrs.update(attrs) super(AdminDateWidget, self).__init__(attrs=final_attrs, format=format) class AdminTimeWidget(forms.TimeInput): @property def media(self): js = ["calendar.js", "admin/DateTimeShortcuts.js"] return forms.Media(js=[static("admin/js/%s" % path) for path in js]) def __init__(self, attrs=None, format=None): final_attrs = {'class': 'vTimeField', 'size': '8'} if attrs is not None: final_attrs.update(attrs) super(AdminTimeWidget, self).__init__(attrs=final_attrs, format=format) 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 format_html('<p class="datetime">{0} {1}<br />{2} {3}</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 format_html('<ul{0}>\n{1}\n</ul>', flatatt(self.attrs), format_html_join('\n', '<li>{0}</li>', ((force_text(w),) for w in self))) class AdminRadioSelect(forms.RadioSelect): renderer = AdminRadioFieldRenderer class AdminFileWidget(forms.ClearableFileInput): template_with_initial = ('<p class="file-upload">%s</p>' % forms.ClearableFileInput.template_with_initial) template_with_clear = ('<span class="clearable-file-input">%s</span>' % forms.ClearableFileInput.template_with_clear) def url_params_from_lookup_dict(lookups): """ Converts the type of lookups specified in a ForeignKey limit_choices_to attribute to a dictionary of query parameters """ params = {} if lookups and hasattr(lookups, 'items'): items = [] for k, v in lookups.items(): if callable(v): v = v() if isinstance(v, (tuple, list)): v = ','.join([str(x) for x in v]) elif isinstance(v, bool): # See django.db.fields.BooleanField.get_prep_lookup v = ('0', '1')[v] else: v = six.text_type(v) items.append((k, v)) params.update(dict(items)) return params class ForeignKeyRawIdWidget(forms.TextInput): """ A Widget for displaying ForeignKeys in the "raw_id" interface rather than in a <select> box. """ def __init__(self, rel, admin_site, attrs=None, using=None): self.rel = rel self.admin_site = admin_site self.db = using super(ForeignKeyRawIdWidget, self).__init__(attrs) def render(self, name, value, attrs=None): rel_to = self.rel.to if attrs is None: attrs = {} extra = [] if rel_to in self.admin_site._registry: # The related object is registered with the same AdminSite related_url = reverse('admin:%s_%s_changelist' % (rel_to._meta.app_label, rel_to._meta.model_name), current_app=self.admin_site.name) params = self.url_parameters() if params: url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) else: url = '' if "class" not in attrs: attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook. # TODO: "lookup_id_" is hard-coded here. This should instead use # the correct API to determine the ID dynamically. extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % (related_url, url, name)) extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>' % (static('admin/img/selector-search.gif'), _('Lookup'))) output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra if value: output.append(self.label_for_value(value)) return mark_safe(''.join(output)) def base_url_parameters(self): return url_params_from_lookup_dict(self.rel.limit_choices_to) 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 try: obj = self.rel.to._default_manager.using(self.db).get(**{key: value}) return ' <strong>%s</strong>' % escape(Truncator(obj).words(14, truncate='...')) except (ValueError, self.rel.to.DoesNotExist): return '' class ManyToManyRawIdWidget(ForeignKeyRawIdWidget): """ A Widget for displaying ManyToMany ids in the "raw_id" interface rather than in a <select multiple> box. """ def render(self, name, value, attrs=None): if attrs is None: attrs = {} if self.rel.to in self.admin_site._registry: # The related object is registered with the same AdminSite attrs['class'] = 'vManyToManyRawIdAdminField' if value: value = ','.join([force_text(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) if value: return value.split(',') 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, can_add_related=None): 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 # Backwards compatible check for whether a user can add related # objects. if can_add_related is None: can_add_related = rel.to in admin_site._registry self.can_add_related = can_add_related # 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 @property def media(self): return self.widget.media def render(self, name, value, *args, **kwargs): rel_to = self.rel.to info = (rel_to._meta.app_label, rel_to._meta.model_name) self.widget.choices = self.choices output = [self.widget.render(name, value, *args, **kwargs)] if self.can_add_related: related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) # TODO: "add_id_" is hard-coded here. This should instead use the # correct API to determine the ID dynamically. output.append('<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % (related_url, name)) output.append('<img src="%s" width="10" height="10" alt="%s"/></a>' % (static('admin/img/icon_addlink.gif'), _('Add Another'))) return mark_safe(''.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 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 AdminEmailInputWidget(forms.EmailInput): def __init__(self, attrs=None): final_attrs = {'class': 'vTextField'} if attrs is not None: final_attrs.update(attrs) super(AdminEmailInputWidget, self).__init__(attrs=final_attrs) class AdminURLFieldWidget(forms.URLInput): 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) def render(self, name, value, attrs=None): html = super(AdminURLFieldWidget, self).render(name, value, attrs) if value: value = force_text(self._format_value(value)) final_attrs = {'href': smart_urlquote(value)} html = format_html( '<p class="url">{0} <a{1}>{2}</a><br />{3} {4}</p>', _('Currently:'), flatatt(final_attrs), value, _('Change:'), html ) return html class AdminIntegerFieldWidget(forms.TextInput): class_name = 'vIntegerField' def __init__(self, attrs=None): final_attrs = {'class': self.class_name} if attrs is not None: final_attrs.update(attrs) super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs) class AdminBigIntegerFieldWidget(AdminIntegerFieldWidget): class_name = 'vBigIntegerField' 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)