PageRenderTime 35ms CodeModel.GetById 1ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/admin/widgets.py

https://code.google.com/p/mango-py/
Python | 296 lines | 266 code | 11 blank | 19 comment | 13 complexity | ae5532a23cfe1289edc1e2eff42eea85 MD5 | raw file
  1"""
  2Form Widget classes specific to the Django admin site.
  3"""
  4
  5import django.utils.copycompat as copy
  6
  7from django import forms
  8from django.forms.widgets import RadioFieldRenderer
  9from django.forms.util import flatatt
 10from django.utils.html import escape
 11from django.utils.text import truncate_words
 12from django.utils.translation import ugettext as _
 13from django.utils.safestring import mark_safe
 14from django.utils.encoding import force_unicode
 15from django.conf import settings
 16from django.core.urlresolvers import reverse, NoReverseMatch
 17
 18class FilteredSelectMultiple(forms.SelectMultiple):
 19    """
 20    A SelectMultiple with a JavaScript filter interface.
 21
 22    Note that the resulting JavaScript assumes that the jsi18n
 23    catalog has been loaded in the page
 24    """
 25    class Media:
 26        js = (settings.ADMIN_MEDIA_PREFIX + "js/core.js",
 27              settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js",
 28              settings.ADMIN_MEDIA_PREFIX + "js/SelectFilter2.js")
 29
 30    def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
 31        self.verbose_name = verbose_name
 32        self.is_stacked = is_stacked
 33        super(FilteredSelectMultiple, self).__init__(attrs, choices)
 34
 35    def render(self, name, value, attrs=None, choices=()):
 36        if attrs is None: attrs = {}
 37        attrs['class'] = 'selectfilter'
 38        if self.is_stacked: attrs['class'] += 'stacked'
 39        output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
 40        output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
 41        # TODO: "id_" is hard-coded here. This should instead use the correct
 42        # API to determine the ID dynamically.
 43        output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \
 44            (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
 45        return mark_safe(u''.join(output))
 46
 47class AdminDateWidget(forms.DateInput):
 48    class Media:
 49        js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
 50              settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
 51
 52    def __init__(self, attrs={}, format=None):
 53        super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}, format=format)
 54
 55class AdminTimeWidget(forms.TimeInput):
 56    class Media:
 57        js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
 58              settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
 59
 60    def __init__(self, attrs={}, format=None):
 61        super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}, format=format)
 62
 63class AdminSplitDateTime(forms.SplitDateTimeWidget):
 64    """
 65    A SplitDateTime Widget that has some admin-specific styling.
 66    """
 67    def __init__(self, attrs=None):
 68        widgets = [AdminDateWidget, AdminTimeWidget]
 69        # Note that we're calling MultiWidget, not SplitDateTimeWidget, because
 70        # we want to define widgets.
 71        forms.MultiWidget.__init__(self, widgets, attrs)
 72
 73    def format_output(self, rendered_widgets):
 74        return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
 75            (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
 76
 77class AdminRadioFieldRenderer(RadioFieldRenderer):
 78    def render(self):
 79        """Outputs a <ul> for this set of radio fields."""
 80        return mark_safe(u'<ul%s>\n%s\n</ul>' % (
 81            flatatt(self.attrs),
 82            u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
 83        )
 84
 85class AdminRadioSelect(forms.RadioSelect):
 86    renderer = AdminRadioFieldRenderer
 87
 88class AdminFileWidget(forms.ClearableFileInput):
 89    template_with_initial = (u'<p class="file-upload">%s</p>'
 90                            % forms.ClearableFileInput.template_with_initial)
 91    template_with_clear = (u'<span class="clearable-file-input">%s</span>'
 92                           % forms.ClearableFileInput.template_with_clear)
 93
 94def url_params_from_lookup_dict(lookups):
 95    """
 96    Converts the type of lookups specified in a ForeignKey limit_choices_to
 97    attribute to a dictionary of query parameters
 98    """
 99    params = {}
100    if lookups and hasattr(lookups, 'items'):
101        items = []
102        for k, v in lookups.items():
103            if isinstance(v, list):
104                v = u','.join([str(x) for x in v])
105            elif isinstance(v, bool):
106                # See django.db.fields.BooleanField.get_prep_lookup
107                v = ('0', '1')[v]
108            else:
109                v = unicode(v)
110            items.append((k, v))
111        params.update(dict(items))
112    return params
113
114class ForeignKeyRawIdWidget(forms.TextInput):
115    """
116    A Widget for displaying ForeignKeys in the "raw_id" interface rather than
117    in a <select> box.
118    """
119    def __init__(self, rel, attrs=None, using=None):
120        self.rel = rel
121        self.db = using
122        super(ForeignKeyRawIdWidget, self).__init__(attrs)
123
124    def render(self, name, value, attrs=None):
125        if attrs is None:
126            attrs = {}
127        related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())
128        params = self.url_parameters()
129        if params:
130            url = u'?' + u'&amp;'.join([u'%s=%s' % (k, v) for k, v in params.items()])
131        else:
132            url = u''
133        if "class" not in attrs:
134            attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
135        output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
136        # TODO: "id_" is hard-coded here. This should instead use the correct
137        # API to determine the ID dynamically.
138        output.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
139            (related_url, url, name))
140        output.append(u'<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup')))
141        if value:
142            output.append(self.label_for_value(value))
143        return mark_safe(u''.join(output))
144
145    def base_url_parameters(self):
146        return url_params_from_lookup_dict(self.rel.limit_choices_to)
147
148    def url_parameters(self):
149        from django.contrib.admin.views.main import TO_FIELD_VAR
150        params = self.base_url_parameters()
151        params.update({TO_FIELD_VAR: self.rel.get_related_field().name})
152        return params
153
154    def label_for_value(self, value):
155        key = self.rel.get_related_field().name
156        try:
157            obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
158            return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14))
159        except (ValueError, self.rel.to.DoesNotExist):
160            return ''
161
162class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
163    """
164    A Widget for displaying ManyToMany ids in the "raw_id" interface rather than
165    in a <select multiple> box.
166    """
167    def render(self, name, value, attrs=None):
168        if attrs is None:
169            attrs = {}
170        attrs['class'] = 'vManyToManyRawIdAdminField'
171        if value:
172            value = ','.join([force_unicode(v) for v in value])
173        else:
174            value = ''
175        return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
176
177    def url_parameters(self):
178        return self.base_url_parameters()
179
180    def label_for_value(self, value):
181        return ''
182
183    def value_from_datadict(self, data, files, name):
184        value = data.get(name)
185        if value:
186            return value.split(',')
187
188    def _has_changed(self, initial, data):
189        if initial is None:
190            initial = []
191        if data is None:
192            data = []
193        if len(initial) != len(data):
194            return True
195        for pk1, pk2 in zip(initial, data):
196            if force_unicode(pk1) != force_unicode(pk2):
197                return True
198        return False
199
200class RelatedFieldWidgetWrapper(forms.Widget):
201    """
202    This class is a wrapper to a given widget to add the add icon for the
203    admin interface.
204    """
205    def __init__(self, widget, rel, admin_site, can_add_related=None):
206        self.is_hidden = widget.is_hidden
207        self.needs_multipart_form = widget.needs_multipart_form
208        self.attrs = widget.attrs
209        self.choices = widget.choices
210        self.widget = widget
211        self.rel = rel
212        # Backwards compatible check for whether a user can add related
213        # objects.
214        if can_add_related is None:
215            can_add_related = rel.to in admin_site._registry
216        self.can_add_related = can_add_related
217        # so we can check if the related object is registered with this AdminSite
218        self.admin_site = admin_site
219
220    def __deepcopy__(self, memo):
221        obj = copy.copy(self)
222        obj.widget = copy.deepcopy(self.widget, memo)
223        obj.attrs = self.widget.attrs
224        memo[id(self)] = obj
225        return obj
226
227    def _media(self):
228        return self.widget.media
229    media = property(_media)
230
231    def render(self, name, value, *args, **kwargs):
232        rel_to = self.rel.to
233        info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
234        try:
235            related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
236        except NoReverseMatch:
237            info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower())
238            related_url = '%s%s/%s/add/' % info
239        self.widget.choices = self.choices
240        output = [self.widget.render(name, value, *args, **kwargs)]
241        if self.can_add_related:
242            # TODO: "id_" is hard-coded here. This should instead use the correct
243            # API to determine the ID dynamically.
244            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
245                (related_url, name))
246            output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
247        return mark_safe(u''.join(output))
248
249    def build_attrs(self, extra_attrs=None, **kwargs):
250        "Helper function for building an attribute dictionary."
251        self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs)
252        return self.attrs
253
254    def value_from_datadict(self, data, files, name):
255        return self.widget.value_from_datadict(data, files, name)
256
257    def _has_changed(self, initial, data):
258        return self.widget._has_changed(initial, data)
259
260    def id_for_label(self, id_):
261        return self.widget.id_for_label(id_)
262
263class AdminTextareaWidget(forms.Textarea):
264    def __init__(self, attrs=None):
265        final_attrs = {'class': 'vLargeTextField'}
266        if attrs is not None:
267            final_attrs.update(attrs)
268        super(AdminTextareaWidget, self).__init__(attrs=final_attrs)
269
270class AdminTextInputWidget(forms.TextInput):
271    def __init__(self, attrs=None):
272        final_attrs = {'class': 'vTextField'}
273        if attrs is not None:
274            final_attrs.update(attrs)
275        super(AdminTextInputWidget, self).__init__(attrs=final_attrs)
276
277class AdminURLFieldWidget(forms.TextInput):
278    def __init__(self, attrs=None):
279        final_attrs = {'class': 'vURLField'}
280        if attrs is not None:
281            final_attrs.update(attrs)
282        super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
283
284class AdminIntegerFieldWidget(forms.TextInput):
285    def __init__(self, attrs=None):
286        final_attrs = {'class': 'vIntegerField'}
287        if attrs is not None:
288            final_attrs.update(attrs)
289        super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
290
291class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
292    def __init__(self, attrs=None):
293        final_attrs = {'class': 'vCommaSeparatedIntegerField'}
294        if attrs is not None:
295            final_attrs.update(attrs)
296        super(AdminCommaSeparatedIntegerFieldWidget, self).__init__(attrs=final_attrs)