Opened 33 hours ago

Closed 3 hours ago

#36183 closed Bug (invalid)

Model o2o inheritance with abstract models does not "evaluate" a lazy relationship

Reported by: BeryCZ Owned by:
Component: Database layer (models, ORM) Version: 5.1
Severity: Normal Keywords:
Cc: BeryCZ Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When I define models like this

class AbstractPage(models.Model):
    mtime = models.DateTimeField(
        verbose_name="Last modification at",
        auto_now=True,
    )

    class Meta:
        abstract = True


class AbstractPageType(models.Model):
    page = models.OneToOneField(
        "Page",
        verbose_name="ID",
        primary_key=True,
        on_delete=models.CASCADE,
        related_name="%(class)s",
        parent_link=True,
    )
    title = models.CharField(
        verbose_name="Title",
        max_length=255,
        null=False,
        blank=False,
    )

    class Meta:
        abstract = True


class Page(AbstractPage):
    pass


class ArticlePage(AbstractPageType, Page):
    pass

And then I do

ArticlePage.objects.order_by("-mtime").all()

I get

  File "django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/query.py", line 1701, in order_by
    obj.query.add_ordering(*field_names)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/sql/query.py", line 2249, in add_ordering
    self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/sql/query.py", line 1777, in names_to_path
    path_to_parent = opts.get_path_to_parent(model)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/options.py", line 755, in get_path_to_parent
    targets = (final_field.remote_field.get_related_field(),)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "django/db/models/fields/reverse_related.py", line 311, in get_related_field
    field = self.model._meta.get_field(self.field_name)
            ^^^^^^^^^^^^^^^^

Exception Type: AttributeError
Exception Value: 'str' object has no attribute '_meta'

It works when I do

class ArticlePage(AbstractPageType, Page):
    page = models.OneToOneField(
        "Page",
        verbose_name="ID",
        primary_key=True,
        on_delete=models.CASCADE,
        related_name="%(class)s",
        parent_link=True,
    )

but I would prefer not to have to define page field in every PageType.

Change History (1)

comment:1 by Natalia Bidart, 3 hours ago

Resolution: invalid
Status: newclosed

Hello BeryCZ, thank you for this report. I've analyzed your models and I fear that the issue is the complex inheritance that you have proposed. Specifically, you cannot have a model (ArticlePage) that both inherits from Page and has a OneToOneField to Page. Without understanding your use case, it feels like a possible solution would be for your "pages" to have a FK to "page types" (instead of the other way around with the 1-1 field).

Your model definitions create an inheritance conflict:

  1. AbstractPageType expects a separate Page instance (this means every model that inherits from AbstractPageType must have a separate Page instance linked via the page field).
  2. ArticlePage is also inheriting Page, which means Django expects ArticlePage itself to be a Page instance.

Django sees two competing ways to resolve ArticlePage:

  • ArticlePage is a Page (due to model inheritance), or
  • ArticlePage has a separate Page instance (due to the page OneToOneField).

The error is caused because Django tries to retrieve the mtime field, which exists on Page (inherited via AbstractPage). But because ArticlePage also has a OneToOneField called page, Django gets confused about how to resolve mtime. Instead of treating page as a relationship to another Page instance, Django interprets it as a string.

Solutions are either to remove the direct inheritance to Page or remove the page field from AbstractPageType.

If you have further questions, there are several user support channels available. Please refer to TicketClosingReasons/UseSupportChannels for ways to get help.

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