【问题标题】:How do you change the default widget for all Django date fields in a ModelForm?如何更改 ModelForm 中所有 Django 日期字段的默认小部件?
【发布时间】:2010-10-14 05:45:46
【问题描述】:

给定一组典型模型:

# 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()

...

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

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

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

感谢您的想法和意见。

【问题讨论】:

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


    【解决方案1】:

    您可以在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...
    

    【讨论】:

    • +1 如果这是您在使用 DateTimeFields 的多个表单中需要的常见行为,这是 DRY 方法。
    • 好东西。 formfield_callback 记录在哪里?
    • 谢谢,詹姆斯。这有很大帮助。我在我的博客 (strattonbrazil.blogspot.com/2011/03/…) 上提供了一个完整的实现示例。
    • 感谢 James 的 DRY 解决方案,感谢 voodoogiant 在您的博客上的全面实施。效果很好。
    • 快速警告以节省他人时间。如果您从定义 formfield_callback 的自定义基表单类继承,则不会调用 BaseForm.formfield_callback。这是因为 formfield_callback 作为 new 的一部分被调用(即在 ModelFormMetaClass 中)。我为这个问题创建了一个不错的解决方法,如果您有兴趣,可以在这里找到描述:stackoverflow.com/questions/7342925/…
    【解决方案2】:

    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

    【讨论】:

    • 很好的参考。可以以不需要为每个表单手动输入“self.fields ['question']”行的方式完成吗? (例如,对于 self.fields 中的字段:if isinstance(field,models.DateField): field.widget = mywidget?干杯
    【解决方案3】:

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

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

    还有一种更短的方法:

    class ArticleForm(ModelForm):
         pub_date = DateField(widget=MyDateWidget())
    
         class Meta:
             model = Article
    

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

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

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

    【讨论】:

    • 感谢您的回复。您建议的解决方案(保留标准 HTML)很有趣,但仍然是劳动密集型的,容易出现开发人员错误,并且需要大量重复代码。我的目标是找到一个解决这些问题的解决方案。有什么想法吗?
    • 好吧,我不需要你正在尝试的规模上的东西(对于项目中的大量输入),但是 django admin 使用 datepicker 小部件来完成它,你可以查看代码django.contrib.admin 看看它是怎么做的。
    【解决方案4】:

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

    models.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}}
    

    【讨论】:

      【解决方案5】:

      您确实想定义一个自定义小部件,并使用小部件的inner Media class 来定义页面中必须包含的 JS(和 CSS?)文件才能使小部件工作。如果你做对了,你可以让你的小部件完全独立和可重用。请参阅django-markitup 获取一个example of doing this(它有一个可重复使用的MarkItUp! universal markup editor 小部件)。

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

      【讨论】:

        【解决方案6】:

        有些人可能会对此皱眉,但为了用您的自定义小部件替换日期选择器,我将为您的项目创建一个 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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-03-25
          • 2011-04-10
          • 1970-01-01
          • 2010-10-14
          • 2014-10-01
          • 2014-07-06
          • 2023-04-06
          相关资源
          最近更新 更多