Skip to content

Instantly share code, notes, and snippets.

@ebrelsford
Created March 28, 2013 13:56
Show Gist options
  • Save ebrelsford/5263306 to your computer and use it in GitHub Desktop.
Save ebrelsford/5263306 to your computer and use it in GitHub Desktop.
A modification of Django's admin widget RelatedFieldWidgetWrapper, which adds the handy + to select boxes when picking a related model. This one doesn't assume the user's picking a related model instance--it simply lets the user pick a model instance or add a new one if needed. This is useful on forms (such as the example in forms.py) where you …
"""
An example form using AddAnotherWidgetWrapper.
"""
from django import forms
from .models import Owner
from .widgets import AddAnotherWidgetWrapper
class PickOwnerForm(forms.Form):
owner = forms.ModelChoiceField(
queryset=Owner.objects.all().order_by('name'),
widget=AddAnotherWidgetWrapper(
forms.Select(),
Owner,
)
)
{% extends "admin/base_site.html" %}
{% comment %}
An example template that renders a form where an AddAnotherWidgetWrapper is present.
{% endcomment %}
{% load i18n admin_static admin_modify %}
{% load admin_urls %}
{% block extrahead %}
{{ block.super }}
<script src="{{ STATIC_URL }}admin/js/admin/RelatedObjectLookups.js"></script>
{% block bodyclass %}change-list{% endblock %}
{% block content %}
<div>
Pick an owner or add a new one:
</div>
{{ form.media }}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="{% trans "Picked" %}" />
</form>
{% endblock %}
import copy
from django import forms
from django.contrib.admin.templatetags.admin_static import static
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
class AddAnotherWidgetWrapper(forms.Widget):
"""
This class is a wrapper to a given widget to add the add icon for the
admin interface. Modeled after
django.contrib.admin.widgets.RelatedFieldWidgetWrapper
"""
def __init__(self, widget, model):
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.model = model
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):
model = self.model
info = (model._meta.app_label, model._meta.object_name.lower())
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
related_url = reverse('admin:%s_%s_add' % info)
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 _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_)
@icuy
Copy link

icuy commented Apr 14, 2014

Great, work in django 1.6 is well.

@renodesper
Copy link

Hi, how to retain the data I put in parent form after submitting child form? My child form replace the parent form so the value is disappear.

@niorko
Copy link

niorko commented Jul 3, 2015

Hi, I want to use ForeignKeyRawIdWidget with my custom model, due to pop-up window.

class PickForm(forms.Form):
    _selected_action = forms.CharField(widget=forms.MultipleHiddenInput)
    my_id = forms.ModelChoiceField(
        MyModel.objects.all(),
        widget=ForeignKeyRawIdWidget(
            rel=ManyToOneRel('self', admin.site),
            admin_site=admin.site
        )
    )

We are still using LTS django 1.4 and nothing but the plain input is displayed. There is no icon for selecting the choice that I want.

Have you got any suggestions?

@efimerdlerkravitz
Copy link

@niorko did you manage to solve your problem ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment