以自定义形式使用 Django 时间/日期小部件

Posted

技术标签:

【中文标题】以自定义形式使用 Django 时间/日期小部件【英文标题】:Using Django time/date widgets in custom form 【发布时间】:2010-09-07 12:07:53 【问题描述】:

如何使用默认管理员在我的自定义视图中使用的漂亮的 javascript 日期和时间小部件?

我看过the Django forms documentation,里面简单提到了django.contrib.admin.widgets,但是不知道怎么用?

这是我想要应用的模板。

<form action="." method="POST">
    <table>
        % for f in form %
           <tr> <td>  f.name </td> <td> f </td> </tr>
        % endfor %
    </table>
    <input type="submit" name="submit" value="Add Product">
</form>

另外,我认为应该注意的是,我自己并没有真正为这个表单编写视图,我使用的是通用视图。这是来自 url.py 的条目:

(r'^admin/products/add/$', create_object, 'model': Product, 'post_save_redirect': ''),

而且我对整个 Django/MVC/MTV 的东西都比较陌生,所以请放轻松...

【问题讨论】:

jqueryui.com/demos/datepicker trentrichardson.com/examples/timepicker 对于日期小部件,有一些简单的替代方案。我发布了一些例子here 另一种方法是使用 html5 日期/时间选择器,在没有任何 Javascript 的现代浏览器中工作 - ***.com/a/70800722/182604 【参考方案1】:

随着时间的推移,这个答案越来越复杂,并且需要许多技巧,可能应该警告你不要这样做。它依赖于管理员未记录的内部实现细节,很可能在未来版本的 Django 中再次出现问题,而且实现起来并不比找到另一个 JS 日历小部件并使用它更容易。

也就是说,如果你决心完成这项工作,你必须这样做:

    为你的模型定义你自己的ModelForm子类(最好把它放在你的应用程序的forms.py中),并告诉它使用AdminDateWidget/AdminTimeWidget/AdminSplitDateTime(替换'mydate'等使用模型中的正确字段名称):

     from django import forms
     from my_app.models import Product
     from django.contrib.admin import widgets                                       
    
     class ProductForm(forms.ModelForm):
         class Meta:
             model = Product
         def __init__(self, *args, **kwargs):
             super(ProductForm, self).__init__(*args, **kwargs)
             self.fields['mydate'].widget = widgets.AdminDateWidget()
             self.fields['mytime'].widget = widgets.AdminTimeWidget()
             self.fields['mydatetime'].widget = widgets.AdminSplitDateTime()
    

    更改您的 URLconf 以将 'form_class': ProductForm 而不是 'model': Product 传递给通用 create_object 视图(当然,这意味着 from my_app.forms import ProductForm 而不是 from my_app.models import Product)。

    在模板的头部,包含 form.media 以输出指向 Javascript 文件的链接。

    还有最棘手的部分:管理日期/时间小部件假定 i18n JS 内容已加载,并且还需要 core.js,但不会自动提供其中任何一个。因此,在 form.media 上面的模板中,您需要:

     <script type="text/javascript" src="/my_admin/jsi18n/"></script>
     <script type="text/javascript" src="/media/admin/js/core.js"></script>
    

您可能还希望使用以下管理 CSS(感谢 Alex 提及这一点):

    <link rel="stylesheet" type="text/css" href="/media/admin/css/forms.css"/>
    <link rel="stylesheet" type="text/css" href="/media/admin/css/base.css"/>
    <link rel="stylesheet" type="text/css" href="/media/admin/css/global.css"/>
    <link rel="stylesheet" type="text/css" href="/media/admin/css/widgets.css"/>

这意味着 Django 的管理媒体 (ADMIN_MEDIA_PREFIX) 位于 /media/admin/ - 您可以根据自己的设置进行更改。理想情况下,您应该使用上下文处理器将此值传递给您的模板,而不是对其进行硬编码,但这超出了本问题的范围。

这还需要手动将 URL /my_admin/jsi18n/ 连接到 django.views.i18n.javascript_catalog 视图(如果您不使用 I18N,则为 null_javascript_catalog)。您必须自己执行此操作,而不是通过管理应用程序,因此无论您是否登录到管理员都可以访问它(感谢Jeremy 指出这一点)。您的 URLconf 的示例代码:

