django-simple-history icon indicating copy to clipboard operation
django-simple-history copied to clipboard

Django polymorphic compatibility

Open 73VW opened this issue 8 months ago • 0 comments

Hello everyone,

First of all, let me thank you for this awesome package. It was a real pleasure to use it to track changes in my models.

Problem Statement

I have tried using it with a pair of models that use django-polymorphic and couldn't manage to make it work how I wanted.

Here's a minimal reproducible example of my models:

from polymorphic.models import PolymorphicModel

class BaseStage(PolymorphicModel):
    name = models.CharField(max_length=256)
    display_name = models.CharField(null=True, max_length=256)
    history = HistoricalRecords(inherit=True)


class Company(BaseStage):
    basestage_ptr = models.OneToOneField(
        BaseStage,
        parent_link=True,
        related_name="company_ptr",
        on_delete=models.RESTRICT,
    )

class Stage(BaseStage):
    parent = HistoricForeignKey(BaseStage, on_delete=models.RESTRICT, related_name="children")
    company = models.ForeignKey("Company", on_delete=models.RESTRICT, default=1, related_name="stages")

The goal was to store the hierarchy of a company in a tree. Our requirements were the following:

  • Root of the tree can only be a company instance
  • Nodes are all linked to their parent and to the company

Describe the solution you'd like

I would have loved to be able to do:

BaseStage.history.filter(Q(Company___pk=company.id) | Q(Stage___company=company))

Just like in django polymorphic.

This would avoid doing something like this:

qs = Stage.history.filter(
    id__in=BaseStage.objects.filter(Q(Company___pk=pk) | Q(Stage___company=pk)).values_list("id", flat=True),
)

dates = qs.values_list("history_date", flat=True)
dates.extend(Company.history.filter(pk=pk).values_list("history_date", flat=True)
 

When one could do:

BaseStage.history.filter(Q(Company___pk=company.id) | Q(Stage___company=company)).values_list("history_date", flat=True)

In order to obtain all the history dates of the whole tree.

Describe alternatives you've considered

I have tried to use Polymorphic as base but couldn't manage to make it work:


class PolymorphicHistoricalRecords(HistoricalRecords):
    def __init__(self, *args, bases=(PolymorphicModel,), **kwargs):
        return super().__init__(*args, bases=bases, **kwargs)

class BaseStage(PolymorphicModel):
    history = PolymorphicHistoricalRecords(inherit=True)
    

It seems that the HistoricXX classes never inherit from PolymorphicModel.

Additional context

I am pretty sure that this would need changes about field naming in either this module or in django polymorphic because django polymorphic needs a polymorphic_ctype column, creating a history on a polymorphic model also generates it and having a polymorphic history on a polymorphic model would require this column twice.

73VW avatar Jun 26 '24 12:06 73VW