Sử dụng các tiện ích thời gian / ngày Django ở dạng tùy chỉnh


171

Làm cách nào tôi có thể sử dụng các tiện ích ngày và giờ JavaScript tiện lợi mà quản trị viên mặc định sử dụng với chế độ xem tùy chỉnh của tôi?

Tôi đã xem qua tài liệu biểu mẫu Django và nó đề cập ngắn gọn về django.contrib.admin.widgets, nhưng tôi không biết cách sử dụng nó?

Đây là mẫu của tôi mà tôi muốn nó được áp dụng.

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

Ngoài ra, tôi nghĩ cần lưu ý rằng tôi chưa thực sự viết một quan điểm cho bản thân mình cho hình thức này, tôi đang sử dụng một quan điểm chung. Đây là mục từ url.py:

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

Và tôi hoàn toàn mới đối với toàn bộ điều Django / MVC / MTV, vì vậy hãy đi dễ dàng ...



Câu trả lời:


159

Sự phức tạp ngày càng tăng của câu trả lời này theo thời gian và nhiều yêu cầu hack, có lẽ nên cảnh báo bạn không nên làm điều này. Nó dựa vào các chi tiết triển khai nội bộ không có giấy tờ của quản trị viên, có khả năng sẽ phá vỡ một lần nữa trong các phiên bản tương lai của Django và không dễ thực hiện hơn là chỉ tìm một tiện ích lịch JS khác và sử dụng tiện ích đó.

Điều đó nói rằng, đây là những gì bạn phải làm nếu bạn quyết tâm thực hiện công việc này:

  1. Xác định lớp con ModelForm của riêng bạn cho mô hình của bạn (tốt nhất là đặt nó trong Forms.txt trong ứng dụng của bạn) và bảo nó sử dụng AdminDateWidget / AdminTimeWidget / AdminSplitDateTime (thay thế 'mydate', v.v. bằng tên trường thích hợp từ mô hình của bạn):

    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. Thay đổi URLconf của bạn để chuyển 'form_group': ProductForm thay vì 'model': Sản phẩm sang chế độ xem chung tạo_object (nghĩa là "từ my_app.forms nhập ProductForm" thay vì "từ my_app.models nhập sản phẩm", tất nhiên).

  3. Trong phần đầu của mẫu của bạn, bao gồm {{form.media}} để xuất các liên kết đến các tệp Javascript.

  4. Và phần hacky: các widget ngày / giờ của quản trị viên giả định rằng các công cụ JS i18n đã được tải và cũng yêu cầu core.js, nhưng không tự động cung cấp một cái. Vì vậy, trong mẫu của bạn ở trên {{form.media}} bạn sẽ cần:

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

    Bạn cũng có thể muốn sử dụng CSS quản trị sau (cảm ơn Alex đã đề cập đến điều này):

    <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"/>

Điều này ngụ ý rằng phương tiện quản trị của Django (ADMIN_MEDIA_PREFIX) là tại / media / admin / - bạn có thể thay đổi điều đó cho thiết lập của mình. Lý tưởng nhất là bạn sử dụng bộ xử lý ngữ cảnh để chuyển các giá trị này vào mẫu của bạn thay vì mã hóa nó, nhưng điều đó nằm ngoài phạm vi của câu hỏi này.

Điều này cũng yêu cầu URL / my_admin / jsi18n / được kết nối thủ công với chế độ xem django.view.i18n.javascript_catalog (hoặc null_javascript_catalog nếu bạn không sử dụng I18N). Bạn phải tự làm việc này thay vì truy cập ứng dụng quản trị viên để có thể truy cập được bất kể bạn có đăng nhập vào quản trị viên hay không (cảm ơn Jeremy vì đã chỉ ra điều này). Mã mẫu cho URLconf của bạn:

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

Cuối cùng, nếu bạn đang sử dụng Django 1.2 trở lên, bạn cần một số mã bổ sung trong mẫu để giúp các widget tìm phương tiện của chúng:

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

Cảm ơn lupefiasco cho sự bổ sung này.


