In the Django admin, I would really like to be able to display an editable ManyToManyField in the list display.
It doesn't necessarily need to be the full ManyToManyField control - being able to save just one value would be good enough for the purposes of the list display (though the underlying values are many-to-many in nature).
My model looks like this:
class Item(models.Model):
name = models.CharField(max_length=500)
colour = models.ManyToManyField(Colour, related_name='primary_colour')
If I try this in admin.py
:
class ItemAdmin(admin.ModelAdmin):
list_display = ('name', 'colour')
list_editable = ('colour')
Then I get this error:
'ItemAdmin.list_display[6]', 'colour' is a ManyToManyField which is not supported.
Is there any way at all that I can show an editable ManyToManyField for rapid editing in the list display?
I found this related question, which explains how to make the values visible in the list display, but not editable: ManyToManyField widget in a django admin change list?
Django by default won't allow to add ManyToManyField in list_editable in ModelAdmin. So we need to override model admin methods.
On looking your models you need to follow below steps to get the ManyToManyField editable in list display page.
In apps/forms.py you need to define which ManyToMany fields you need to make editable in list display page. As below,
from django import forms
from app.models import Item
class ItemChangeListForm(forms.ModelForm):
# here we only need to define the field we want to be editable
colour = forms.ModelMultipleChoiceField(queryset=Colour.objects.all(),
required=False)
In app/admin.py you need to override methods of model admin. As below,
from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from app.models import Item
from app.forms import ItemChangeListForm
class ItemChangeList(ChangeList):
def __init__(self, request, model, list_display,
list_display_links, list_filter, date_hierarchy,
search_fields, list_select_related, list_per_page,
list_max_show_all, list_editable, model_admin):
super(ItemChangeList, self).__init__(request, model,
list_display, list_display_links, list_filter,
date_hierarchy, search_fields, list_select_related,
list_per_page, list_max_show_all, list_editable,
model_admin)
# these need to be defined here, and not in ItemAdmin
self.list_display = ['action_checkbox', 'name', 'colour']
self.list_display_links = ['name']
self.list_editable = ['colour']
class ItemAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
return ItemChangeList
def get_changelist_form(self, request, **kwargs):
return ItemChangeListForm
admin.site.register(Item, ItemAdmin)
Now you all set to check the changes, run server and check django admin for Movie model. You can edit ManyToMany field directly from list display page.
Note : If you are going to use muliptle ManyToManyFields editable in list then, you need to set DATA_UPLOAD_MAX_NUMBER_FIELDS in settings.py .
You can easily add a custom view to your admin urls and add the required html/javascript/ajax. Here's the basics:
class ItemAdmin(admin.ModelAdmin):
# regular stuff
def render_foo(self, obj):
# add this to your list_display
html = '<stuff><input/submit action></stuff>'
return mark_safe(html)
def get_urls(self):
urls = super(ItemAdmin, self).get_urls()
extra_urls = patterns('',
(r'^process_foo/$', self.admin_site.admin_view(self.process_foo)),
)
return extra_urls + urls
def process_foo(self, request):
if not request.is_ajax():
raise Http404
foo = request.GET.get("attr")
# process m2m
# return some json