以自定义形式使用 Django time/date 小部件

如何使用默认管理员在自定义视图中使用的漂亮的 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 还是个新手,所以请放轻松..。

133450 次浏览

随着时间的推移,这个答案越来越复杂,需要很多技巧,这可能应该提醒您根本不要这样做。它依赖于管理员未记录的内部实现细节,在 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视图(当然,这意味着将 from my_app.forms import ProductForm而不是 from my_app.models import Product)。

  3. 在模板的头部,包含 \{\{ form.media }}以输出到 Javascript 文件的链接。

  4. 最奇怪的部分是: admin date/time 小部件假设 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 (感谢 亚历克斯提到这一点) :

    <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)。您必须自己完成这项工作,而不是通过管理应用程序,这样无论您是否登录到管理程序都可以访问它(感谢 杰里米指出这一点)。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>

感谢 卢佩菲斯科增加了这个。

由于这个解决方案很骇人听闻,我认为使用您自己的日期/时间小部件和一些 JavaScript 更可行。

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

下面是我在 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)

作为对 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 %}

我发现自己经常引用这篇文章,并且发现 文件定义了一种不那么蹩脚的方法来覆盖默认的小部件。

(不需要重写 ModelForm 的 _ _ init _ _ 方法)

但是,您仍然需要像 Carl 提到的那样适当地连接您的 JS 和 CSS。

表格 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

引用 字段类型查找默认的表单字段。

更新了 分割日期时间需要 = 错误的解决方案和解决方案:

表格 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,)

格式

<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'),

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

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

把这个放在你的表格里

从 Django 1.2 RC1开始,如果您使用 Django admin date picker widge 技巧,那么必须将以下内容添加到您的模板中,否则您将看到日历图标 URL 通过“/miss-admin-media-prefix/”引用。

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

为小部件分配一个类,然后将该类绑定到 JQuery 数据采集器怎么样?

姜戈表格:

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

在姜戈10。 Myproject/urls.py: 在 urlpattern 的开头

  from django.views.i18n import JavaScriptCatalog


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

在我的模板中:

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

我使用这个,它是伟大的,但我有2个问题与模板:

  1. 我看到日历图标 两次的每个文件在模板。
  2. 对于 TimeField,我有“ 输入有效日期。

    Here is a screenshot of the Form

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)

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

对于 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>caption{caption-side:unset}</style><!--caption fix for bootstrap4-->
\{\{ form.media }}        {# Form required JS and CSS #}

步骤3: forms.py中使用 admin widgets 作为日期-时间输入字段。

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,
}

我的姜戈设置: 1.11 Bootstrap: 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 %}

2020年6月3日(所有的答案都不起作用,你可以试试我使用的这个解决方案

对于表单中的时间字段(本例中为 开始结束) ,使用简单的 Charfield

表格.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()

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

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


begin = DateTimeField(widget=MinimalSplitDateTimeMultiWidget())

enter image description here

另一个简单的 2021年姜戈3(3.2)解决方案;)因为 andyw 的解决方案不能在 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)