Skip to content

Instantly share code, notes, and snippets.

@jacoduplessis
Created April 12, 2020 06:04
Show Gist options
  • Save jacoduplessis/90410280bcfb58a57dc018d913a14f66 to your computer and use it in GitHub Desktop.
Save jacoduplessis/90410280bcfb58a57dc018d913a14f66 to your computer and use it in GitHub Desktop.
MPTT admin to move multiple nodes at once
class PlaceAdmin(MPTTModelAdmin):
def get_model_info(self):
app_label = self.model._meta.app_label
return app_label, self.model._meta.model_name
def get_move_form_class(self):
class MoveSelectTargetForm(forms.Form):
target = forms.ModelChoiceField(self.model.objects.all())
position = TreeNodePositionField()
object_ids = forms.CharField(max_length=1000)
return MoveSelectTargetForm
def move_selected_objects(self, request, queryset):
selected = queryset.values_list('pk', flat=True)
info = self.get_model_info()
url = reverse(f'admin:%s_%s_move_select_target' % info)
return HttpResponseRedirect('%s?ids=%s' % (
url,
','.join(str(pk) for pk in selected),
))
actions = [
'move_selected_objects',
]
def move_select_target(self, request):
form = self.get_move_form_class()()
form.initial['object_ids'] = request.GET.get('ids')
info = self.get_model_info()
action = reverse(f'admin:%s_%s_move_process' % info)
selection_ids = request.GET.get('ids').split(',')
selection = self.model.objects.filter(id__in=selection_ids)
context = {
**self.admin_site.each_context(request),
'form': form,
'action': action,
'selection': selection,
'opts': self.model._meta,
}
return TemplateResponse(request, 'mptt_move/move_select_target.html', context)
def move_process(self, request):
url = reverse('admin:%s_%s_changelist' % self.get_model_info(),
current_app=self.admin_site.name)
response = HttpResponseRedirect(url)
form = self.get_move_form_class()(request.POST)
if not form.is_valid():
# need something proper here
self.message_user(request, 'Invalid submission')
return response
else:
ids = form.cleaned_data['object_ids'].split(',')
for obj in self.model.objects.filter(id__in=ids):
payload = {
'target': form.cleaned_data['target'],
'position': form.cleaned_data['position']
}
move_form = MoveNodeForm(obj, payload)
if not move_form.is_valid():
self.message_user(request, f'Invalid move operation: {form.errors}', level=messages.ERROR)
continue
else:
with transaction.atomic():
move_form.save()
with transaction.atomic():
self.model.objects.rebuild()
self.message_user(request, 'Move instruction processed, tree rebuilt.')
return response
def get_urls(self):
urls = super().get_urls()
info = self.get_model_info()
my_urls = [
path('move-select-target/',
self.admin_site.admin_view(self.move_select_target),
name='%s_%s_move_select_target' % info),
path('move-process/',
self.admin_site.admin_view(self.move_process),
name='%s_%s_move_process' % info),
]
return my_urls + urls
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment