From 8b11bf0f53fabd411971814984379fbf642ed94b Mon Sep 17 00:00:00 2001 From: Bartosz Dabrowski Date: Mon, 19 Aug 2024 15:18:15 +0200 Subject: [PATCH] - Fixed bug in checking the existence of custom __init__ method in an object. The __init__ wasn't called previously. - Added unit-tests for #615 - update changelog Co-authored-by: Bartosz Dabrowski Co-authored-by: Brian Kohan --- docs/changelog.rst | 1 + src/polymorphic/query.py | 2 +- .../tests/migrations/0001_initial.py | 50 +++++++++++++------ src/polymorphic/tests/models.py | 20 ++++++++ src/polymorphic/tests/test_orm.py | 13 ++++- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 9d978db9..1af8b2a5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,7 @@ Changelog v4.2.0 (2025-12-04) ------------------- +* Fixed `The objects which were transmogrified aren't initialized correctly if they implement __init__ method. `_ * Implemented `Defer to chunk_size parameter on .iterators for fetching get_real_instances() `_ * Fixed `Show full admin context (breadcrumb and logout nav) in model type selection admin form `_ * Fixed `Issue with Autocomplete Fields in StackedPolymorphicInline.Child Inline `_ diff --git a/src/polymorphic/query.py b/src/polymorphic/query.py index e09e2d9a..e2416ba7 100644 --- a/src/polymorphic/query.py +++ b/src/polymorphic/query.py @@ -87,7 +87,7 @@ def transmogrify(cls, obj): """ Upcast a class to a different type without asking questions. """ - if "__init__" not in obj.__dict__: + if "__init__" not in obj.__class__.__dict__: # Just assign __class__ to a different value. new = obj new.__class__ = cls diff --git a/src/polymorphic/tests/migrations/0001_initial.py b/src/polymorphic/tests/migrations/0001_initial.py index ad1e92a7..620f48a3 100644 --- a/src/polymorphic/tests/migrations/0001_initial.py +++ b/src/polymorphic/tests/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.26 on 2025-12-02 16:47 +# Generated by Django 4.2.26 on 2025-12-03 23:29 from django.db import migrations, models import django.db.models.deletion @@ -12,8 +12,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), ('contenttypes', '0002_remove_content_type_name'), + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ @@ -56,6 +56,18 @@ class Migration(migrations.Migration): }, bases=(polymorphic.showfields.ShowFieldTypeAndContent, models.Model), ), + migrations.CreateModel( + name='Duck', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + ), migrations.CreateModel( name='Enhance_Base', fields=[ @@ -315,6 +327,17 @@ class Migration(migrations.Migration): }, bases=('tests.blogbase',), ), + migrations.CreateModel( + name='BlueHeadDuck', + fields=[ + ('duck_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='tests.duck')), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + bases=('tests.duck',), + ), migrations.CreateModel( name='CustomPkInherit', fields=[ @@ -833,18 +856,6 @@ class Migration(migrations.Migration): 'base_manager_name': 'objects', }, ), - migrations.CreateModel( - name='Duck', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=30)), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype')), - ], - options={ - 'abstract': False, - 'base_manager_name': 'objects', - }, - ), migrations.CreateModel( name='DateModel', fields=[ @@ -1108,6 +1119,17 @@ class Migration(migrations.Migration): }, bases=(polymorphic.showfields.ShowFieldTypeAndContent, models.Model), ), + migrations.CreateModel( + name='PurpleHeadDuck', + fields=[ + ], + options={ + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('tests.blueheadduck', models.Model), + ), migrations.CreateModel( name='Model2D', fields=[ diff --git a/src/polymorphic/tests/models.py b/src/polymorphic/tests/models.py index 2933b853..2d9c3fee 100644 --- a/src/polymorphic/tests/models.py +++ b/src/polymorphic/tests/models.py @@ -535,3 +535,23 @@ def __str__(self): class Team(models.Model): team_name = models.CharField(max_length=100) user_profiles = models.ManyToManyField(UserProfile, related_name="user_teams") + + +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 diff --git a/src/polymorphic/tests/test_orm.py b/src/polymorphic/tests/test_orm.py index 25d12e03..37aa52d9 100644 --- a/src/polymorphic/tests/test_orm.py +++ b/src/polymorphic/tests/test_orm.py @@ -23,7 +23,6 @@ ChildModelWithManager, CustomPkBase, CustomPkInherit, - Duck, Enhance_Base, Enhance_Inherit, InitTestModelSubclass, @@ -90,6 +89,8 @@ UUIDPlainC, UUIDProject, UUIDResearchProject, + Duck, + PurpleHeadDuck, ) @@ -1411,3 +1412,13 @@ def test_iteration(self): # known deletion issue with oracle # https://github.com/jazzband/django-polymorphic/issues/673 pass + + def test_transmogrify_with_init(self): + 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"