(r'^my_admin/jsi18n', 'django.views.i18n.javascript_catalog'),

最后,如果您使用的是 Django 1.2 或更高版本,您需要在模板中添加一些额外的代码来帮助小部件找到它们的媒体:

% load adminmedia % /* At the top of the template. */

/* In the head section of the template. */
<script type="text/javascript">
window.__admin_media_prefix__ = "% filter escapejs %% admin_media_prefix %% endfilter %";
</script>

感谢lupefiasco 的添加。

【讨论】:

最有帮助的在线帖子,但最终出现日期/时间小部件并填充字段,我现在要把它拿出来,因为尽管在表单字段中有 required=False 它现在显示消息“请输入有效的日期/时间。”如果我将其留空,则为我不需要的字段。为我返回 jquery。 这是 Django 1.1.1 的方式吗? 在 Django 1.2 RC1 及更高版本中,您需要对上述内容进行一些小改动。见:***.com/questions/38601/… 你知道这是否仍然适用于 Django 1.4?尽管我确信使用 jQuery 的 datepicker,但我很好奇 Django 团队是否可能让他们的小部件更公开可用。 (我怀疑不是,因为这个 QA 似乎是周围最多的文档) 目前还没有使管理小部件更容易在管理员之外重用的进展,我不认为会有任何进展。这将是很多工作,并且有更高优先级的项目需要处理。 (顺便说一句,我是 Django 核心团队的一员)。【参考方案2】:

由于该解决方案很老套,我认为将您自己的日期/时间小部件与一些 JavaScript 结合使用更为可行。

【讨论】:

我同意。几个月前我尝试用我的 django 项目来做这件事。我最终放弃了,只是用 jQueryUI 添加了我自己的。花了整整 10 分钟才让它工作。 作为记录,我同意,我赞成这个答案,而且我从来没有在生产站点的答案中实际使用过该技术;-)【参考方案3】:

我发现自己经常引用这篇文章,并发现documentation 定义了一种稍微 不太老套的方法来覆盖默认小部件。

(无需重写 ModelForm 的 __init__ 方法)

但是,正如 Carl 所提到的,您仍然需要正确连接 JS 和 CSS。

forms.py

from django import forms
from my_app.models import Product
from django.contrib.admin import widgets                                       


class ProductForm(forms.ModelForm):
    mydate = forms.DateField(widget=widgets.AdminDateWidget)
    mytime = forms.TimeField(widget=widgets.AdminTimeWidget)
    mydatetime = forms.SplitDateTimeField(widget=widgets.AdminSplitDateTime)

    class Meta:
        model = Product

参考Field Types 查找默认表单字段。

【讨论】:

我不同意这种方法不那么骇人听闻。当然,它看起来更好,但是您完全覆盖了模型表单生成的表单字段,这也可能会删除模型字段中的选项,例如 help_text。覆盖 init 只会更改小部件,并保留表单字段的其余部分。【参考方案4】:

我的 1.4 版本的头代码(一些新的和一些删除的)

% block extrahead %

<link rel="stylesheet" type="text/css" href=" STATIC_URL admin/css/forms.css"/>
<link rel="stylesheet" type="text/css" href=" STATIC_URL admin/css/base.css"/>
<link rel="stylesheet" type="text/css" href=" STATIC_URL admin/css/global.css"/>
<link rel="stylesheet" type="text/css" href=" STATIC_URL admin/css/widgets.css"/>

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/core.js"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/jquery.js"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/jquery.init.js"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/actions.js"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/calendar.js"></script>
<script type="text/javascript" src=" STATIC_URL admin/js/admin/DateTimeShortcuts.js"></script>

% endblock %

【讨论】:

太棒了。我从过去两周开始尝试这个,这对我很有帮助,非常感谢 使用 STATIC_URL 或 % load staticfiles % 和 % static 'admin/js/core.js' %... 是 Django 1.4+ 所必需的,因为整个 MEDIA_URL命名空间已被弃用并删除。如果您在 server/urls.py 中映射了 ^admin/,则 /admin/jsi18n 会正确解析 好东西,在 Django 1.4 中很有用【参考方案5】:

是的,我最终覆盖了 /admin/jsi18n/ 网址。

