题
如何在我的自定义视图中使用默认管理员使用的漂亮 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 还很陌生,所以请放轻松......
解决方案
随着时间的推移,这个答案变得越来越复杂,并且需要很多技巧,可能应该警告您不要这样做。它依赖于管理员未记录的内部实现细节,很可能在 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 而不是“模型”: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(谢谢 亚历克斯 提到这一点):
<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>
谢谢 卢佩菲亚斯科 对于这个补充。
其他提示
由于解决方案很hackish,我认为使用您自己的日期/时间小部件和一些 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)
我的 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 %}
我发现自己经常引用这篇文章,并发现 文档 定义了一个 轻微地 覆盖默认小部件的更简单的方法。
(无需重写 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
参考 字段类型 找到默认的表单字段。
从 Django 1.2 RC1 开始,如果您使用 Django 管理日期选择器小技巧,则必须将以下内容添加到您的模板中,否则您将看到通过“/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>
补充 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 %}
如果上述方法失败,以下方法也可以作为最后的手段
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
只为您的小部件分配一个类,然后将该类绑定到 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();
对于 Django >= 2.0
笔记:对日期时间字段使用管理小部件并不是一个好主意,因为如果您使用 bootstrap 或任何其他 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,
}
更新了解决方案和解决方法 分割日期时间 和 必需=假:
表格.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,)
表单.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'),
我用这个,它很棒,但是我的模板有两个问题:
- 我看到日历图标 两次 对于模板中的每个归档。
对于 TimeField 我有 '输入一个有效日期。'
模型.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 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>
我的 Django 设置: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 %}