Наследование OneToOneField приводит к ошибке типа: __init__() получил неожиданный аргумент ключевого слова ‘related_name’

#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']}