140 lines
4.8 KiB
Python
140 lines
4.8 KiB
Python
from functools import update_wrapper
|
|
from django.contrib.admin import ModelAdmin, helpers
|
|
from django.contrib.admin.utils import flatten_fieldsets
|
|
from django.forms.formsets import all_valid
|
|
from django.urls import reverse, path
|
|
from django.core.exceptions import PermissionDenied
|
|
|
|
|
|
class CloneModelAdmin(ModelAdmin):
|
|
clone_verbose_name = "Clone"
|
|
change_form_template = 'admin/admin_change_form.html'
|
|
cloneable_fields = []
|
|
|
|
def get_urls(self):
|
|
|
|
# Not certain what this wrap() function is exactly. Just copied it from the django admin get_urls function
|
|
def wrap(view):
|
|
def wrapper(*args, **kwargs):
|
|
return self.admin_site.admin_view(view)(*args, **kwargs)
|
|
|
|
wrapper.model_admin = self
|
|
return update_wrapper(wrapper, view)
|
|
|
|
info = self.opts.app_label, self.opts.model_name
|
|
|
|
new_urlpatterns = [
|
|
path(
|
|
"<path:object_id>/clone/",
|
|
wrap(self.clone_view),
|
|
name="%s_%s_clone" % info,
|
|
),
|
|
]
|
|
|
|
original_urlpatterns = super(CloneModelAdmin, self).get_urls()
|
|
|
|
# Important to add custom urls before the existing ones.
|
|
# Last entry is <URLPattern '<path:object_id>/'> which will catch everything not already picked up
|
|
return new_urlpatterns + original_urlpatterns
|
|
|
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
|
url = reverse("admin:{0}_{1}_clone".format(self.opts.app_label, self.opts.model_name), args=[object_id])
|
|
|
|
extra_context = extra_context or {}
|
|
extra_context.update({
|
|
'clone_verbose_name': self.clone_verbose_name,
|
|
'clone_link': url,
|
|
})
|
|
return super(CloneModelAdmin, self).change_view(request, object_id, form_url, extra_context)
|
|
|
|
def clone_view(self, request, object_id, form_url='', extra_context=None):
|
|
if not self.has_add_permission(request):
|
|
raise PermissionDenied
|
|
|
|
original_obj = self.get_object(request, object_id)
|
|
|
|
if original_obj is None:
|
|
return self._get_obj_does_not_exist_redirect(
|
|
request, self.opts, object_id
|
|
)
|
|
|
|
fieldsets = self.get_fieldsets(request, None)
|
|
|
|
model_form = self.get_form(
|
|
request, None, change=False, fields=flatten_fieldsets(fieldsets)
|
|
)
|
|
|
|
if request.method == "POST":
|
|
form = model_form(request.POST)
|
|
formsets, inline_instances = self._create_formsets(
|
|
request,
|
|
form.instance,
|
|
change=False,
|
|
)
|
|
form_validated = form.is_valid()
|
|
if form_validated:
|
|
new_object = self.save_form(request, form, change=False)
|
|
else:
|
|
new_object = form.instance
|
|
|
|
if all_valid(formsets) and form_validated:
|
|
self.save_model(request, new_object, form, False)
|
|
self.save_related(request, form, formsets, False)
|
|
change_message = self.construct_change_message(
|
|
request, form, formsets, False
|
|
)
|
|
self.log_addition(request, new_object, change_message)
|
|
return self.response_add(request, new_object)
|
|
else:
|
|
form_validated = False
|
|
|
|
else:
|
|
new_obj = self.model()
|
|
for field in self.cloneable_fields:
|
|
setattr(new_obj, field, getattr(original_obj, field))
|
|
form = model_form(instance=new_obj)
|
|
|
|
formsets, inline_instances = self._create_formsets(
|
|
request, new_obj, change=True
|
|
)
|
|
|
|
admin_form = helpers.AdminForm(
|
|
form,
|
|
list(self.get_fieldsets(request)),
|
|
self.get_prepopulated_fields(request),
|
|
self.readonly_fields,
|
|
model_admin=self
|
|
)
|
|
media = self.media
|
|
|
|
inline_formsets = self.get_inline_formsets(
|
|
request, formsets, inline_instances
|
|
)
|
|
for inline_formset in inline_formsets:
|
|
media += inline_formset.media
|
|
|
|
title = u'{0} {1}'.format(self.clone_verbose_name, original_obj)
|
|
|
|
context = {
|
|
**self.admin_site.each_context(request),
|
|
"title": title,
|
|
"original": title,
|
|
"adminform": admin_form,
|
|
"is_popup": "_popup" in request.POST or "_popup" in request.GET,
|
|
"show_delete": False,
|
|
"media": media,
|
|
"inline_admin_formsets": inline_formsets,
|
|
"errors": helpers.AdminErrorList(form, formsets),
|
|
**(extra_context or {}),
|
|
}
|
|
|
|
context.update(extra_context or {})
|
|
|
|
return self.render_change_form(
|
|
request,
|
|
context,
|
|
form_url=form_url,
|
|
change=False,
|
|
add=True
|
|
)
|