Skip to content Skip to sidebar Skip to footer

How Do I Attach A Listener To A Date Selection In The Django Admin?

I have a Django admin page for a model with a DateField called 'date': # models.py class Article(models.Model): date = models.DateField( help_text='Article publication

Solution 1:

Why does your current solution not work?

The reason why this isn't working quite as you expect is that the django-admin date-time field is already run by a whole load of javascript. It is not a 'native' date-time input.

The input event will be fired whenever the element's value is changed by a user interaction. If the value is changed programatically, by another javascript function, the input event is not fired.

When you select a date from the calendar, this is what is happening. The value of the input is updated, but the input event is not fired.

How can I fix it?

There are 3 solutions that come to mind:

Option 1

Use a different widget. There are a whole heap of date selectors out there, some of which I am sure will have an easy API to add a call-back for when the value is changed. I don't know a good one off the top of my head, but google will be your friend here.

Options 2 and 3 involve using the current widget. If we dig a bit in the code, we see that there are 2 js files that are loaded that are important for this widget:

  • django/contrib/admin/static/admin/js/calendar.js
  • django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js

After a bit of digging about in there you'll see that there's a function called handleCalendarCallback in DateTimeShortcuts. This is the function that set's the value.

handleCalendarCallback: function(num) {
    var format = get_format('DATE_INPUT_FORMATS')[0];
    // the format needs to be escaped a little
    format = format.replace('\\', '\\\\')
        .replace('\r', '\\r')
        .replace('\n', '\\n')
        .replace('\t', '\\t')
        .replace("'", "\\'");
    return function(y, m, d) {
        DateTimeShortcuts.calendarInputs[num].value = new Date(y, m - 1, d).strftime(format);
        DateTimeShortcuts.calendarInputs[num].focus();
        document.getElementById(DateTimeShortcuts.calendarDivName1 + num).style.display = 'none';
    };
},

Option 2

Make your own widget that uses a new version of the above file with that one function edited a bit. It would look something like this:

class AdminDateWidget(forms.DateInput):
    class Media:
        js = [
            'admin/js/calendar.js',
            'admin/js/admin/MyVersionOfDateTimeShortcuts.js',
        ]

    def __init__(self, attrs=None, format=None):
        attrs = {'class': 'vDateField', 'size': '10', **(attrs or {})}
        super().__init__(attrs=attrs, format=format)

You could then use that widget for the relevant fields and you're edited code would run. Unfortunately because that file is a static file and not a template you can't use inheritance, so you would have to copy and paste the whole thing and then edit that one function.

Option 3

You'll notice that after the value is updated, .focus() is called, and so we could make use of that and attach an event listener to the focus event. Of course we'd only want something to happen if the value has actually changed, so we'd need to do something like this:

var dateValue;
window.onload = function() {
    var dateInput = document.getElementById("id_date");
    dateInput.addEventListener('focus', dateListener);

    function dateListener(event) {
        if (dateValue === event.target.value) {
            return
        }
        dateValue = event.target.value;
        // put the stuff you actually want to happen here
    }
}

Post a Comment for "How Do I Attach A Listener To A Date Selection In The Django Admin?"