Как создать поле модели 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