42

给定一组典型模型:

# 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 将通过),并且在我看来似乎是不必要的重复工作。另一方面,我不喜欢修改被认为是models.DateField 的规范版本的东西。

想法和意见表示赞赏。

4

6 回答 6

59

您可以在您的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(**kwargs)

class SomeForm(forms.ModelForm)
    formfield_callback = make_custom_datefield

    class Meta:
        # normal modelform stuff here...
于 2009-03-19T05:41:25.710 回答
8

这篇文章帮助了我无数次。

它的核心涉及覆盖 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

这对我python3有用django 1.11

于 2009-03-19T03:12:43.790 回答
6

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

您可以制作自己的表单小部件并覆盖表单中的字段,指定您自己的小部件,就像在 Soviut 的答案中一样。

还有一种更短的方法:

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

     class Meta:
         model = Article

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

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

当您在表单中覆盖它时,您也可以只提供小部件的类。然后通过类名用jQuery捕获它们。

于 2009-03-19T03:21:02.320 回答
3

我使用 JQuery。您只需查找要与日期选择器关联的字段的“id”,并将它们与 JQuery 和正确的显示格式绑定:

模型.py

class ObjectForm(ModelForm):
    class Meta:
        model = Object        
        fields = ['FieldName1','FieldName2']

在您使用视图呈现的页面顶部:

<head>
    <link type="text/css" href="/media/css/ui-darkness/jquery-ui-1.8.2.custom.css" rel="Stylesheet" /> 
    <script type="text/javascript" src="/media/js/jquery-1.4.2.min.js"></script>
    <script type="text/javascript" src="/media/js/jquery-ui-1.8.2.custom.min.js"></script>
</head>
<script type="text/javascript">
 $(function() {
        $("#id_FieldName1").datepicker({ dateFormat: 'yy-mm-dd' });
        $("#id_FieldName2").datepicker({ dateFormat: 'yy-mm-dd' });
 });
</script>
...
{{form}}
于 2010-07-23T20:52:33.423 回答
1

您确实想要定义一个自定义小部件,并使用小部件的内部 Media 类来定义必须包含在页面中以使小部件工作的 JS(和 CSS?)文件。如果你做对了,你可以让你的小部件完全独立和可重用。有关执行此操作的一个示例,请参见django-markitup(它具有MarkItUp! 通用标记编辑器的可重用小部件)。

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

于 2009-03-19T09:31:31.930 回答
1

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

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

于 2013-06-04T16:35:33.070 回答