首页 / 知识

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

2023-04-13 21:02:00

Using Django time/date widgets in custom form

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

我浏览了Django表单文档,其中简要提到了django.contrib.admin.widgets,但我不知道如何使用它?

这是我希望将其应用于的模板。

1
2
3
4
5
6
7
8
<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中的条目:

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

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


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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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}}上方的模板中,您需要:

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

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

    1
    2
    3
    4
    <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应用程序,因此无论您是否登录管理员都可以访问它(感谢Jeremy指出了这一点)。 URLconf的示例代码:

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

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

    1
    2
    3
    4
    5
    {% 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 %}";

    感谢lupefiasco的添加。


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


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

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

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

    这是我创建的i18n_javascript函数。

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

    我的1.4版头代码(有些是新增的,有些是删除的)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    {% 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 type="text/javascript" src="{{ STATIC_URL }}admin/js/core.js">
    <script type="text/javascript" src="{{ STATIC_URL }}admin/js/admin/RelatedObjectLookups.js">
    <script type="text/javascript" src="{{ STATIC_URL }}admin/js/jquery.js">
    <script type="text/javascript" src="{{ STATIC_URL }}admin/js/jquery.init.js">
    <script type="text/javascript" src="{{ STATIC_URL }}admin/js/actions.js">
    <script type="text/javascript" src="{{ STATIC_URL }}admin/js/calendar.js">
    <script type="text/javascript" src="{{ STATIC_URL }}admin/js/admin/DateTimeShortcuts.js">

    {% endblock %}

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

    (无需重写ModelForm的__init__方法)

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

    forms.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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 admin日期选择器widge技巧,则必须将以下内容添加到模板中,否则您将看到通过" / missing-admin-media-prefix"引用的日历图标url /"。

    1
    2
    3
    4
    5
    {% 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 %}";


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {% 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 type="text/javascript" src="/media/admin/js/core.js">
    <script type="text/javascript" src="/media/admin/js/admin/RelatedObjectLookups.js">

    {{ form.media }}

    {% endblock %}

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

    1
    2
    3
    4
    5
    6
    7
    class PaymentsForm(forms.ModelForm):
        class Meta:
            model = Payments

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

    如同

    1
    2
    3
    4
    5
    class PaymentsForm(forms.ModelForm):
        date = forms.DateField(widget=SelectDateWidget())

        class Meta:
            model = Payments

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


    对于Django> = 2.0

    Note: Using admin widgets for date-time fields is not a good idea as admin style-sheets can conflict with your site style-sheets in case you are using bootstrap or any other CSS frameworks. If you are building your site on bootstrap use my bootstrap-datepicker widget django-bootstrap-datepicker-plus.

    步骤1:将javascript-catalog URL添加到项目(而非应用程序)的urls.py文件中。

    1
    2
    3
    4
    5
    from django.views.i18n import JavaScriptCatalog

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

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

    1
    2
    3
    4
    5
    <script type="text/javascript" src="{% url 'javascript-catalog' %}">
    <script type="text/javascript" src="{% static '/admin/js/core.js' %}">
    <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中,将管理小部件用于日期时间输入字段。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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,
            }

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

    Django Forms.py:

    1
    2
    3
    4
    5
    6
    7
    8
    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:

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

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

  • 对于模板中的每个字段,我都会两次看到日历图标。
  • 对于TimeField,我有"输入有效日期"。

    Here is a screenshot of the Form

  • models.py

    1
    2
    3
    4
    5
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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']


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

    forms.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

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

    urls.py

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

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

    由于没有一个答案是完全清楚的,因此我共享我的模板代码,该代码完全没有错误。

    模板的上半部分:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    {% extends 'base.html' %}
    {% load static %}
    {% load i18n %}

    {% block head %}
        Add Interview
    {% endblock %}

    {% block content %}

    <script type="text/javascript" src="{% url 'javascript-catalog' %}">
    <script type="text/javascript" src="{% static 'admin/js/core.js' %}">
    <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' %}">

    下半区:

    1
    2
    3
    4
    5
    <script type="text/javascript" src="/admin/jsi18n/">
    <script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}">
    <script type="text/javascript" src="{% static 'admin/js/jquery.init.js' %}">
    <script type="text/javascript" src="{% static 'admin/js/actions.min.js' %}">
    {% endblock %}

    在Django 10中。
    MyProject的/ urls.py:
    在urlpatterns的开头

    1
    2
    3
    4
    5
    6
    7
      from django.views.i18n import JavaScriptCatalog

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

    在我的template.html中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {% load staticfiles %}

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

        <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 type="text/javascript" src="{% static"/admin/js/SelectFilter2.js" %}">
        <script type="text/javascript" src="{% static"/admin/js/admin/RelatedObjectLookups.js" %}">
        <script type="text/javascript" src="{% static"/admin/js/actions.js" %}">
        <script type="text/javascript" src="{% static"/admin/js/calendar.js" %}">
        <script type="text/javascript" src="{% static"/admin/js/admin/DateTimeShortcuts.js" %}">


    日期自定义管理员默认

    最新内容

    相关内容

    热门文章

    推荐文章

    标签云

    猜你喜欢