Opened 3 months ago

Closed 8 weeks ago

#36295 closed Bug (fixed)

Unable to Override GenericForeignKey in Inherited Abstract Class

Reported by: Gagan Deep Owned by: Ahmed Nassar
Component: contrib.contenttypes Version: 5.2
Severity: Normal Keywords:
Cc: Gagan Deep Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Attempting to override a GenericForeignKey field in an inherited abstract model does not work as expected. According to the Django documentation, it should be possible to hide a field inherited from an abstract model by setting it to None.

However, this does not seem to work for GenericForeignKey.

I have created a Django project for quick testing.

Steps to replicate:

  1. Define an abstract model with a GenericForeignKey field.
  2. Create another abstract model that inherits from the first and attempt to override the GenericForeignKey field by setting it to None.
  3. Define a concrete model that inherits from the second abstract model.

Code Example

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models


class AbstractBaseModel(models.Model):
    content_type = models.ForeignKey(
        ContentType, on_delete=models.SET_NULL, null=True, blank=True
    )
    object_id = models.PositiveIntegerField(null=True, blank=True)
    related_object = GenericForeignKey("content_type", "object_id")
    description = models.TextField(null=True, blank=True)

    class Meta:
        abstract = True


class AbstractDerivedModel(AbstractBaseModel):
    related_object = None  # Override GenericForeignKey
    description = None

    class Meta:
        abstract = True


class ConcreteEntity(AbstractDerivedModel):
    name = models.CharField(max_length=255)

    class Meta:
        abstract = False

Expected Behavior:

The related_object field in AbstractDerivedModel should override the GenericForeignKey in AbstractBaseModel, making it effectively absent in ConcreteEntity.

Actual Behavior:

Django still considers the GenericForeignKey field from AbstractBaseModel when defining ConcreteEntity, leading to unexpected behavior.

Additional Notes:

Change History (13)

comment:1 by Sarah Boyce, 3 months ago

Triage Stage: UnreviewedAccepted

Thank you!
Using the models as defined above, I was able to replicate

  • tests/generic_relations/tests.py

    diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py
    index e0c6fe2db7..c4163f3c87 100644
    a b  
    11from django.contrib.contenttypes.models import ContentType
    22from django.contrib.contenttypes.prefetch import GenericPrefetch
    3 from django.core.exceptions import FieldError
     3from django.core.exceptions import FieldError, FieldDoesNotExist
    44from django.db.models import Q, prefetch_related_objects
    55from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
    66
    from .models import (  
    99    Animal,
    1010    Carrot,
    1111    Comparison,
     12    ConcreteEntity,
    1213    ConcreteRelatedModel,
    1314    ForConcreteModelModel,
    1415    ForProxyModelModel,
    class GenericRelationsTests(TestCase):  
    780781                self.platypus.latin_name,
    781782            )
    782783
     784    def test_overwriting_generic_fk(self):
     785        with self.assertRaises(FieldDoesNotExist):
     786            ConcreteEntity._meta.get_field('description')
     787        with self.assertRaises(FieldDoesNotExist):
     788            ConcreteEntity._meta.get_field('related_object')
     789
    783790
    784791class ProxyRelatedModelTest(TestCase):

comment:2 by Sarah Boyce, 3 months ago

Component: Database layer (models, ORM)contrib.contenttypes

comment:3 by Gagan Deep, 3 months ago

Hello Sarah! Can I get some pointers how can I approach to solve this issue? I would like to attempt a fix.

comment:4 by Senthil Kumar, 3 months ago

This is my first time working with the Django source code. After reviewing the codebase, I believe the fix below might address the issue. However, I'm unsure whether this is the expected behavior that requires a documentation update or if it's an actual bug.

File db/models/base.py -- line 348

https://github.com/django/django/blob/8ebdd37a0b1755842baae3bd34d388156ad4bf53/django/db/models/base.py#L348

if field.column is not None:  # the exact fix
        field = copy.deepcopy(field)
        if not base._meta.abstract:
               field.mti_inherited = True
               new_class.add_to_class(field.name, field)

Apologies, but I'm unsure how to present this from the codebase. It would be helpful if someone could guide me on how to share the diff, similar to the comments above. Thanks!

Version 1, edited 3 months ago by Senthil Kumar (previous) (next) (diff)

comment:5 by Ahmed Nassar, 3 months ago

Owner: set to Ahmed Nassar
Status: newassigned

comment:6 by Ahmed Nassar, 2 months ago

Has patch: set

comment:7 by Arne, 2 months ago

The PR looks fine to me. Just waiting for some minor, optional adjustments before _ready for checkin_.

comment:8 by Arne, 2 months ago

Triage Stage: AcceptedReady for checkin

comment:9 by Ahmed Nassar, 2 months ago

Can anyone review? Looking forward to merging this PR.

comment:10 by Sarah Boyce, 8 weeks ago

Patch needs improvement: set
Triage Stage: Ready for checkinAccepted

comment:11 by Ahmed Nassar, 8 weeks ago

Patch needs improvement: unset

comment:12 by Sarah Boyce, 8 weeks ago

Triage Stage: AcceptedReady for checkin

comment:13 by Sarah Boyce <42296566+sarahboyce@…>, 8 weeks ago

Resolution: fixed
Status: assignedclosed

In 84e91262:

Fixed #36295, Refs #24305 -- Allowed overriding GenericForeignKey fields on abstract models.

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