如何在 django 的 ModelForm 中使用 DatePicker?

How to use a DatePicker in a ModelForm in django?

我正在使用 django 3.0,我正试图在我的 ModelForm 中显示一个日期选择器小部件,但我不知道如何(我只能得到文本字段)。我曾尝试寻找一些解决方案,但找不到任何解决方案。这就是我的模型和 ModelForm 的样子:

class Membership(models.Model):
  start_date = models.DateField(default=datetime.today, null=True)
  owner = models.ForeignKey(Client, on_delete=models.CASCADE, null=True)
  type = models.ForeignKey(MembershipType, on_delete=models.CASCADE, null=True)

class MembershipForm(ModelForm):
  class Meta:
    model = Membership
    fields = ['owner', 'start_date', 'type']
    widgets = {
        'start_date': forms.DateInput
    }

这是我的 html:

<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

这是预期的行为。 DateInput widget [Django-doc] 只是一个带有可选 format 参数的 <input type="text"> 元素。

你可以使用一个包,例如 django-bootstrap-datepicker-plus [pypi] , 然后用 DatePickerInput:

定义一个表单
from bootstrap_datepicker_plus import <b>DatePickerInput</b>

class MembershipForm(ModelForm):
  class Meta:
    model = Membership
    fields = ['owner', 'start_date', 'type']
    widgets = {
        'start_date': <b>DatePickerInput</b>
    }

在模板中,您需要呈现表单的媒体并加载 bootstrap css 和 javascript:

{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{{ <b>form.media</b> }}

<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

虽然 很棒,但有一些替代方案不需要额外的依赖项。

几个选项,按照努力程度的顺序排列:

  1. 使用SelectDateWidget instead of the default DateInput不需要 JavaScript):

    class MyForm(forms.Form):
        date = forms.DateField(widget=forms.SelectDateWidget())
    
  2. 使用浏览器的内置日期选择器,通过实现使用 HTML <input type="date"> 元素的自定义小部件(no JavaScript 必填):

    class MyDateInput(forms.widgets.DateInput):
        input_type = 'date'
    
    class MyForm(forms.Form):
        date = forms.DateField(widget=MyDateInput())
    

    或者,或者:

    class MyForm(forms.Form):
        date = forms.DateField(widget=forms.DateInput(attrs=dict(type='date')))
    
  3. 使用 django.contrib.admin 中的日期选择器,详见 here。简而言之,您需要一些东西:

    from django.contrib.admin.widgets import AdminDateWidget
    ...
    class MyForm(forms.Form):
        date = forms.DateField(widget=AdminDateWidget())
    

    然后,要使其正常工作,请将以下依赖项添加到您的模板 <head>:

    <link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
    <script src="{% static 'admin/js/core.js' %}"></script>
    <script src="{% url 'admin:jsi18n' %}"></script>  {# see note below #}
    {{ form.media }}  {# this adds 'calendar.js' and 'DateTimeShortcuts.js' #}   
    

    现在有一个问题:admin:jsi18n url 仅适用于具有管理员访问权限的用户,因此您可能需要替换它并在 urls.py 中定义替代路径,例如:

    from django.views import i18n
    ...
    urlpatterns = [
        ...,
        path('jsi18n/', i18n.JavaScriptCatalog.as_view(), name='jsi18n'),
    ]
    

最后,小部件的外观如下(在 Firefox 上):

我个人最喜欢第二种选择。它还允许我们指定初始值、最小值和最大值(在 django 中你可以这样做,例如使用 attrs 参数)。下面是显示 HTML 元素的快速片段:

<input type="date" value="2021-09-09" min="2021-09-09">

正如其他人所说,这是预料之中的,因为它只是一个特殊的文本字段。

我更喜欢的替代方法是使用 django-widget-tweaks,因为这会将 front-end 自定义推送回您的模板,而不是在后端编辑 forms.py。 Saving/testing 也更快,因为应用程序不必在每次保存时重新加载。

安装到您的环境:

pip install django-widget-tweaks

添加到已安装的应用程序:

INSTALLED_APPS = [
   ...
   "widget_tweaks",
]

添加到您的模板:

{% extends 'app/base.html' %}
{% load widget_tweaks %}

使用 render_field 和输入标签属性来自定义您的字段。例如下面使用 bootstrap 5. 注意我们如何在模板标签中指定 typeclass 等属性:

<div class="col-2">
   <label for="{{ form.date.id_for_label }}" class="col-form-label">{{ form.year.label }}</label>
</div>
<div class="col-4">
   {% render_field form.year type="date" class="form-control" placeholder="mm/dd/yyyy" %}
<div>

Django 4.0。把它留在这里以防它帮助别人。 这会将最小日期和默认值设置为今天的日期,应该在 forms.py 中使用。就我而言,我在我的 .html 中使用脆皮形式来呈现该字段。

from datetime import date

today = date.today()
    
class DateForm(forms.ModelForm):
   target_Date = forms.DateField(widget=forms.TextInput(attrs={'min': today, 'value': today, 'type': 'date'}), required=True)

class Meta:
    model = DateForm
    fields = ['target_Date']