0

我的情况如下:

class MyModel(models.Model):
    field 1 = models.CharField(kwarg1 = value1, kwarg2 = value2, ...)

class MyCustomCharField(forms.CharField):
    def __init__(self, mandatarg1, ckwarg1 = cdefault1, ...):
       ...
    ...

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['field1', ...]

    field1 = MyCustomCharField(mandatvalue1, ckwarg1 = cvalue1, ...)

问题是:在我的形式中,我失去了kwarg1 = default1, kwarg2 = default2, ...,例如可以是help_text="blablabla"and max_length=255

有人想像一个正确的方法来保留这个默认参数吗?


我试过了:

field1 = MyModel._meta.get_field('field1').formfield(form_class=MyCustomCharField, ckwarg1 = cvalue1)

它工作得很好,但我不能传递 mandatvalue1 属性,所以我不能做我需要的!


并且

field1 = MyCustomCharField(mandatvalue1, ckwarg1 = cvalue1, ..., **MyModel._meta.get_field('field1').__dict__)

但是在这里我的字典中有太多的键,Django 崩溃了..


最后,我可以这样做:

field1 = MyCustomCharField(mandatvalue1, ckwarg1 = cvalue1, ..., help_text = MyModel._meta.get_field('field1').help_text, ...)

但是我有很多值得复制的价值,而且它几乎不可维护..有什么想法吗?我有点疯狂,所以我真的很想用一种干净的方式来做这件事(如果不是太多的话)

4

1 回答 1

1

您可以使用**kwargs来捕获和传递您未直接指定的所有关键字参数。kwargs可以是任何东西,但约定是使用该名称。它看起来如下:

def __init__(self, mandatarg1, ckwarg1=cdefault1, **kwargs)
    # do something
    super(MyCustomCharField, self).__init__(**kwargs)
    # do something else

由于双星号**,所有未明确定义的关键字参数都将打包在一个dict名为kwargs. 通过传递**kwargs给 super 的调用,这些值在__init__使用这些关键字参数调用 super 的函数之前再次解包。

例如,您可以调用以下命令:

field1 = MyCustomCharField(mandatvalue1, max_length=255, ckwarg1 = cvalue1)

kwargs然后看起来像这样:

kwargs = {'max_length': 255}

并且 python 将处理对 super 的调用,就好像你调用了:

super(MyCustomCharField, self).__init__(max_length=255)

对于带有单个星号的位置参数也可以这样做,惯例是使用*args. args将是位置参数的元组,与传递参数的顺序一致。*args并且**kwargs必须是函数定义中定义的最后一个参数。但是,您应该小心*args:始终记住,在函数定义中,位置参数和关键字参数没有区别。因此,如果要扩展MultiValueField类,并且希望将字段作为位置参数传递,则必须将子类__init__方法中的每个参数都视为位置参数。所以:

class MyCustomMultiValueField(forms.MultiValueField):
    def __init__(customarg1, customarg2=None, *args, **kwargs):
        # do something
        super(MyCustomMultiValueField, self).__init__(*args, **kwargs)

# this won't work, as now customargs2 is (BooleanField(), DateField()), instead of fields
MyCustomMultiValueField(1, (BooleanField(), DateField()))

# you can still pass fields as a keyword argument
MyCustomMultiValueField(1, fields=(BooleanField(), DateField()))

如果您只想指定关键字参数,或者如果您想在 super 的位置参数之后指定位置参数,一种常见的方法是只指定*args**kwargs作为参数:

def __init__(self, *args, **kwargs):
    keywordarg1 = kwargs.pop('keyword1', None)
    mandatorykeywordarg2 = kwargs.pop('keyword2')
    if len(args) == 2:
        # because tuples are immutable
        args = list(args)
        positionalargumentafterfields = args.pop(1)
    super(MyCustomMultiValueField, self).__init__(*args, **kwargs)
    # *args also works with a list

一个常见的用例是当您想要指定额外的参数时,而其他一些代码依赖于基类的位置参数来创建您的子类。Django 模型和表单是您希望这样做以防止破坏代码的示例。

函数定义中的参数文档。

于 2013-08-09T16:41:42.800 回答