这是我在 urls.py 中添加的内容。确保它在 /admin/ url 上方

    (r'^admin/jsi18n', i18n_javascript),

这是我创建的 i18n_javascript 函数。

from django.contrib import admin
def i18n_javascript(request):
  return admin.site.i18n_javascript(request)

【讨论】:

哦,很好。我会将此添加到我上面的答案中,以便人们在遇到麻烦之前更容易找到。【参考方案6】:

从 Django 1.2 RC1 开始,如果您使用 Django 管理员日期选择器小部件技巧,则必须将以下内容添加到您的模板中,否则您将看到日历图标 url 通过“/missing-admin-”引用媒体前缀/"。

% load adminmedia % /* At the top of the template. */

/* In the head section of the template. */
<script type="text/javascript">
window.__admin_media_prefix__ = "% filter escapejs %% admin_media_prefix %% endfilter %";
</script>

【讨论】:

【参考方案7】:

补充 Carl Meyer 的回答,我想评论一下,您需要将该标题放在模板内的某个有效块(标题内)中。

% block extra_head %

<link rel="stylesheet" type="text/css" href="/media/admin/css/forms.css"/>
<link rel="stylesheet" type="text/css" href="/media/admin/css/base.css"/>
<link rel="stylesheet" type="text/css" href="/media/admin/css/global.css"/>
<link rel="stylesheet" type="text/css" href="/media/admin/css/widgets.css"/>

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/media/admin/js/core.js"></script>
<script type="text/javascript" src="/media/admin/js/admin/RelatedObjectLookups.js"></script>

 form.media 

% endblock %

【讨论】:

我还发现这个链接很有用:groups.google.ca/group/django-users/msg/1a40cf5f153cd23e【参考方案8】:

对于 Django >= 2.0

注意:对日期时间字段使用管理小部件不是一个好主意,因为如果您使用引导程序或任何其他 CSS 框架,管理样式表可能会与您的站点样式表发生冲突。如果您在引导程序上构建您的网站,请使用我的引导程序日期选择器小部件django-bootstrap-datepicker-plus。

第 1 步:javascript-catalog URL 添加到您的项目(不是应用程序)的urls.py 文件中。

from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    path('jsi18n', JavaScriptCatalog.as_view(), name='javascript-catalog'),
]

第 2 步:将所需的 JavaScript/CSS 资源添加到您的模板中。

<script type="text/javascript" src="% url 'javascript-catalog' %"></script>
<script type="text/javascript" src="% static '/admin/js/core.js' %"></script>
<link rel="stylesheet" type="text/css" href="% static '/admin/css/widgets.css' %">
<style>.calendar>table>captioncaption-side:unset</style><!--caption fix for bootstrap4-->
 form.media         # Form required JS and CSS #

第 3 步:在您的 forms.py 中为日期时间输入字段使用管理小部件。

from django.contrib.admin import widgets
from .models import Product

class ProductCreateForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'publish_date', 'publish_time', 'publish_datetime']
        widgets = 
            'publish_date': widgets.AdminDateWidget,
            'publish_time': widgets.AdminTimeWidget,
            'publish_datetime': widgets.AdminSplitDateTime,
        

【讨论】:

如另一个答案中所述,另一个答案(***.com/a/1392329)我相信使用AdminSplitDateTime需要使用SplitDateTimeField---否则提交时会遇到问题(***.com/q/58932440)【参考方案9】:

如果上述方法失败,以下方法也将作为最后的手段

class PaymentsForm(forms.ModelForm):
    class Meta:
        model = Payments

    def __init__(self, *args, **kwargs):
        super(PaymentsForm, self).__init__(*args, **kwargs)
        self.fields['date'].widget = SelectDateWidget()

class PaymentsForm(forms.ModelForm):
    date = forms.DateField(widget=SelectDateWidget())

    class Meta:
        model = Payments

把它放在你的 forms.py from django.forms.extras.widgets import SelectDateWidget

【讨论】:

它给了我错误'DateField'对象没有属性'needs_multipart_form'【参考方案10】:

只为您的小部件分配一个类,然后将该类绑定到 JQuery 日期选择器呢?

Django forms.py:

