以自定义形式使用Django时间/日期小部件
如何在我的自定义视图中使用默认管理员使用的漂亮的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而不是" model":Product传递给通用的create_object视图(这当然意味着"从my_app.forms导入ProductForm"而不是"从my_app.models导入Product")。
- 在模板的开头,包括{{form.media}},以输出指向Javascript文件的链接。
- 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的添加。
回答
由于该解决方案有点漏洞,因此我认为将自己的日期/时间窗口小部件与一些JavaScript结合使用会更可行。
回答
是的,我最终覆盖了/ 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)
回答
为了补充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。
表格
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
参考字段类型以查找默认表单字段。
回答
使用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'),
回答
如果以上操作失败,以下内容也将作为最后的解决方法
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.forms.extras.widgets import SelectDateWidget将其放入forms.py中。
回答
从Django 1.2 RC1开始,如果我们使用的是Django admin日期选择器widge技巧,则必须将以下内容添加到模板中,否则我们将看到日历图标的url通过" / missing-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>