Reputation: 2685
I have following models structure:
class Parent(models.Model):
fieldA = models.TextField()
fieldB = models.TextField()
class Child(Parent):
fieldC = models.CharField()
I noticed some unexpected behavior in following code snippet:
child = Child(fieldA = 'Text fieldA', fieldB = 'Text fieldB', fieldC = 'Text fieldC')
child.full_clean()
child.save()
self.assertEqual(Child.objects.count(), 1)
child.delete()
self.assertEqual(Child.objects.count(), 0)
child.full_clean()
child.save()
Not getting into why I add the second child.save()
, assertions are passed, but when I want to save it with this second error it is failing with ValueError
:
ValueError: save() prohibited to prevent data loss due to unsaved related object 'parent_ptr'
At the same time I don't see any such error with following code:
parent = Parent(fieldA = 'Text fieldA', fieldB = 'Text fieldB')
parent.full_clean()
parent.save()
self.assertEqual(Parent.objects.count(), 1)
parent.delete()
self.assertEqual(Parent.objects.count(), 0)
parent.full_clean()
parent.save()
Why is that happening? Is someone able to tell me how I am supposed to fix the first snippet?
Upvotes: 0
Views: 98
Reputation: 13709
What's happening is that you're taking advantage of a feature called "multi-table inheritance".
https://docs.djangoproject.com/en/3.0/topics/db/models/#multi-table-inheritance
What this means is that instead of Child
representing a table with three fields, it only has two fields: fieldC
and a foreign key (OneToOneField
) to a record in Parent
.
When you delete the Child
, it deletes both the Child
and Parent
rows. When you try to save the instance of Child
again, the value of the parent_ptr
OneToOneField
still contains the ID of the old Parent
row, which points to something that doesn't exist anymore.
You probably want to use abstract base classes instead:
https://docs.djangoproject.com/en/3.0/topics/db/models/#abstract-base-classes
class Base(models.Model):
fieldA = models.TextField()
fieldB = models.TextField()
class Meta:
abstract = True
class Parent(Base):
pass
class Child(Base):
fieldC = models.CharField()
Here, there is no link between Parent
and Child
: the table that Child
represents has three fields, and the table that Parent
represents has two fields.
Upvotes: 2