Skip to content

Instantly share code, notes, and snippets.

@quxf2012
Last active November 22, 2023 11:25
Show Gist options
  • Save quxf2012/b1827d2bf02322c2779e8309ba089163 to your computer and use it in GitHub Desktop.
Save quxf2012/b1827d2bf02322c2779e8309ba089163 to your computer and use it in GitHub Desktop.
django admin manytomany field list_edit
"""
base https://gist.github.com/jdklub/9261959
usage:
@admin.register(XxxModel)
class XxxModelAdmin(ModelMultipleListEdit, admin.ModelAdmin):
m_list_editable={
"field": {
"queryset": field_queryset,
"index": -1 # list_display index; insert if index exists else append
}
}
"""
from django import forms
from django.forms.utils import flatatt
from django.utils.safestring import mark_safe
__all__ = ['ModelMultipleListEdit']
class StyleableCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
def __init__(self, attrs=None, ul_attrs=None):
self.ul_attrs = ul_attrs
super().__init__(attrs)
def render(self, *args, **kwargs):
html = super().render(*args, **kwargs)
final_attrs = self.build_attrs(self.ul_attrs)
return mark_safe(html.replace('<ul>', '<ul%s>' % flatatt(final_attrs)))
class ModelMultipleListEdit:
"""
m_list_editable={
"field": {
"queryset": field_queryset,
"index": -1 # list_display index, insert if index exist else append
}
}
"""
m_list_editable = None
def get_changelist_form(self, request, **kwargs):
if not self.m_list_editable:
return super().get_changelist_form(request, **kwargs)
# dynamic create Form
cls_dict = {}
for field, v in self.m_list_editable.items():
queryset = v['queryset']
# dynamic create ManyToManyField form field
cls_dict[field] = forms.ModelMultipleChoiceField(queryset=queryset, widget=StyleableCheckboxSelectMultiple(ul_attrs={"style": "height:80px; overflow-y:scroll;"}), required=False)
cls = type('CloudAppForm', (forms.ModelForm,), cls_dict)
return cls
def get_changelist_instance(self, request):
changeList = super().get_changelist_instance(request)
if not self.m_list_editable:
return changeList
# injection ManyToManyField to list_display list_editable ;
# use to skip ManyToManyField check;
list_display = list(self.list_display)
list_editable = list(self.list_editable)
for field, v in self.m_list_editable.items(): # key
if field not in list_display:
if 'index' in v: # 0 is valid value
list_display.insert(int(v['index']), field)
else:
list_display.append(field)
if field not in list_editable:
list_editable.append(field)
self.list_display = list_display
self.list_editable = tuple(list_editable)
# fix first request display
changeList.list_display = list_display
if changeList.is_popup:
changeList.list_editable = ()
else:
changeList.list_editable = list_editable
# fix first request display
return changeList
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment