Created new models to replicate out of and split coord app
Moved helpers and views to their respective new apps
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
from functools import update_wrapper
|
||||
from django.contrib.admin import ModelAdmin, helpers
|
||||
from django.contrib.admin.exceptions import DisallowedModelAdminToField
|
||||
from django.contrib.admin.options import TO_FIELD_VAR
|
||||
from django.contrib.admin.utils import flatten_fieldsets, unquote
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse, path
|
||||
from django.core.exceptions import PermissionDenied, FieldDoesNotExist
|
||||
|
||||
|
||||
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):
|
||||
to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR))
|
||||
if to_field:
|
||||
print("To field in clone_view. What is this? ",to_field)
|
||||
if to_field and not self.to_field_allowed(request, to_field):
|
||||
raise DisallowedModelAdminToField(
|
||||
"The field %s cannot be referenced." % to_field
|
||||
)
|
||||
|
||||
if not self.has_add_permission(request):
|
||||
raise PermissionDenied
|
||||
original_obj = self.get_object(request, unquote(object_id), to_field)
|
||||
if original_obj is None:
|
||||
return self._get_obj_does_not_exist_redirect(request, self.opts, object_id)
|
||||
if not self.has_view_or_change_permission(request, original_obj):
|
||||
raise PermissionDenied
|
||||
|
||||
fieldsets = self.get_fieldsets(request, original_obj)
|
||||
model_form = self.get_form(request, original_obj, change=False, fields=flatten_fieldsets(fieldsets))
|
||||
|
||||
if request.method == "POST":
|
||||
form = model_form(request.POST)
|
||||
formsets, inline_instances = self._create_formsets(
|
||||
request,
|
||||
original_obj,
|
||||
change=False,
|
||||
)
|
||||
|
||||
form_validated = form.is_valid()
|
||||
if form_validated:
|
||||
new_object = self.save_form(request, form, change=False)
|
||||
self.save_model(request, new_object, form, False)
|
||||
self.clone_related(request, original_obj, new_object)
|
||||
return redirect(reverse("admin:{0}_{1}_change".format(self.opts.app_label, self.opts.model_name), args=[new_object.pk]))
|
||||
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, original_obj, change=False)
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
def clone_related(self, request, original_obj, new_obj):
|
||||
for inline in self.inlines:
|
||||
if not inline.clone_parent:
|
||||
continue
|
||||
try:
|
||||
inline.model._meta.get_field(inline.clone_parent)
|
||||
except FieldDoesNotExist:
|
||||
continue
|
||||
inline_objects = inline.model.objects.filter(**{inline.clone_parent: original_obj})
|
||||
for inline_object in inline_objects:
|
||||
inline_object.pk = None
|
||||
setattr(inline_object, inline.clone_parent, new_obj)
|
||||
inline_object.save()
|
||||
|
||||
|
||||
class InlineAdminFormSetFakeOriginal(helpers.InlineAdminFormSet):
|
||||
|
||||
def __iter__(self):
|
||||
# the template requires the AdminInlineForm to have an `original`
|
||||
# attribute, which is the model instance, in order to display the
|
||||
# 'Delete' checkbox
|
||||
# we don't have `original` because we are just providing initial
|
||||
# data to the form, so we attach a "fake original" (something that
|
||||
# evaluates to True) to fool the template and make is display
|
||||
# the 'Delete' checkbox
|
||||
# needless to say this is a terrible hack and will break in future
|
||||
# django versions :)
|
||||
for inline_form in super(InlineAdminFormSetFakeOriginal, self).__iter__():
|
||||
if inline_form.form.initial:
|
||||
inline_form.original = True
|
||||
yield inline_form
|
||||
Reference in New Issue
Block a user