3
Bài đăng hữu ích nhất trên mạng để thực hiện việc này nhưng cuối cùng đã có tiện ích ngày / giờ xuất hiện và trường điền hiện sẽ loại bỏ nó vì mặc dù có yêu cầu = Sai trong trường biểu mẫu, giờ đây nó hiển thị thông báo "Nhập ngày / giờ hợp lệ." cho trường không bắt buộc của tôi nếu tôi để trống. Quay lại jquery cho tôi.
PhoebeB

Đây có phải là cách để đi trên Django 1.1.1?
Nacho

1
Trong Django 1.2 RC1 trở lên, bạn cần thực hiện một thay đổi nhỏ ở trên. Xem: stackoverflow.com/questions/38601/ từ
mshafrir

1
Bạn có biết nếu điều này vẫn áp dụng cho Django 1.4? Mặc dù tôi bị thuyết phục khi sử dụng công cụ hẹn hò của jQuery, tôi tò mò liệu nhóm Django có thể làm cho tiện ích của họ công khai hơn không. (Tôi nghi ngờ là không, vì QA này dường như là tài liệu nhiều nhất xung quanh)
John C

1
Không có bất kỳ động thái nào trong việc làm cho các widget quản trị viên dễ dàng tái sử dụng hơn bên ngoài quản trị viên và tôi không đoán rằng sẽ có bất kỳ tiện ích nào. Nó sẽ là rất nhiều công việc, và có những mục ưu tiên cao hơn nhiều để làm việc. (Tôi là một phần của nhóm nòng cốt Django, btw).
Carl Meyer

64

Vì giải pháp là hackish, tôi nghĩ rằng sử dụng tiện ích ngày / giờ của riêng bạn với một số JavaScript là khả thi hơn.


8
Tôi đồng ý. Tôi đã thử làm điều này một vài tháng trước với dự án django của tôi. Cuối cùng tôi đã từ bỏ và chỉ thêm riêng mình với jQueryUI. Mất tất cả 10 phút để nó hoạt động.
linh mục

8
Đối với hồ sơ, tôi đồng ý, tôi đã đưa ra câu trả lời này và tôi chưa bao giờ thực sự sử dụng kỹ thuật này trong câu trả lời của mình trong một trang sản xuất ;-)
Carl Meyer

12

Đúng, cuối cùng tôi đã ghi đè / admin / jsi18n / url.

Đây là những gì tôi đã thêm vào trong url của mình. Hãy chắc chắn rằng nó ở trên / admin / url

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

Và đây là chức năng i18n_javascript mà tôi đã tạo.

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

Oh, điểm rất tốt. Tôi sẽ thêm câu này vào câu trả lời của tôi ở trên để mọi người dễ dàng tìm thấy hơn trước khi họ gặp rắc rối.
Carl Meyer

12

Tôi thấy mình tham khảo bài này rất nhiều, và thấy rằng các tài liệu định nghĩa một chút cách ít hacky để widget ghi đè mặc định.

( Không cần ghi đè phương thức __init__ của ModelForm )

Tuy nhiên, bạn vẫn cần kết nối phù hợp với JS và CSS của mình như Carl đề cập.

biểu mẫu

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

Các loại trường tham chiếu để tìm các trường mẫu mặc định.


5
Tôi không đồng ý rằng cách tiếp cận này ít hackish hơn. Nó trông đẹp hơn, chắc chắn, nhưng bạn hoàn toàn ghi đè các trường biểu mẫu được tạo bởi biểu mẫu mô hình, cũng có thể xóa các tùy chọn khỏi các trường mô hình, như helpSphere. Ghi đè init chỉ thay đổi widget và để phần còn lại của trường biểu mẫu như cũ.
Carl Meyer

11

Mã đầu của tôi cho phiên bản 1.4 (một số mới và một số đã bị xóa)

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

3
tuyệt vời . tôi đã thử điều này từ 2 tuần trước và điều này giúp tôi cảm ơn rất nhiều
numah

sử dụng {{STATIC_URL}} hoặc {% load staticfiles%} với {% static 'admin / js / core.js'%} ... là bắt buộc đối với Django 1.4+, vì toàn bộ không gian tên MEDIA_URL không được dùng nữa và bị xóa. / admin / jsi18n giải quyết đúng nếu bạn có ^ admin / ánh xạ trong máy chủ / urls.py
jeberle

những thứ hay ho, hữu ích trong trường hợp của Django 1.4
Ashish

