Opened 5 years ago

Closed 5 years ago

#29981 closed Bug (fixed)

"Please correct the error below." (with no errors displayed) when changing an inline that has a one-to-one relation to the parent that uses to_field

Reported by: Bernie Owned by: Patrik Sletmo
Component: contrib.admin Version: 2.1
Severity: Normal Keywords:
Cc: Sergey Fedoseev Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Tim Graham)

Code sample:

models.py:

from django.db import models

class Entry(models.Model):
    slug = models.SlugField(max_length=80, unique=True)
    title = models.CharField(max_length=255, blank=True, null=True)

class EntryDetail(models.Model):
    entry = models.OneToOneField(
        Entry,
        to_field='slug',
        primary_key=True,
        on_delete=models.CASCADE
    )
    description = models.TextField(blank=True, null=True)

admin.py

from django.contrib import admin
from .models import Entry, EntryDetail

class EntryDetailInline(admin.StackedInline):
    model = EntryDetail

@admin.register(Entry)
class EntryAdmin(admin.ModelAdmin):
    inlines = [EntryDetailInline]

    def get_readonly_fields(self, request, obj=None):
        # Even with EntryDetail.entry_id ON UPDATE: CASCADE set on DB-level
        # changing Entry.slug through the admin-change-form with EntryDetail-inline fails.
        # Set slug readonly as workaround. Don't know if this is worth fixing.
        readonly_fields = super().get_readonly_fields(request, obj)
        if hasattr(obj, 'entrydetail') and 'slug' not in readonly_fields:
            readonly_fields += ('slug',)
        return readonly_fields

Steps to reproduce the bug:

In the admin:

  • add an Entry
  • fill slug, title, and "Entry detail" description
  • save and continue editing
  • change "Entry detail" description
  • save
  • Error message in admin: "Please correct the error below." (with no errors listed). formset[0].errors is [{'entry': ['The inline value did not match the parent instance.']}] which comes from InlineForeignKeyField value is aaa and orig is 1.

Workaround:

in models.py replace

class EntryDetailInline(admin.StackedInline):
    model = EntryDetail

with:

from django.forms import BaseInlineFormSet

class EntryDetailFormSet(BaseInlineFormSet):
    def add_fields(self, form, index):
        super().add_fields(form, index)
        related_name = self._pk_field.remote_field.related_name or self._pk_field.remote_field.name
        if hasattr(self.instance, related_name):
            form.fields[self._pk_field.name].to_field = self._pk_field.remote_field.field_name

class EntryDetailInline(admin.StackedInline):
    model = EntryDetail
    formset = EntryDetailFormSet

Change History (7)

comment:1 by Tim Graham, 5 years ago

Description: modified (diff)
Summary: invalid_choice error when changing InlineModel with a OneToOne relation via to_field"Please correct the error below." (with no errors displayed) when changing an inline that has a one-to-one relation to the parent that uses to_field
Triage Stage: UnreviewedAccepted

I reproduced the issue on master (78fc64578a8715b9812075bbebc829c1251c07fa). I removed AnotherEntryDetailInline from the description as it doesn't seem required to reproduce the issue.

comment:2 by Sergey Fedoseev, 5 years ago

Cc: Sergey Fedoseev added

comment:3 by Patrik Sletmo, 5 years ago

Owner: changed from nobody to Patrik Sletmo
Status: newassigned

I've managed to reproduce the issue and will start to look for a solution.

in reply to:  3 comment:4 by Bernie, 5 years ago

Replying to Patrik Sletmo:

I've managed to reproduce the issue and will start to look for a solution.

Start looking at django.forms.models.BaseInlineFormSet.add_fields:

form.fields[name] = InlineForeignKeyField(self.instance, **kwargs)

When there is an existing inline-instance, InlineForeignKeyField needs to know about the to_field.

comment:5 by Patrik Sletmo, 5 years ago

Thanks Bernie!

There is currently some code for correctly setting the to_field here, but it appears that this only works for foreign key fields when they are not also the primary key of the contained model. The code linked above was introduced 10 years ago in this commit and I'm beginning to wonder if the placement was incorrect back then.

I've tried moving the linked if-statement outside of its surrounding block, and this seems to fix the issue while not breaking any existing test cases. I'll look through the code a bit more to see if this change introduces any issue or if there exist a better way of doing this. If this turns out to be a reasonable solution I'll make sure to submit a patch tomorrow.

comment:6 by Patrik Sletmo, 5 years ago

Has patch: set

PR

It has been a busy week so I haven't really been able to spend any time on this until now. I have submitted a patch with a fix for the issue together with a regression test.

Last edited 5 years ago by Tim Graham (previous) (diff)

comment:7 by Tim Graham <timograham@…>, 5 years ago

Resolution: fixed
Status: assignedclosed

In 14e2b1b0:

Fixed #29981 -- Fixed inline formsets with a OnetoOneField primary key that uses to_field.

Note: See TracTickets for help on using tickets.
Back to Top