Added traveller clone function which duplicates inlines
This commit is contained in:
@@ -171,15 +171,17 @@ class FamilyInline(admin.StackedInline):
|
|||||||
model = Family
|
model = Family
|
||||||
classes = ['collapse']
|
classes = ['collapse']
|
||||||
extra = 0
|
extra = 0
|
||||||
|
clone_parent = "traveller"
|
||||||
|
|
||||||
|
|
||||||
class TravellerRouteInline(admin.TabularInline):
|
class TravellerRouteInline(admin.TabularInline):
|
||||||
model = TravellerRoute
|
model = TravellerRoute
|
||||||
extra = 0
|
extra = 0
|
||||||
|
clone_parent = "traveller"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Traveller)
|
@admin.register(Traveller)
|
||||||
class TravellerAdmin(MyImportExportModelAdmin, admin.ModelAdmin, TravellerRollMixin):
|
class TravellerAdmin(MyImportExportModelAdmin, CloneModelAdmin, TravellerRollMixin):
|
||||||
list_display = ["first_name", "last_name", "school", "year_level", "is_active", "address", "stop_route", "shuttle", "travel_start_date", "travel_end_date"]
|
list_display = ["first_name", "last_name", "school", "year_level", "is_active", "address", "stop_route", "shuttle", "travel_start_date", "travel_end_date"]
|
||||||
list_filter = [
|
list_filter = [
|
||||||
"is_active", "school", "year_level", "eligibility_status", "bus_stops__bus", "shuttle",
|
"is_active", "school", "year_level", "eligibility_status", "bus_stops__bus", "shuttle",
|
||||||
@@ -190,6 +192,7 @@ class TravellerAdmin(MyImportExportModelAdmin, admin.ModelAdmin, TravellerRollMi
|
|||||||
title="End date"
|
title="End date"
|
||||||
))
|
))
|
||||||
]
|
]
|
||||||
|
cloneable_fields = ["last_name"]
|
||||||
search_fields = ["first_name", "last_name", "address"]
|
search_fields = ["first_name", "last_name", "address"]
|
||||||
inlines = [FamilyInline, TravellerRouteInline]
|
inlines = [FamilyInline, TravellerRouteInline]
|
||||||
readonly_fields = ["travel_start_date", "travel_end_date", "fare_paying", "created_on", "last_edit", "is_active", "address"]
|
readonly_fields = ["travel_start_date", "travel_end_date", "fare_paying", "created_on", "last_edit", "is_active", "address"]
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from django.contrib.admin import ModelAdmin, helpers
|
from django.contrib.admin import ModelAdmin, helpers
|
||||||
from django.contrib.admin.utils import flatten_fieldsets
|
from django.contrib.admin.exceptions import DisallowedModelAdminToField
|
||||||
from django.forms.formsets import all_valid
|
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.urls import reverse, path
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied, FieldDoesNotExist
|
||||||
|
|
||||||
|
|
||||||
class CloneModelAdmin(ModelAdmin):
|
class CloneModelAdmin(ModelAdmin):
|
||||||
@@ -48,55 +50,45 @@ class CloneModelAdmin(ModelAdmin):
|
|||||||
return super(CloneModelAdmin, self).change_view(request, object_id, form_url, extra_context)
|
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):
|
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):
|
if not self.has_add_permission(request):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
original_obj = self.get_object(request, unquote(object_id), to_field)
|
||||||
original_obj = self.get_object(request, object_id)
|
|
||||||
|
|
||||||
if original_obj is None:
|
if original_obj is None:
|
||||||
return self._get_obj_does_not_exist_redirect(
|
return self._get_obj_does_not_exist_redirect(request, self.opts, object_id)
|
||||||
request, self.opts, object_id
|
if not self.has_view_or_change_permission(request, original_obj):
|
||||||
)
|
raise PermissionDenied
|
||||||
|
|
||||||
fieldsets = self.get_fieldsets(request, None)
|
fieldsets = self.get_fieldsets(request, original_obj)
|
||||||
|
model_form = self.get_form(request, original_obj, change=False, fields=flatten_fieldsets(fieldsets))
|
||||||
model_form = self.get_form(
|
|
||||||
request, None, change=False, fields=flatten_fieldsets(fieldsets)
|
|
||||||
)
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = model_form(request.POST)
|
form = model_form(request.POST)
|
||||||
formsets, inline_instances = self._create_formsets(
|
formsets, inline_instances = self._create_formsets(
|
||||||
request,
|
request,
|
||||||
form.instance,
|
original_obj,
|
||||||
change=False,
|
change=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
form_validated = form.is_valid()
|
form_validated = form.is_valid()
|
||||||
if form_validated:
|
if form_validated:
|
||||||
new_object = self.save_form(request, form, change=False)
|
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_model(request, new_object, form, False)
|
||||||
self.save_related(request, form, formsets, False)
|
self.clone_related(request, original_obj, new_object)
|
||||||
change_message = self.construct_change_message(
|
return redirect(reverse("admin:{0}_{1}_change".format(self.opts.app_label, self.opts.model_name), args=[new_object.pk]))
|
||||||
request, form, formsets, False
|
|
||||||
)
|
|
||||||
self.log_addition(request, new_object, change_message)
|
|
||||||
return self.response_add(request, new_object)
|
|
||||||
else:
|
|
||||||
form_validated = False
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
new_obj = self.model()
|
new_obj = self.model()
|
||||||
for field in self.cloneable_fields:
|
for field in self.cloneable_fields:
|
||||||
setattr(new_obj, field, getattr(original_obj, field))
|
setattr(new_obj, field, getattr(original_obj, field))
|
||||||
form = model_form(instance=new_obj)
|
form = model_form(instance=new_obj)
|
||||||
|
formsets, inline_instances = self._create_formsets(request, original_obj, change=False)
|
||||||
formsets, inline_instances = self._create_formsets(
|
|
||||||
request, new_obj, change=True
|
|
||||||
)
|
|
||||||
|
|
||||||
admin_form = helpers.AdminForm(
|
admin_form = helpers.AdminForm(
|
||||||
form,
|
form,
|
||||||
@@ -107,9 +99,7 @@ class CloneModelAdmin(ModelAdmin):
|
|||||||
)
|
)
|
||||||
media = self.media
|
media = self.media
|
||||||
|
|
||||||
inline_formsets = self.get_inline_formsets(
|
inline_formsets = self.get_inline_formsets(request, formsets, inline_instances)
|
||||||
request, formsets, inline_instances
|
|
||||||
)
|
|
||||||
for inline_formset in inline_formsets:
|
for inline_formset in inline_formsets:
|
||||||
media += inline_formset.media
|
media += inline_formset.media
|
||||||
|
|
||||||
@@ -137,3 +127,35 @@ class CloneModelAdmin(ModelAdmin):
|
|||||||
change=False,
|
change=False,
|
||||||
add=True
|
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