如何创建一个django模型字段mixin

时间:2016-07-01 21:30:14

标签: python django

我正在尝试为模型字段(而不是表单字段)创建通用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。

2 个答案:

答案 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