diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 15c9745f83..4925eeff76 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -207,16 +207,29 @@ class ModelAdmin(object): if isinstance(db_field, models.ManyToManyField) and db_field.rel.filter_interface: widget = widgets.FilteredSelectMultiple(db_field.verbose_name, db_field.rel.filter_interface-1) return db_field.formfield(widget=widget, **kwargs) + # For DateTimeFields, use a special field and widget. if isinstance(db_field, models.DateTimeField): return forms.SplitDateTimeField(required=not db_field.blank, widget=widgets.AdminSplitDateTime(), label=capfirst(db_field.verbose_name), **kwargs) + # For DateFields, add a custom CSS class. if isinstance(db_field, models.DateField): return db_field.formfield(widget=forms.TextInput(attrs={'class': 'vDateField', 'size': '10'})) + # For TimeFields, add a custom CSS class. if isinstance(db_field, models.TimeField): return db_field.formfield(widget=forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'})) + + # For ForeignKey or ManyToManyFields, use a special widget. + if db_field.rel and isinstance(db_field.rel, (models.ManyToOneRel, models.ManyToManyRel)): + # Wrap the widget's render() method with a method that adds + # extra HTML to the end of the rendered output. + formfield = db_field.formfield() + formfield.widget.render = widgets.RelatedFieldWidgetWrapper(formfield.widget.render, db_field.rel) + return formfield + + # For any other type of field, just call its formfield() method. return db_field.formfield(**kwargs) def has_add_permission(self, request): diff --git a/django/contrib/admin/templates/widget/foreign.html b/django/contrib/admin/templates/widget/foreign.html index 301f5214db..476fdb9b37 100644 --- a/django/contrib/admin/templates/widget/foreign.html +++ b/django/contrib/admin/templates/widget/foreign.html @@ -1,15 +1,5 @@ {% load admin_modify adminmedia %} {% output_all bound_field.form_fields %} -{% if bound_field.raw_id_admin %} - {% if bound_field.field.rel.limit_choices_to %} - Lookup - {% else %} - Lookup - {% endif %} -{% else %} -{% if bound_field.needs_add_label %} - Add Another -{% endif %}{% endif %} {% if change %} {% if bound_field.field.primary_key %} {{ bound_field.original_value }} diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 3a7c334301..b925693398 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -63,12 +63,10 @@ class AdminBoundField(object): self.field = field self.original = original self.form_fields = [field_mapping[name] for name in self.field.get_manipulator_field_names('')] - self.element_id = self.form_fields[0].get_id() self.has_label_first = not isinstance(self.field, models.BooleanField) self.raw_id_admin = use_raw_id_admin(field) self.is_date_time = isinstance(field, models.DateTimeField) self.is_file_field = isinstance(field, models.FileField) - self.needs_add_label = field.rel and (isinstance(field.rel, models.ManyToOneRel) or isinstance(field.rel, models.ManyToManyRel)) and field.rel.to._meta.admin self.hidden = isinstance(self.field, models.AutoField) self.first = False @@ -81,9 +79,6 @@ class AdminBoundField(object): self.cell_class_attribute = ' class="%s" ' % ' '.join(classes) self._repr_filled = False - if field.rel: - self.related_url = '../../../%s/%s/' % (field.rel.to._meta.app_label, field.rel.to._meta.object_name.lower()) - def original_value(self): if self.original: return self.original.__dict__[self.field.column] diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index e3f3e63c94..1a2abe85ce 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -40,3 +40,36 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget): def format_output(self, rendered_widgets): return u'

%s %s
%s %s

' % \ (_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]) + +class RelatedFieldWidgetWrapper(object): + """ + This class is a wrapper whose __call__() method mimics the interface of a + Widget's render() method. + """ + def __init__(self, render_func, rel): + self.render_func, self.rel = render_func, rel + + def __call__(self, name, value, *args, **kwargs): + from django.conf import settings + rel_to = self.rel.to + related_url = '../../../%s/%s/' % (rel_to._meta.app_label, rel_to._meta.object_name.lower()) + output = [self.render_func(name, value, *args, **kwargs)] + if self.rel.raw_id_admin: + if self.rel.limit_choices_to: + url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in self.rel.limit_choices_to.items()]) + else: + url = '' + # TODO: "id_" is hard-coded here. This should instead use the correct + # API to determine the ID dynamically. + output.append(' ' % \ + (related_url, url, name)) + output.append('Lookup' % settings.ADMIN_MEDIA_PREFIX) + #if self.change: # TODO + #output.append(' TODO') + elif rel_to._meta.admin: # 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 u''.join(output)