Upgraders: Beware of empty/null Date and DateTime values

FYI for those people upgrading from earlier versions (v13 and earlier).

Scenario

  1. You have a DocField with a datatype of either 'Date' or 'DateTime'.
  2. One of your documents has a value for this DocField that is blank/empty/null.

Earlier Versions of Frappe Framework:

some_date = frappe.db.get_single_value("MyDocType", "my_docfield")

The value of ‘some_date’ is a Python 'None' (NoneType)

Newer Versions of Frappe

(Version 15 definitely, perhaps 14 too. It’s hard to tell because of all the backporting that happens)

some_date = frappe.db.get_single_value("MyDocType", "my_docfield")

The value of ‘result’ is a Python Date type , with a value of 0001-01-01 (January 1st 1900)

Problems:

Consider the following code.

if not some_date:  # if the value is null  ...
    do_stuff()

This code block will execute in V13. But it will not execute in V15. :person_facepalming:
This is because a Python Date 0001-01-01 will not coerce to a boolean False.

  1. This Framework behavior is very surprising (which is why I’m writing my 10th forum topic)
  2. It’s very non-Pythonic and wrong, imo.

Python has a standard 'NoneType' for a reason: to help developers deal with the concept of ‘null’. Substituting the value “January 1st 1900” was always a workaround we made in previous decades, when we worked on older software that lacked proper typing for dates. January 1st 1900 is a legitimate date in history: it’s not a substitute for empty / blank / none / null / N/A, etc.

Anyhow, if this surprises anyone, I suggest you audit your custom code for any if not datefield: kind of evaluations.

(when I’m finished with my upgrade, I may to try reverting the Frappe code back to the old None behavior. see what breaks. and try to fix it. if I’m successful, I’ll shared my forked code.)

4 Likes

Has this change been declared anywhere?
It would be interesting to know the rationale, if any.

Otherwise, it could also be a bug, a simple oversight, or some moment’s sloppyness or fatigue of a very engaged dev, couldn’t it?

The “if field” and “if not field” idiom is used extensively IIRC. So this change might not be widespread, and I imagine that it might have happened in a database abstraction layer at a rather limited number of place(s).