class MyForm(forms.ModelForm):

  class Meta:
    model = MyModel

  def __init__(self, *args, **kwargs):
    super(MyForm, self).__init__(*args, **kwargs)
    self.fields['my_date_field'].widget.attrs['class'] = 'datepicker'

还有一些用于模板的 JavaScript:

  $(".datepicker").datepicker();

【讨论】:

***.com/questions/660929/…【参考方案11】:

这是另一个 2020 年的解决方案,其灵感来自 @Sandeep's。使用在这个gist 中找到的 MinimalSplitDateTimeMultiWidget,在我们的如下表单中,我们可以使用现代浏览器的日期和时间选择器(例如通过'type':'date')。我们不需要任何 JS。

class EditAssessmentBaseForm(forms.ModelForm):
    class Meta:
        model = Assessment
        fields = '__all__'

    begin = DateTimeField(widget=MinimalSplitDateTimeMultiWidget())

【讨论】:

AttributeError: 'NoneType' object has no attribute 'copy' 你有工作应用示例吗? 我使用以下代码对其进行了初始化: date_created = forms.DateTimeField(widget=MinimalSplitDateTimeMultiWidget(attrs='type': 'date')) 但它不起作用 你需要提供更多信息@ARKhan @ABHAYKOTAL @ARKhan 将代码替换为 `date_attrs = attrs 或 time_attrs = attrs 或 ` 我更新了要点以反映@DanieleStoDomingo 的修复【参考方案12】:

2021 年 Django 3 (3.2) 的另一个简单解决方案 ;) 导致 andyw 的解决方案在 web Firefox 中不起作用...

% load static %

% block extrahead %
 block.super 
<script type="text/javascript" src="% static 'admin/js/cancel.js' %"></script>
<link rel="stylesheet" type="text/css" href="% static 'admin/css/forms.css' %">
<script src="% url 'admin:jsi18n' %"></script>
<script src="% static 'admin/js/jquery.init.js' %"></script>
<script src="% static 'admin/js/core.js' %"></script>
 form.media 
% endblock %

<form action="" method="post">
% csrf_token %
 form.as_p 
<input type="submit" value="Сохранить">
</form>

然后您就可以使用您的表单了。 示例:

from django.contrib.admin import widgets
date_time = forms.SplitDateTimeField(widget=widgets.AdminSplitDateTime)

【讨论】:

【参考方案13】:

使用 required=False 更新了 SplitDateTime 的解决方案和解决方法:

forms.py

from django import forms

class SplitDateTimeJSField(forms.SplitDateTimeField):
    def __init__(self, *args, **kwargs):
        super(SplitDateTimeJSField, self).__init__(*args, **kwargs)
        self.widget.widgets[0].attrs = 'class': 'vDateField'
        self.widget.widgets[1].attrs = 'class': 'vTimeField'  


class AnyFormOrModelForm(forms.Form):
    date = forms.DateField(widget=forms.TextInput(attrs='class':'vDateField'))
    time = forms.TimeField(widget=forms.TextInput(attrs='class':'vTimeField'))
    timestamp = SplitDateTimeJSField(required=False,)

form.html

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="/admin_media/js/core.js"></script>
<script type="text/javascript" src="/admin_media/js/calendar.js"></script>
<script type="text/javascript" src="/admin_media/js/admin/DateTimeShortcuts.js"></script>

urls.py

(r'^admin/jsi18n/', 'django.views.i18n.javascript_catalog'),

【讨论】:

我还填了一张票来修复 SplitDateTime 小部件code.djangoproject.com/ticket/12303【参考方案14】:

我用这个,很好,但是模板有两个问题:

    我看到模板中每个文件的日历图标 两次

    对于 TimeField,我有“输入有效日期。

models.py

from django.db import models
    name=models.CharField(max_length=100)
    create_date=models.DateField(blank=True)
    start_time=models.TimeField(blank=False)
    end_time=models.TimeField(blank=False)

forms.py

from django import forms
from .models import Guide
from django.contrib.admin import widgets

class GuideForm(forms.ModelForm):
    start_time = forms.DateField(widget=widgets.AdminTimeWidget)
    end_time = forms.DateField(widget=widgets.AdminTimeWidget)
    create_date = forms.DateField(widget=widgets.AdminDateWidget)
    class Meta:
        model=Guide
        fields=['name','categorie','thumb']