10

Bắt đầu từ Django 1.2 RC1, nếu bạn đang sử dụng thủ thuật xóa sổ chọn ngày quản trị viên Django, thì phần sau phải được thêm vào mẫu của bạn hoặc bạn sẽ thấy url biểu tượng lịch được tham chiếu qua "/ thiếu-admin-media-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>

7

Bổ sung câu trả lời của Carl Meyer, tôi muốn nhận xét rằng bạn cần đặt tiêu đề đó trong một số khối hợp lệ (bên trong tiêu đề) trong mẫu của bạn.

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

Tôi cũng thấy liên kết này hữu ích: Groups.google.ca/group/django-users/msg/1a40cf5f153cd23e
Alex. S.

6

Đối với Django> = 2.0

Lưu ý: Sử dụng tiện ích quản trị cho các trường thời gian không phải là ý kiến ​​hay vì các biểu định kiểu quản trị viên có thể xung đột với các biểu định kiểu trang web của bạn trong trường hợp bạn đang sử dụng bootstrap hoặc bất kỳ khung CSS nào khác. Nếu bạn đang xây dựng trang web của mình trên bootstrap, hãy sử dụng tiện ích bootstrap-datepicker của tôi django-bootstrap-datepicker-plus .

Bước 1: Thêm javascript-catalogURL vào urls.pytệp của dự án (không phải ứng dụng) .

from django.views.i18n import JavaScriptCatalog

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

Bước 2: Thêm tài nguyên JavaScript / CSS cần thiết vào mẫu của bạn.

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

Bước 3: Sử dụng các widget quản trị cho các trường nhập ngày giờ trong của bạn 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,
        }

5

Dưới đây cũng sẽ làm việc như là phương sách cuối cùng nếu thất bại ở trên

class PaymentsForm(forms.ModelForm):
    class Meta:
        model = Payments

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

Giống như

class PaymentsForm(forms.ModelForm):
    date = forms.DateField(widget=SelectDateWidget())

    class Meta:
        model = Payments

đặt cái này trong mẫu của bạn from django.forms.extras.widgets import SelectDateWidget


1
nó gây ra lỗi cho tôi 'Đối tượng DateField' không có thuộc tính 'cần_multipart_form'
madeeha ameer

3

Điều gì về việc chỉ định một lớp cho widget của bạn và sau đó ràng buộc lớp đó với công cụ hẹn hò JQuery?

Biểu mẫu Django:

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'

Và một số JavaScript cho mẫu:

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


1

Đã cập nhật giải pháp và cách giải quyết cho SplitDateTime với yêu cầu = Sai :

biểu mẫu

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>

url

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

Tôi cũng đã điền một vé để sửa mã widget SplitDateTime code.djangoproject.com/ticket/12303
Sam A.

1

Tôi sử dụng cái này, thật tuyệt, nhưng tôi có 2 vấn đề với mẫu:

  1. Tôi thấy các biểu tượng lịch hai lần cho mỗi lần nộp trong mẫu.
  2. Và đối với TimeField tôi có ' Nhập một ngày hợp lệ. '

    Dưới đây là một ảnh chụp màn hình của Mẫu

mô hình

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)

biểu mẫu

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

0

Trong Django 10. myproject / urls.py: ở đầu urlpotypes

  from django.views.i18n import JavaScriptCatalog

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

Trong mẫu của tôi.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>

0

Cài đặt Django của tôi: 1.11 Bootstrap: 3.3.7

Vì không có câu trả lời nào hoàn toàn rõ ràng, tôi đang chia sẻ mã mẫu của mình mà không có lỗi nào cả.

Nửa trên của mẫu:

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

Nửa dưới:

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

0

Ngày 3 tháng 6 năm 2020 (Tất cả các câu trả lời không hoạt động, bạn có thể thử giải pháp này tôi đã sử dụng. Chỉ dành cho TimeField)

Sử dụng đơn giản Charfieldcho các trường thời gian ( bắt đầukết thúc trong ví dụ này) trong các biểu mẫu.

biểu mẫu

chúng ta có thể sử dụng Formhoặc ModelFormở đây.

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

Chuyển đổi đầu vào chuỗi thành đối tượng thời gian trong chế độ xem.

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.