Recently I've faced an issue with Django, model inheritance and how the creation of model instances works. Suppose I have the following (basic) setup:
class InviteBaseManager(models.Manager):
def create(self):
new_code = # create some kind of unique code, not really relevant here.
return super().create(code=new_code)
class InviteBase(models.Model):
code = models.CharField(max_length=10, blank=False, null=False, unique=True)
creationDate = models.DateTimeField(default=timezone.now())
objects = InviteBaseManager()
class PartyInviteManager(models.Manager):
def create(self, name):
# method 1
newInvite = InviteBase.objects.create()
print(newInvite.code) # is definitly set, lets assume "ABCD"
# as expexted, the "InviteBase" table has one row with code "ABCD" and
# default creationDate
newPartyInvite = super().create(partyName=name, invite=newInvite)
print(newPartyInvite.invite.code) # is EMPTY, prints nothing
# In fact, when looking at the db, there is still only *one* row in the table "InviteBase",
# with an *empty* code field and a default creationDate field.
return newPartyInvite
#method 2
newPartyInvite = super().create(partyName=name)
# creates the InviteBase instance implicitly, again, newPartyInvite.invite.code is empty.
# fill newPartyInvite.invity.code manually.
return newPartyInvite
class PartyInvite(InviteBase):
#Isn't blank=False and null=False unnecessary? A child should probably not exist with no parent?
invite = models.OneToOneField(InviteBase, parent_link=True, on_delete=models.CASCADE, null=False, blank=False)
partyName = models.CharField(...)
objects = PartyInviteManager()
So the question is: How can I pass an already existing instance of the base class inside the create
method of my PartyInviteManager
? Even when using method 1, the existing instance that I pass along seems to be overwritten. A new one is created with the default value. Interestingly enough, this violates the constraints that code
cannot be blank or NULL
.
This behaviour seems a bit odd to me? Can someone point out what I am missing here?
To clarify: I know that I should usually use **kwargs
in the create
methods and that inheritance might not be the ideal use case here, but I'm just very curious about this behaviour. I know that this kind of model inheritance wont even create a pk
for the child model (because holding a OneToOneField
to the parent class effectively acts as a pk
anyway), but why would it be impossible to pass a manually created instance as parent? Am I not allowed to use inheritance for my use-case?
I think I've found the correct way of doing this:
class InviteBaseManager(models.Manager):
def create(self, **kwargs):
kwargs['code'] = #createCode
return super().create(**kwargs)
class InviteBase(models.Model):
code = models.CharField(max_length=10, blank=False, null=False, unique=True)
creationDate = models.DateTimeField(default=timezone.now())
objects = InviteBaseManager()
class PartyInviteManager(InviteBaseManager):
def create(self, name):
return = super().create(partyName=name)
class PartyInvite(InviteBase):
invite = models.OneToOneField(InviteBase, parent_link=True, on_delete=models.CASCADE, null=False, blank=False)
partyName = models.CharField(...)
objects = PartyInviteManager()
The PartyInviteManager
now inherits from the InviteManager
as well. When calling super.create()
from the child manager, the base manager gets called, appends the code
field and everything works as expected. I also found out, that if the PartyInviteManager
does not inherit from the InviteBaseManager
, the create
method of InviteBaseManager
does not get called when creating a new PartyInvite
. This seems very odd to me.
Of course, the easier way would have been to create the code as default value via a function, like that:
def createCode():
return "ABCD" # add fancy code creation magic here.
class InviteBase:
code = models.CharField(max_length=128, blank=False, null=False, default=createCode)
But if, depending on the child class, the code needs additional information (such as invited members or whatever), this approach would not work anymore.
On a side note, interestingly enough the following code snippet:
invite = PartyInviteManager.objects.create(partyName='Birthday')
print(invite.invite.code)
print(invite.code)
produces the following output:
ABCD
ABCD
In the create
method of the PartyInviteManager
, one can directly use code=XXXX
to pass along a string for the InviteBase
model, invite_code
on the other hand does not work.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.