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


171

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

我浏览了Django表单文档,其中简要提到了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都是陌生的,所以请放轻松...



Answers:


159

随着时间的流逝,此答案的复杂性不断提高,并且需要进行许多破解,可能应该警告您不要这样做。它依赖于管理员未记录的内部实现细节,可能会在将来的Django版本中再次中断,并且比找到另一个JS日历小部件并使用它更容易实现。

就是说,如果您决心进行这项工作,这是您必须做的:

  1. 为模型定义自己的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()
  2. 更改您的URLconf,以将“ form_class”:ProductForm而不是“ model”:Product传递到通用的create_object视图(这当然意味着“从my_app.forms import ProductForm”而不是“从my_app.models import Product”)。

  3. 在模板的开头,包括{{form.media}},以输出指向Javascript文件的链接。

  4. hacky部分:admin日期/时间小部件假定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)。您必须自己执行此操作,而不是通过admin应用程序,因此无论您是否登录到admin都可以访问它(感谢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的添加。


3
在线上最有用的操作,但是最终显示了日期/时间窗口小部件,并且填充字段现在将被删除,因为尽管表单字段中的required = False,它现在仍显示消息“输入有效的日期/时间”。如果我将其保留为空白,则为我的非必填字段。回到我的jQuery。
PhoebeB

这是在Django 1.1.1上进行的方式吗?
Nacho

1
在Django 1.2 RC1和更高版本中,您需要对上述内容进行一些小的更改。请参阅:stackoverflow.com/questions/38601/...
mshafrir

1
您知道这是否仍然适用于Django 1.4吗?尽管我确信要使用jQuery的datepicker,但我很好奇Django团队是否可能使其小部件更加公开可用。(我怀疑不是,因为此QA似乎是最多的文档)
约翰·C

1
一直没有使admin窗口小部件在admin之外更易于重用的动向,而且我猜也不会有。这将需要大量工作,并且还有很多更高优先级的项目需要处理。(我是Django核心团队的成员,顺便说一句)。
卡尔·梅尔

64

由于该解决方案有点漏洞,因此我认为将自己的日期/时间窗口小部件与一些JavaScript结合使用更可行。


8
我同意。几个月前,我尝试在django项目中执行此操作。我最终放弃了,只是用jQueryUI添加了自己的。花了10分钟全部使它工作。
priestc

8
记录下来,我同意,我已经投票赞成这个答案,但我从未在生产现场的答案中实际使用过该技术;-)
卡尔·迈耶

12

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

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

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

这是我创建的i18n_javascript函数。

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

哦,很好。我将其添加到上面的答案中,以便人们在遇到麻烦之前更容易找到它。
卡尔·迈尔

12

我发现自己经常引用这篇文章,并且发现文档定义了一种略微不友好的方法来覆盖默认小部件。

无需重写ModelForm的__init__方法

但是,您仍然需要如Carl所述适当地连接JS和CSS。

表格

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

参考字段类型以查找默认表单字段。


5
我不同意这种方法的技巧不足。看起来确实更好,但是您将完全覆盖模型表单生成的表单字段,这也可能会删除模型字段中的选项,例如help_text。覆盖init只会更改窗口小部件,而保留其余的表单字段。
卡尔·梅耶

11

我的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 %}

3
棒极了。我在过去2周中一直在尝试此操作,这非常感谢我
numerah 2013年

Django 1.4及更高版本需要使用{{STATIC_URL}}或{%load staticfiles%}与{%static'admin / js / core.js'%} ...,因为整个MEDIA_URL命名空间已被弃用并删除。/ admin / jsi18n可以正确解析,如果您在server / urls.py中映射了^ admin /
jeberle 2013年

好东西,对于Django 1.4很有用
Ashish 2014年

10

从Django 1.2 RC1开始,如果您使用的是Django admin日期选择器widge技巧,则必须将以下内容添加到模板中,否则您将看到通过“ / missing-admin-media-prefix”引用的日历图标url /”。

{% 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 %}


6

对于Django> = 2.0

注意:对于日期时间字段使用管理小部件不是一个好主意,因为如果您使用引导程序或任何其他CSS框架,则管理样式表可能会与您的网站样式表发生冲突。如果您要在Bootstrap上构建站点,请使用我的bootstrap-datepicker小部件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>caption{caption-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,
        }

5

如果以上操作失败,以下内容也将作为最后的手段

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


1
它给我错误“ DateField”对象没有属性“ needs_multipart_form”
madeeha ameer 2013年

3

仅向您的小部件分配一个类,然后将该类绑定到JQuery datepicker怎么样?

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


1

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

表格

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
Sam A.

1

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

  1. 对于模板中的每个字段,我都会两次看到日历图标。
  2. 对于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)

表格

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']

0

在Django 10中,myproject / 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>

0

我的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 %}

0

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

在表单中Charfield为时间字段(在此示例中为开始结束)使用simple 。

表格

我们可以在这里使用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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.