Как создать поле модели django mixin

Я пытаюсь сделать общий mixin для полей модели (в отличие от полей формы), init для mixin принимает именованные аргументы. У меня проблемы с созданием экземпляра mixin с другим классом.

здесь код

class MyMixin(object):
    def __init__(self, new_arg=None, *args, **kwargs):
        super(MyMixin, self).__init__(*args, **kwargs)
        print self.__class__, new_arg


class MyMixinCharField(MyMixin, models.CharField):
    pass

...

class MyMixinModelTest(models.Model):
    myfield = MyMixinCharField(max_length=512,new_arg="myarg")

выполнение миграции для этой модели приводит к следующему результату:

<class 'myapp.mixintest.fields.MyMixinCharField'> myarg 
<class 'myapp.mixintest.fields.MyMixinCharField'> None 
<class 'myapp.mixintest.fields.MyMixinCharField'> None 
Migrations for 'mixintest':   
   0001_initial.py:
        - Create model MyMixinModelTest

во-первых, почему init работает 3 раза? Где kwarg 'new_arg' во втором два? Как создать поле mixin для Джанго?

изменить: В отличие от еще вопрос этот вопрос спрашивает о поле mixins, связанный вопрос относится к модель миксины.

2 ответов


во-первых, почему init работает 3 раза?

хотя models.py только после импорта Field объекты, созданные в нем, например...

myfield = MyMixinCharField(max_length=512, new_arg="myarg")

...клонируются несколько раз, что включает вызов конструктора поля с использованием ключевых слов args, с которыми они были первоначально созданы. Вы можете использовать traceback модуль, чтобы увидеть, где это происходит...

import traceback

class MyMixin(object):
    def __init__(self, new_arg=None, *args, **kwargs):
        super(MyMixin, self).__init__(*args, **kwargs)
        print self.__class__, new_arg
        traceback.print_stack()

...что показывает следующее несколько раз в выводе...

  File "django/db/migrations/state.py", line 393, in from_model
    fields.append((name, field.clone()))
  File "django/db/models/fields/__init__.py", line 464, in clone
    return self.__class__(*args, **kwargs)
  File "myproj/myapp/models.py", line 11, in __init__
    traceback.print_stack()

где kwarg 'new_arg во втором два?

если вы изначально позвонил...

myfield = MyMixinCharField(max_length=512, new_arg="myarg")

..."myarg" передается как new_arg параметр to...

def __init__(self, new_arg=None, *args, **kwargs):

...но потому, что вы не передаете этот параметр базовому Field конструктор...

super(MyMixin, self).__init__(*args, **kwargs)

...он не хранится нигде в базовом Field объект, поэтому, когда поле клонируется, то new_arg параметр не передается конструктору.

однако передача этой опции конструктору суперкласса не будет работать, потому что CharField не поддерживает это ключевое слово arg, так что вы получите...

  File "myproj/myapp/models.py", line 29, in MyMixinModelTest
    myfield = MyMixinCharField(max_length=512, new_arg="myarg")
  File "myproj/myapp/models.py", line 25, in __init__
    super(MyMixinCharField, self).__init__(*args, **kwargs)
  File "django/db/models/fields/__init__.py", line 1072, in __init__
    super(CharField, self).__init__(*args, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'new_arg'

Как создать миксин поля для django?

из-за этого поведения клонирования, если вы хотите добавить пользовательские параметры поля, вы должны определить пользовательский deconstruct() метод так, что Django может сериализовать ваш новый вариант...

class MyMixin(object):
    def __init__(self, new_arg=None, *args, **kwargs):
        super(MyMixin, self).__init__(*args, **kwargs)
        self.new_arg = new_arg
        print self.__class__, new_arg

    def deconstruct(self):
        name, path, args, kwargs = super(MyMixin, self).deconstruct()
        kwargs['new_arg'] = self.new_arg
        return name, path, args, kwargs


class MyMixinCharField(MyMixin, models.CharField):
    pass


class MyMixinModelTest(models.Model):
    myfield = MyMixinCharField(max_length=512, new_arg="myarg")

...какие результаты...

<class 'myapp.models.MyMixinCharField'> myarg
<class 'myapp.models.MyMixinCharField'> myarg
<class 'myapp.models.MyMixinCharField'> myarg

поэтому я понял это после многих возиться и перечитывать Django docs на пользовательских полях модели Вам нужен деконструктор вместе с вашим init. Поля Django нуждаются в deconstruct метод для сериализации.

mixin также должен иметь этот метод:

class MyMixin(object):
def __init__(self, new_arg=None, *args, **kwargs):
    self.new_arg = new_arg
    super(MyMixin, self).__init__(*args, **kwargs)

def deconstruct(self):
    name, path, args, kwargs = super(MyMixin, self).deconstruct()
    if self.new_arg is not None:
        kwargs['new_arg'] = self.new_arg
    return name, path, args, kwargs