I'm using Django 2.2, Python 3.7 and my attempt was to set up some common kwargs (say, on_delete
and related_name
) to the OneToOneField
, by a sub class like the following
class MyOneToOneField(models.OneToOneField):
def __init__(self, to):
super().__init__(to, on_delete=models.CASCADE, related_name='extra_data')
And the model class is like
class UserExtraData(models.Model):
entity = MyOneToOneField(USER_MODEL)
However, when running makemigrations , it results in:
TypeError: Couldn't reconstruct field entity on UserExtraData: init () got an unexpected keyword argument 'related_name'
(I tried removing all other fields, so I am pretty sure this is the field that caused the issue).
How can I fix this?
To fix this type of problem, you can change your project settings file. In the settings file, after INSTALLEDD_APPS
:
AUTH_USER_MODEL= '<your app name>.<class name>'
if you tend to specify the related_name
in MyOneToOneField
not to use a arg passed to it. I prefer
class UserExtraData(models.Model):
entity = models.OneToOneField(USER_MODEL, on_delete=models.CASCADE, related_name='extra_data')
or you could try:
class MyOneToOneField(models.OneToOneField):
def __init__(self, to, *args, **kwargs):
super().__init__(to, *args, **kwargs)
# do others
class UserExtraData(models.Model):
entity = MyOneToOneField(USER_MODEL, on_delete=models.CASCADE, related_name='extra_data')
Shouldn't it be like this according to doc 1
class MyOneToOneField(models.OneToOneField):
def __init__(self, to, *args, **kwargs):
kwargs['related_name'] = 'extra_data'
kwargs['on_delete'] = models.CASCADE
super().__init__(to, *args, **kwargs)
Found out it was because Django was going to clone
the field for some reason, after digging a little to Django's suggested by the exception traceback:
File ".../django/db/migrations/state.py", line 414, in from_model
fields.append((name, field.clone()))
File ".../django/db/models/fields/__init__.py", line 493, in clone
return self.__class__(*args, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'related_name'
So take a look at Field.clone
, whose source is
def clone(self):
"""
Uses deconstruct() to clone a new copy of this Field.
Will not preserve any class attachments/attribute names.
"""
name, path, args, kwargs = self.deconstruct()
return self.__class__(*args, **kwargs)
apparently the TypeError
is due to kwargs
contains related_name
and on_delete
, which is returned from deconstruct
.
Then, the docstring of deconstruct
says it is to "Return enough information to recreate the field as a 4-tuple:", so I guess what I missed is that I should have overridden it to provide a kwargs
that does not contain on_delete
and related_name
, but only to
.
So it finally works like this:
class MyOneToOneField(models.OneToOneField):
def __init__(self, to):
super().__init__(to, on_delete=models.CASCADE, related_name='extra_data')
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
return name, path, args, {'to': kwargs['to']}
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.