#python #django #django-models
#python #django #django-models
Вопрос:
Я использую Django 2.2, Python 3.7, и моя попытка состояла в том, чтобы настроить некоторые общие kwargs (скажем, on_delete
и related_name
) для OneToOneField
, с помощью подкласса, подобного следующему
class MyOneToOneField(models.OneToOneField):
def __init__(self, to):
super().__init__(to, on_delete=models.CASCADE, related_name='extra_data')
И класс модели выглядит так
class UserExtraData(models.Model):
entity = MyOneToOneField(USER_MODEL)
Однако при запуске makemigrations это приводит к:
Ошибка типа: не удалось восстановить объект поля в UserExtraData: init() получил неожиданный аргумент ключевого слова ‘related_name’
(Я попытался удалить все остальные поля, поэтому я почти уверен, что именно это поле вызвало проблему).
Как я могу это исправить?
Ответ №1:
Чтобы устранить проблему такого типа, вы можете изменить файл настроек вашего проекта. В файле настроек, после INSTALLEDD_APPS
:
AUTH_USER_MODEL= '<your app name>.<class name>'
Ответ №2:
Если вы склонны указывать related_name
in MyOneToOneField
, не используйте переданный ему аргумент. Я предпочитаю
class UserExtraData(models.Model):
entity = models.OneToOneField(USER_MODEL, on_delete=models.CASCADE, related_name='extra_data')
Или вы могли бы попробовать:
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')
Комментарии:
1. Спасибо. Это решило исключение, но не проблему, из-за которой я хотел бы отклонить аргументы,
on_delete
которые должны быть переданы в это поле.
Ответ №3:
Возможно, так и должно быть, согласно документации:
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)
Комментарии:
1. Спасибо. Это решило исключение, но не проблему, которую я хотел бы запретить аргументам
on_delete
, которые должны передаваться в это поле. Нашел решение самостоятельно. Было бы неплохо, если бы вы могли предоставить более подробную информацию или цитаты о том, что предлагается в какой части документа.2. Я рад, что вы нашли решение. Вы сделали хорошее предложение. Я обновил ссылку.
Ответ №4:
Я узнал, что это было потому, что Джанго clone
по какой-то причине собирался на поле. Немного покопавшись, Django предложил это с помощью трассировки исключений:
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'
Итак, взгляните Field.clone
, чей источник
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)
По-видимому TypeError
, это связано с kwargs
тем, что содержит related_name
и on_delete
, которое возвращается из deconstruct
.
Затем в строке документа deconstruct
говорится, что он должен «возвращать достаточно информации, чтобы воссоздать поле как 4-кортеж:», поэтому, я думаю, я пропустил, что я должен был переопределить его, чтобы предоставить a kwargs
, который не содержал on_delete
и related_name
, но только to
.
Итак, это, наконец, работает так:
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']}