The RelatedFieldWidgetWrapper (found in django.contrib.admin.widgets) is used in the Admin pages to include the capability on a Foreign Key control to add a new related record. (In English: puts the little green plus sign to the right of the control.) In a new application of the FilteredSelectMultiple widget I discussed recently, I needed to add this functionality.
This wrapper is a little complicated, but it didn’t take too much Google searching to find an example.  A Google Code file from something called Crimson Online helped me understand the concept.
The wrapper takes three required parameters plus one optional:
- The widget to be wrapped. In my case the FilteredSelectMultiple widget
- A relation that defines the two models involved
- A reference to the admin site
- The Boolean can_add_related (optional)
For the first parameter, I used the widget with its parameters:
FilteredSelectMultiple(('entities'),False,)
The relation confused me at first, but the example made it easy. Â The model linked to the form, in this case Item, provides it.
Item._meta.get_field('entities').re
l
The third parameter, the admin site object, is used along with the relation to determine if the user has permission to add this related model. Â The example used django.contrib.admin.site, which I found doesn’t work. Â Instead, I captured the admin_site value from the ModelAdmin class in my admin.py and set it as a variable for the form. Â Then I referenced that variable in the \__init__ method of the form to include it in the widget. Â This means that the widget has to be assigned within the __init__ method of the form.
self.admin_site
The fourth parameter can be used to override the permission check performed by the widget. Â However, when a user without the permission to add clicks that little green plus sign, an ugly 500 error is returned.
Here is the code used:
in admin.py:
class ItemAdmin(admin.ModelAdmin): form = ItemAdminForm def __init__(self, model, admin_site): super(ItemAdmin,self).__init__(model,admin_site) self.form.admin_site = admin_site # capture the admin_site admin.site.register(Item, ItemAdmin)
in forms.py
from django.contrib.admin.widgets import FilteredSelectMultiple, RelatedFieldWidgetWrapper class ItemAdminForm(forms.ModelForm): entities = forms.ModelMultipleChoiceField(queryset=None,label=('Select Entities'),) def __init__(self, *args, **kwargs): super(ItemAdminForm,self).__init__(*args, **kwargs) # set the widget with wrapper self.fields['entities'].widget = RelatedFieldWidgetWrapper( FilteredSelectMultiple(('entities'),False,), Item._meta.get_field('entities').rel, self.admin_site) self.fields['entities'].queryset = Entity.objects.all() class Media: ## media for the FilteredSelectMultiple widget css = { 'all':('/media/css/widgets.css',), } # jsi18n is required by the widget js = ('/admin/jsi18n/',) class Meta: model = Item
UPDATE:
I’ve published a follow up post that covers using this wrapper outside of the admin application. See More RelatedFieldWidgetWrapper – My Very Own Popup.
Â