如何更改ModelForm中所有Django日期字段的默认小部件?

时间:2009-03-19 03:06:56

标签: jquery django django-forms widget django-widget

给出一组典型模型:

# Application A
from django.db import models
class TypicalModelA(models.Model):
    the_date = models.DateField()

 # Application B
from django.db import models
class TypicalModelB(models.Model):
    another_date = models.DateField()

...

如何将所有 DateFields的默认小部件更改为自定义MyDateWidget?

我问,因为我希望我的应用程序有一个jQueryUI日期选择器来输入日期。

我已经考虑过使用我的自定义小部件扩展django.db.models.DateField的自定义字段。这是实施这种全面改变的最佳方式吗?这样的改变需要专门将一个特殊的MyDateField导入每个模型,这是一个劳动密集型,容易出现开发人员错误(即一些模型.DateField将通过),在我看来似乎是不必要的重复工作。另一方面,我不喜欢修改可以被认为是规范版本的模型.DateField。

赞赏思想和意见。

6 个答案:

答案 0 :(得分:56)

您可以在ModelForm类上声明一个名为formfield_callback的属性。这应该是一个将Django模型Field实例作为参数的函数,并返回一个表单Field实例以在表单中表示它。

然后你要做的就是查看传入的模型字段是否为DateField的实例,如果是,则返回自定义字段/窗口小部件。如果没有,模型字段将有一个名为formfield的方法,您可以调用该方法返回其默认表单字段。

所以,比如:

def make_custom_datefield(f):
    if isinstance(f, models.DateField):
        # return form field with your custom widget here...
    else:
        return f.formfield()

class SomeForm(forms.ModelForm)
    formfield_callback = make_custom_datefield

    class Meta:
        # normal modelform stuff here...

答案 1 :(得分:6)

This article曾多次帮助过我。

它的内容涉及覆盖ModelForm的__init__方法,然后调用超类'__init__方法,然后单独调整字段。

class PollForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(PollForm, self).__init__(*args, **kwargs)
        self.fields['question'].widget = forms.Textarea()

    class Meta:
        model = Poll

这种方法可能看起来比Vasil更复杂,但它提供了额外的好处,即能够精确覆盖字段上的任何属性,而无需通过重新声明来重置任何其他属性。

更新:建议的方法可以推广到更改所有日期字段而不严格键入每个名称:

from django.forms import fields as formfields
from django.contrib.admin import widgets

class PollForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(PollForm, self).__init__(*args, **kwargs)
        for field_name in self.fields:
            field = self.fields[field_name]
            if isinstance(field, formfields.DateField):
                field.widget = widgets.AdminDateWidget()

    class Meta:
        model = Poll

这对我python3django 1.11

有用

答案 2 :(得分:6)

好吧,制作一个自定义模型字段只是为了改变它的默认表单小部件并不是一个明显的起点。

您可以创建自己的表单窗口小部件并覆盖表单中的字段,在Soviut的答案中指定您自己的窗口小部件。

还有一个更短的方式:

class ArticleForm(ModelForm):
     pub_date = DateField(widget=MyDateWidget())

     class Meta:
         model = Article

有一个如何编写表单小部件的例子,它位于Django的表单包中。这是一个有3个下拉菜单的日期选择器。

当我只想将一些JavaScript添加到标准HTML输入元素时,我通常会做的就是保持原样并通过稍后使用JavaScript引用它来修改它。您可以轻松捕获Django生成的输入字段的ID的命名约定。

您还可以在窗体中覆盖窗口小部件时为其提供类。然后用类名jQuery抓住它们。

答案 3 :(得分:3)

答案 4 :(得分:1)

您确实要定义自定义窗口小部件,并使用窗口小部件inner Media class来定义必须包含在窗口中以使窗口小部件工作的JS(和CSS?)文件。如果您这样做,您可以使您的小部件完全独立并可重复使用。请参阅django-markitup了解一个example of doing this(它有MarkItUp! universal markup editor的可重复使用的小部件。)

然后使用formfield_callback(请参阅James Bennett的回答)轻松地将该窗口小部件应用于表单中的所有DateField。

答案 5 :(得分:1)

有些人可能会对此感到不满,但是为了用你的自定义小部件替换日期选择器,我会为你的项目创建一个monkeypatch应用程序,并在运行时修补Django本身。这样做的好处是任何第三方应用程序也会受到影响,因此无需修改第三方代码即可为最终用户提供统一的界面:

from django.forms.widgets import DateInput , DateTimeInput, TimeInput
from FOO.widgets import MyjQueryWidget

# be nice and tell you are patching
logger.info("Patching 'DateInput.widget = MyjQueryWidget': Replaces django DateInput to use my new super  'MyjQueryWidget'")

# be nicer and confirm signature of code we are patching and warn if it has changed - uncomment below to get the current hash fingerprint
# raise Exception(hashlib.md5(inspect.getsource(DateInput.widget)).hexdigest()) # uncommet to find latest hash
if not '<enter hexdigest fingerprint here>' == \
        hashlib.md5(inspect.getsource(DateInput.widget)).hexdigest():
    logger.warn("md5 signature of 'DateInput.widget' does not match Django 1.5. There is a slight chance patch "
                    "might be broken so please compare and update this monkeypatch.")

# be nicest and also update __doc__
DateInput.__doc__ = "*Monkeypatched by <app name>*: Replaced django DateInput.widget with my new super  'MyjQueryWidget'" + DateInput.__doc__ 

DateInput.widget = MyjQueryWidget

以上内容的灵感来自于我在项目中使用的html5monkeypatch,请查看patch_widgets.pypatch_fields.py