Hello Neo4j community,
I recently encountered some issues with the DateProperty
and DateTimeProperty
classes in neomodel when trying to handle string inputs and store datetime values in a more readable format.
I recently encountered an issue with the DateProperty
class in neomodel when trying to save date values as strings. Specifically, I was getting the following error:
neomodel.exceptions.DeflateError: Attempting to deflate property 'due_date' on None of class 'Goal': datetime.date object expected, got '2025-12-31'
This error occurred because the deflate
method in DateProperty
was strictly expecting a date
object, but I was trying to save a string representation of a date.
To resolve this, I modified the DateProperty
class. Here's the change I made to the deflate
method:
1. DateProperty Changes:
The original deflate
method in DateProperty
was strictly expecting a date
object, which caused issues when trying to save string date inputs. I modified the deflate
method to handle string inputs as well:
@validator
def deflate(self, value):
if isinstance(value, str): # Added this line
try:
value = datetime.strptime(value, "%Y-%m-%d").date()
except ValueError:
raise ValueError(f"Invalid date format. Expected YYYY-MM-DD, got {value}")
if not isinstance(value, date):
msg = f"datetime.date object expected, got {repr(value)}"
raise ValueError(msg)
return value.isoformat()
With this change, I can now use string representations of dates (like '2025-12-31') when setting DateProperty fields, and neomodel correctly converts and stores them as date objects.
2. DateTimeProperty Changes:
The original DateTimeProperty was storing datetime values as Unix timestamps, which made them hard to read directly in Neo4j. I modified this class to store datetime values as ISO format strings and to handle string inputs (same as above). Storing as ISO formats might not be as universally desired, but the string conversion to date, as above, should prove useful:
@validator
def inflate(self, value):
if isinstance(value, str):
try:
return datetime.fromisoformat(value).replace(tzinfo=pytz.utc)
except ValueError:
raise ValueError(f"Invalid ISO format datetime string: {value}")
elif isinstance(value, datetime):
return value.replace(tzinfo=pytz.utc)
else:
raise TypeError(f"String or datetime object expected, got {type(value)}")
@validator
def deflate(self, value):
if isinstance(value, str):
try:
value = datetime.fromisoformat(value)
except ValueError:
raise ValueError(f"Invalid ISO format datetime string: {value}")
if not isinstance(value, datetime):
raise ValueError(f"datetime object expected, got {type(value)}.")
if value.tzinfo is None:
if config.FORCE_TIMEZONE:
raise ValueError(f"Error deflating {value}: No timezone provided.")
else:
value = value.replace(tzinfo=pytz.utc)
else:
value = value.astimezone(pytz.utc)
return value.isoformat()
These changes allow the DateTimeProperty to:
- Accept both string inputs in ISO format and datetime objects
- Store datetime values as readable ISO format strings in Neo4j
- Ensure all datetime values are in UTC for consistency
Has anyone else encountered similar issues or found alternative solutions? I'd be interested in hearing about other approaches or potential improvements to this solution.