【讨论】:

【参考方案15】:

在 Django 10 中。 我的项目/urls.py: 在 urlpatterns 的开头

  from django.views.i18n import JavaScriptCatalog

urlpatterns = [
    url(r'^jsi18n/$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
.
.
.]

在我的 template.html 中:

% load staticfiles %

    <script src="% static "js/jquery-2.2.3.min.js" %"></script>
    <script src="% static "js/bootstrap.min.js" %"></script>
    # Loading internazionalization for js #
    % load i18n admin_modify %
    <script type="text/javascript" src="% url 'javascript-catalog' %"></script>
    <script type="text/javascript" src="% static "/admin/js/jquery.init.js" %"></script>

    <link rel="stylesheet" type="text/css" href="% static "/admin/css/base.css" %">
    <link rel="stylesheet" type="text/css" href="% static "/admin/css/forms.css" %">
    <link rel="stylesheet" type="text/css" href="% static "/admin/css/login.css" %">
    <link rel="stylesheet" type="text/css" href="% static "/admin/css/widgets.css" %">



    <script type="text/javascript" src="% static "/admin/js/core.js" %"></script>
    <script type="text/javascript" src="% static "/admin/js/SelectFilter2.js" %"></script>
    <script type="text/javascript" src="% static "/admin/js/admin/RelatedObjectLookups.js" %"></script>
    <script type="text/javascript" src="% static "/admin/js/actions.js" %"></script>
    <script type="text/javascript" src="% static "/admin/js/calendar.js" %"></script>
    <script type="text/javascript" src="% static "/admin/js/admin/DateTimeShortcuts.js" %"></script>

【讨论】:

【参考方案16】:

我的 Django 设置:1.11 引导程序:3.3.7

由于没有一个答案是完全清楚的,我将分享我的模板代码,它完全没有错误。

模板的上半部分:

% extends 'base.html' %
% load static %
% load i18n %

% block head %
    <title>Add Interview</title>
% endblock %

% block content %

<script type="text/javascript" src="% url 'javascript-catalog' %"></script>
<script type="text/javascript" src="% static 'admin/js/core.js' %"></script>
<link rel="stylesheet" type="text/css" href="% static 'admin/css/forms.css' %"/>
<link rel="stylesheet" type="text/css" href="% static 'admin/css/widgets.css' %"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
<script type="text/javascript" src="% static 'js/jquery.js' %"></script>

下半场:

<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="% static 'admin/js/vendor/jquery/jquery.min.js' %"></script>
<script type="text/javascript" src="% static 'admin/js/jquery.init.js' %"></script>
<script type="text/javascript" src="% static 'admin/js/actions.min.js' %"></script>
% endblock %

【讨论】:

【参考方案17】:

2020 年 6 月 3 日(所有答案均无效,您可以尝试我使用的此解决方案。仅适用于 TimeField)

将简单的Charfield 用于表单中的时间字段(本例中为startend)。

forms.py

我们可以在这里使用FormModelForm

class TimeSlotForm(forms.ModelForm):
    start = forms.CharField(widget=forms.TextInput(attrs='placeholder': 'HH:MM'))
    end = forms.CharField(widget=forms.TextInput(attrs='placeholder': 'HH:MM'))

    class Meta:
        model = TimeSlots
        fields = ('start', 'end', 'provider')

将字符串输入转换为视图中的时间对象。

import datetime
def slots():
    if request.method == 'POST':
        form = create_form(request.POST)
        if form.is_valid():                
            slot = form.save(commit=False)
            start = form.cleaned_data['start']
            end = form.cleaned_data['end']
            start = datetime.datetime.strptime(start, '%H:%M').time()
            end = datetime.datetime.strptime(end, '%H:%M').time()
            slot.start = start
            slot.end = end
            slot.save()

【讨论】:

以上是关于以自定义形式使用 Django 时间/日期小部件的主要内容,如果未能解决你的问题,请参考以下文章

如何将日期/日历小部件添加到 django 表单?

Django 小部件和日期字段

在 Django admin 中更改日期字段的默认小部件

Django Admin:仅对一个模型字段使用自定义小部件

使用 RadioSelect 小部件自定义 Django 表单

如何在我的自定义小部件模板中包含内置的 django 小部件模板?