Django: how to change the choices of AdminTimeWidget

一曲冷凌霜 提交于 2019-12-28 11:48:11

问题


The AdminTimeWidget rendered in admin for a DateTimeField displays an icon of a clock and when you click you have the choice between: "Now Midnight 6:00 Noon".

How can I change these choices to "16h 17h 18h"?


回答1:


Chris has a great answer. As an alternative you could do this using just javascript. Place the following javascript on the pages where you want the different time options.

DateTimeShortcuts.overrideTimeOptions = function () {
    // Find the first time element
    timeElement = django.jQuery("ul.timelist li").eq(0).clone();
    originalHref = timeElement.find('a').attr('href');

    // remove all existing time elements
    django.jQuery("ul.timelist li").remove();

    // add new time elements representing those you want
    var i=0;
    for (i=0;i<=23;i++) {
        // use a regular expression to update the link
        newHref = originalHref.replace(/Date\([^\)]*\)/g, "Date(1970,1,1," + i + ",0,0,0)");
        // update the text for the element
        timeElement.find('a').attr('href', newHref).text(i+"h");
        // Add the new element into the document
        django.jQuery("ul.timelist").append(timeElement.clone());
    }
}

addEvent(window, 'load', DateTimeShortcuts.overrideTimeOptions);



回答2:


Subclass AdminTimeWidget to include a modified DateTimeShortcuts.js (get to that in a sec), then subclass AdminSplitDateTime to include your subclassed MyAdminTimeWidget instead of the default Django one:

from django.contrib.admin.widgets import AdminTimeWidget
from django.conf import settings

class MyAdminTimeWidget(AdminTimeWidget):
    class Media:
        js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
              settings.MEDIA_URL + "js/admin/DateTimeShortcuts.js")

class MyAdminSplitDateTime(AdminSplitDateTime):
    def __init__(self, attrs=None):
        widgets = [AdminDateWidget, MyAdminTimeWidget]
        forms.MultiWidget.__init__(self, widgets, attrs)

The secret sauce is in django/contrib/admin/media/js/admin/DateTimeShortcuts.js. This is what creates the list you want to modify. Copy this file and paste it into your project's site_media/js/admin directory. The relevant code you need to modify is on lines 85-88:

quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");

Simply add to/delete from/modify that bit of javascript to your heart's content.

Finally, attach your new widget to any DateTimeFields you like. Your best bet for that will probably be the formfield_overrides attribute on ModelAdmin:

class MyModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.DateTimeField: {'widget': MyAdminSplitDateTime},
    }



回答3:


I tried using this method and found the above javascript didn't work when multiple datetime's were present on the form.

here is what I did.

In my ModelAdmin section i added:

class Media:
    js = ('js/clock_time_selections.js',)

then in the js file:

$('document').ready(function () {
    DateTimeShortcuts.overrideTimeOptions = function () {
        var clockCount = 0;
        console.log('ready');
        $('ul.timelist').each(function () {
            var $this = $(this);
            var originalHref = $this.find('a').attr('href');
            console.log(originalHref);
            $this.find('li').remove();
            for (i=8; i <= 20; i++) {
                var newLink = '<li><a href="javascript:DateTimeShortcuts.handleClockQuicklink('+ clockCount + ', ' + i
                    + ');"> ' + i + ':00h</a></li>';
                $this.append(newLink);
            }
            //console.log($this.html());

            clockCount++;
        });
    };

    addEvent(window, 'load', DateTimeShortcuts.overrideTimeOptions);
});

Note: i had to put inside a document.ready because i found that i couldn't control where the script was included in the page (seems to have be loaded before the default calendar js files).




回答4:


There's better solution. After reading DateTimeShortcuts.js the can be simplified to:

(function ($) {
    $(document).ready(function () {

        DateTimeShortcuts.clockHours.default_ = [];

        for (let hour = 8; hour <= 20; hour++) {
            let verbose_name = new Date(1970, 1, 1, hour, 0, 0).strftime('%H:%M');
            DateTimeShortcuts.clockHours.default_.push([verbose_name, hour])
        }

    });
})(django.jQuery);

Then add this code to the javascript file in 'static//time-shortcuts.js' and add Meta to your admin model:

from django.contrib import admin
from .models import MyModel

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    class Media:
        js = [
            '<myapp>/time-shortcuts.js',
        ]



回答5:


Overriding JS by DateTimeShortcuts.overrideTimeOptions function works only with one form ( bug: the change of time in child model affects parent model, so you can't change timefield in child model form by this widget)

If you want use custom time options with inlines:

in /static/admin/js/admin/DateTimeShortcuts.js replace:

quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href",    "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");

by:

for(j=6;j<=23;j++){
    quickElement("a", quickElement("li", time_list, ""), j+":00", "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1," + j + ",0,0,0).strftime('" + time_format + "'));");
}



回答6:


I went with a much simpler approach and it worked for me. I simply added choices to my model using the following code:

class Class(Model): program = ForeignKey('Program') time_of_the_day = TimeField(choices=( (datetime.datetime.strptime('7:00 am', "%I:%M %p").time(), '7:00 am'), (datetime.datetime.strptime('8:00 am', "%I:%M %p").time(), '8:00 am'), (datetime.datetime.strptime('9:00 am', "%I:%M %p").time(), '9:00 am'), (datetime.datetime.strptime('6:00 pm', "%I:%M %p").time(), '6:00 pm'), (datetime.datetime.strptime('7:00 pm', "%I:%M %p").time(), '7:00 pm'), (datetime.datetime.strptime('8:00 pm', "%I:%M %p").time(), '8:00 pm'), (datetime.datetime.strptime('9:00 pm', "%I:%M %p").time(), '9:00 pm'),
))

Hope this helps



来源:https://stackoverflow.com/questions/5770973/django-how-to-change-the-choices-of-admintimewidget

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!