Opened 2 months ago

Closed 2 months ago

#35820 closed Uncategorized (invalid)

Non-editable error for a GenericForeignKey in an ModelForm when updating to version 5.1

Reported by: Arthur Hanson Owned by:
Component: Uncategorized Version: 5.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Arthur Hanson)

As per discussion at https://forum.djangoproject.com/t/non-editable-error-for-a-genericforeignkey-in-an-modelform-when-updating-to-version-5-1/35033

If you have a GFK defined in a model, then create a Form that has a field of the same name you will get a FieldError:

django.core.exceptions.FieldError: 'my_generic_foreign_model' cannot be specified for my_model_form model form as it is a non-editable field

This worked in Django 5.0, but fails when updating to Django 5.1.

It appears when you have a model form with a field that is the same name as in the model (in this case a GFK) it takes the read-only from the model field instead of the form field. For example we have:

class EventRule():
    ...
    action_object_type = models.ForeignKey(
        to='contenttypes.ContentType',
        related_name='eventrule_actions',
        on_delete=models.CASCADE
    )
    action_object_id = models.PositiveBigIntegerField(
        blank=True,
        null=True
    )
    action_object = GenericForeignKey(
        ct_field='action_object_type',
        fk_field='action_object_id'
    )

Then in the form:

class EventRuleForm():
    ...
    action_object = forms.CharField(
        label=_('Action object'),
        required=True,
        help_text=_('Webhook name or script as dotted path module.Class')
    )

In this case we handle action_object in the clean method and use it to set instance.action_object. But now in Django 5.1 it throws the django.core.exceptions.FieldError shown above.

Not sure if this is an undocumented change on purpose or a bug.

Change History (2)

comment:1 by Arthur Hanson, 2 months ago

Description: modified (diff)

comment:2 by Sarah Boyce, 2 months ago

Resolution: invalid
Status: newclosed

This works fine for me

 
+class TaggedItemWithContentObjectForm(forms.ModelForm):
+    content_object = forms.CharField(max_length=10)
+
+    class Meta:
+        model = TaggedItem
+        fields = ["tag", "content_type", "object_id"]
+
+    def clean(self):
+        # Ignore content_object.
+        self.cleaned_data.pop("content_object")
+        return self.cleaned_data
+
+
 class GenericInlineFormsetTests(TestCase):
     def test_output(self):
         GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1)
@@ -217,6 +230,18 @@ class GenericInlineFormsetTests(TestCase):
         with self.assertRaisesMessage(Exception, msg):
             generic_inlineformset_factory(BadModel, TaggedItemForm)
 
+    def test_gfk_form(self):
+        quartz = Mineral.objects.create(name="Quartz", hardness=7)
+        ctype = ContentType.objects.get_for_model(quartz)
+        data = {
+            "tag": "lizard",
+            "content_type": ctype.pk,
+            "object_id": quartz.pk,
+            "content_object": "ignored",
+        }
+        form = TaggedItemWithContentObjectForm(data=data)
+        self.assertEqual(form.is_valid(), True)
+
     def test_save_new_uses_form_save(self):

I get an error if I add content_object to fields which you don't need to do

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