django-nested-admin icon indicating copy to clipboard operation
django-nested-admin copied to clipboard

KeyError: 'changed_data' in nested_admin.formsets.save_existing_objects

Open scottgigante opened this issue 1 year ago • 5 comments

nested_admin.formsets.save_existing_objects seems to be accessing a field that sometimes does not exist. I'm not quite sure how to generate a MRE here, but this is the traceback from my app logs.

Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/forms/models.py", line 1538, in to_python
value = self.queryset.get(**{key: value})
File "/usr/local/lib/python3.11/site-packages/django/db/models/query.py", line 637, in get
raise self.model.DoesNotExist(
MyModel.DoesNotExist: MyModel matching query does not exist.

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/nested_admin/formsets.py", line 304, in save_existing_objects
pk_value = form.fields[pk_name].clean(raw_pk_value)
File "/usr/local/lib/python3.11/site-packages/django/forms/fields.py", line 198, in clean
value = self.to_python(value)
File "/usr/local/lib/python3.11/site-packages/django/forms/models.py", line 1540, in to_python
raise ValidationError(
django.core.exceptions.ValidationError: ['Select a valid choice. That choice is not one of the available choices.']

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/utils/decorators.py", line 46, in _wrapper
return bound_method(*args, **kwargs)
File "/usr/local/lib/python3.11/site-packages/django/utils/decorators.py", line 134, in _wrapper_view
response = view_func(request, *args, **kwargs)
File "/usr/local/lib/python3.11/site-packages/django/contrib/admin/options.py", line 1747, in changeform_view
return self._changeform_view(request, object_id, form_url, extra_context)
File "/usr/local/lib/python3.11/site-packages/django/contrib/admin/options.py", line 1799, in _changeform_view
self.save_related(request, form, formsets, not add)
File "/usr/local/lib/python3.11/site-packages/django/contrib/admin/options.py", line 1255, in save_related
self.save_formset(request, form, formset, change=change)
File "/usr/local/lib/python3.11/site-packages/django/contrib/admin/options.py", line 1243, in save_formset
formset.save()
File "/usr/local/lib/python3.11/site-packages/nested_admin/formsets.py", line 154, in save
instance = self.get_saved_instance_for_form(form, commit, form_instances)
File "/usr/local/lib/python3.11/site-packages/nested_admin/formsets.py", line 237, in get_saved_instance_for_form
instances = self.save_existing_objects([form], commit)
File "/usr/local/lib/python3.11/site-packages/nested_admin/formsets.py", line 314, in save_existing_objects
form.__dict__["changed_data"].append(pk_name)
KeyError: 'changed_data'

scottgigante avatar Feb 05 '24 12:02 scottgigante

@scottgigante did you ever figure this out?

jordanvs avatar Mar 12 '24 18:03 jordanvs

Unfortunately I did not -- I haven't been able to reproduce it.

scottgigante avatar Mar 14 '24 01:03 scottgigante

For me it happened when i used readonly inlines and changed the non-inline object:

class ReadOnlyNestedTabularInline(NestedTabularInline):
    extra = 0

    def has_add_permission(self, request, obj=None):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None):
        return False

And a custom pk field in the inline model:

class Destination(TimeStampedModel):
    uuid = models.UUIDField(primary_key=True)

Barsoomx avatar Apr 08 '24 12:04 Barsoomx

I managed to address my error. Looking up at the exception stack, the initial exception thrown was a Model not found error. I have some complex model inheritance, polymorphism, and soft deletion going on, so without diving into all that:

My issue was caused by one of the inline models of my ModelAdmin not being accessible/found by the methods that save because it was actually soft-deleted. It was present in the queryset that displayed records on the admin page (bug), but not in the queryset that was used to save objects (expected).

Hence the front end was sending the soft-deleted object id in its payload that could not be found for updating, resulting in exceptions that propagated down to this error.

jordanvs avatar Jul 10 '24 17:07 jordanvs

Thanks @jordanvs for the follow-up! I also use soft deletion to filter querysets, so likely this is the same cause.

scottgigante avatar Jul 11 '24 02:07 scottgigante