Skip to content

Instantly share code, notes, and snippets.

@efimerdlerkravitz
Created January 31, 2018 13:24
Show Gist options
  • Save efimerdlerkravitz/52855b8a0e9f35b9bf66f48fdc7dfc2d to your computer and use it in GitHub Desktop.
Save efimerdlerkravitz/52855b8a0e9f35b9bf66f48fdc7dfc2d to your computer and use it in GitHub Desktop.
Edit a many-to-many relationship (ManyToManyField) on the Django Admin change list page.
class BookChangeList(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().__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 BookAdmin
self.list_display = ('name', 'categories')
self.list_display_links = ['name']
self.list_editable = ['categories']
class BookForm(ModelForm):
# this the bit of custom CSS we want to add
style_text = f'columns: {int(BookCategory.objects.count()/5)}'
# here we only need to define the field we want to be editable
categories = ModelMultipleChoiceField(queryset=BookCategory.objects.all(),
widget=StyleableCheckboxSelectMultiple(ul_attrs={"style": style_text}),
required=False)
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
ordering = ('id',)
def get_changelist(self, request, **kwargs):
return BookChangeList
def get_changelist_form(self, request, **kwargs):
return BookForm
@admin.register(BookCategory)
class BookCategoryAdmin(admin.ModelAdmin):
list_display = ['id', 'name']
# this subclass allows us to add a bit of our own CSS at the <ul> level
from django import forms
from django.forms.utils import flatatt
from django.utils.safestring import mark_safe
class StyleableCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
def __init__(self, attrs=None, ul_attrs=None):
self.ul_attrs = ul_attrs
super().__init__(attrs)
def render(self, name, value, attrs=None, choices=None):
html = super().render(name, value, attrs, choices)
final_attrs = self.build_attrs(self.ul_attrs)
return mark_safe(html.replace('<ul', f'<ul {flatatt(final_attrs)}'))
class BookCategory(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Book(models.Model):
name = models.CharField(max_length=255)
categories = models.ManyToManyField('BookCategory', blank=True, null=True)
def __str__(self):
return self.name
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment