jazzband / django-polymorphic

Improved Django model inheritance with automatic downcasting
https://django-polymorphic.readthedocs.io
Other
1.66k stars 280 forks source link

The objects which were transmogrified aren't initialized correctly if they implement `__init__` method. #615

Open bdabrowski opened 3 months ago

bdabrowski commented 3 months ago

When the real_object has a different class from real_class, it gets transmogrified into real_class.

When it implements __init__, it is not called.

https://github.com/jazzband/django-polymorphic/pull/614

bdabrowski commented 3 months ago

In the current situation, we experience demonstrated issue:

# models for https://github.com/jazzband/django-polymorphic/issues/615

class Duck(PolymorphicModel):
    name = models.CharField(max_length=30)

class BlueHeadDuck(Duck):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.color = "blue"

class HomeDuck(models.Model):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.home = "Duckburg"

    class Meta:
        abstract = True

class PurpleHeadDuck(HomeDuck, BlueHeadDuck):
    class Meta:
        proxy = True

Test

    def test_transmogrify_with_init(db):
        pur = PurpleHeadDuck.objects.create()
        assert pur.color == "blue"
        assert pur.home == "Duckburg"

        pur = Duck.objects.get(id=pur.id)
        assert pur.color == "blue"
        # issues/615 fixes following line:
>       assert pur.home == "Duckburg"
E       AttributeError: 'PurpleHeadDuck' object has no attribute 'home'

The code to handle __init__ was there, but it wasn't run due to a condition error.

The code was there, so I assume it was intended for cases such as this one. But I haven't seen this condition fulfilled in any other existing unit test.

I know that Django triggers __init__ only on concrete models but the existence of __init__ with super makes sure it is called for each eventually. I didn't observe this for polymorphic and hope to fix it with my change.