我正在尝试为模型字段(而不是表单字段)创建通用mixin,mixin的init采用命名参数。我遇到了将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'在哪里? 如何为django创建字段mixin?
编辑: 与another question相反,此问题询问 field mixins,链接的问题是指 model mixins。
答案 0 :(得分: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
参数传递给...
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创建字段mixin?
由于此克隆行为,如果您要添加自定义字段选项,则必须定义自定义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
答案 1 :(得分:1)
所以我经过大量的修补和重新阅读django docs on custom model fields后想出来了
你需要一个解构器和你的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