diff --git a/busManager/coord/admin.py b/busManager/coord/admin.py index f8d9aed..796cfc9 100644 --- a/busManager/coord/admin.py +++ b/busManager/coord/admin.py @@ -171,15 +171,17 @@ class FamilyInline(admin.StackedInline): model = Family classes = ['collapse'] extra = 0 + clone_parent = "traveller" class TravellerRouteInline(admin.TabularInline): model = TravellerRoute extra = 0 + clone_parent = "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_filter = [ "is_active", "school", "year_level", "eligibility_status", "bus_stops__bus", "shuttle", @@ -190,6 +192,7 @@ class TravellerAdmin(MyImportExportModelAdmin, admin.ModelAdmin, TravellerRollMi title="End date" )) ] + cloneable_fields = ["last_name"] search_fields = ["first_name", "last_name", "address"] inlines = [FamilyInline, TravellerRouteInline] readonly_fields = ["travel_start_date", "travel_end_date", "fare_paying", "created_on", "last_edit", "is_active", "address"] diff --git a/busManager/coord/adminClone.py b/busManager/coord/adminClone.py index b798996..cd9759f 100644 --- a/busManager/coord/adminClone.py +++ b/busManager/coord/adminClone.py @@ -1,9 +1,11 @@ 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.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 +from django.core.exceptions import PermissionDenied, FieldDoesNotExist class CloneModelAdmin(ModelAdmin): @@ -48,55 +50,45 @@ class CloneModelAdmin(ModelAdmin): 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 + 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 ) - fieldsets = self.get_fieldsets(request, None) + 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 - model_form = self.get_form( - request, None, change=False, fields=flatten_fieldsets(fieldsets) - ) + 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, - form.instance, + original_obj, 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 - + 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, new_obj, change=True - ) + formsets, inline_instances = self._create_formsets(request, original_obj, change=False) admin_form = helpers.AdminForm( form, @@ -107,9 +99,7 @@ class CloneModelAdmin(ModelAdmin): ) media = self.media - inline_formsets = self.get_inline_formsets( - request, formsets, inline_instances - ) + inline_formsets = self.get_inline_formsets(request, formsets, inline_instances) for inline_formset in inline_formsets: media += inline_formset.media @@ -137,3 +127,35 @@ class CloneModelAdmin(ModelAdmin): 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