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开发者_如何学Python"?
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);
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},
}
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).
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',
]
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
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 + "'));");
}
Expanding on @Bit68's answer, assuming other folks might want to create lists of regularly spaced times more than once, I created a helper function to build a choices tuple. (I'm adding a new answer because this length of code is too hard to follow in a comment.) This works in Django 2.2.
Note that this creates a dropdown list of options, it doesn't add options to the default admin date/time widget as the javascript methods do.
import datetime
def get_time_choices(start_time=datetime.time(9,0,0), end_time=datetime.time(17,0,0), delta=datetime.timedelta(minutes=15)):
'''
Builds a choices tuple of (time object, time string) tuples
starting at the start time specified and ending at or before
the end time specified in increments of size delta.
The default is to return a choices tuple for
9am to 5pm in 15-minute increments.
'''
time_choices = ()
time = start_time
while time <= end_time:
time_choices += ((time, time.strftime("%I:%M %p")),)
# This complicated line is because you can't add
# a timedelta object to a time object.
time = (datetime.datetime.combine(datetime.date.today(), time) + delta).time()
return time_choices
Then time_of_the_day = models.TimeField(choices=get_time_choices())
精彩评论