How to Use Custom Validators with Property Inflate/Deflate in Neomodel
Use the @validator decorator from neomodel.properties to wrap custom inflate and deflate methods, ensuring exceptions are automatically converted to InflateError or DeflateError for consistent error handling.
In neomodel, every property class defines two core methods for data conversion: inflate (Neo4j → Python) and deflate (Python → Neo4j). When implementing custom validators with property inflate deflate logic, you must decorate these methods to ensure proper error handling and integration with neomodel's exception hierarchy.
Understanding Property Inflate and Deflate in Neomodel
Every property in neomodel inherits from the base Property class defined in neomodel/properties.py. The two conversion hooks work as follows:
inflate(self, value, rethrow=False): Converts a raw value from the Neo4j driver into a Python-native type.deflate(self, value, rethrow=False): Converts a Python value into a format suitable for Neo4j storage.
These methods are wrapped by the validator decorator (lines 18-45 in neomodel/properties.py), which validates the method name, catches any exception raised by your implementation, and re-raises it as a dedicated InflateError or DeflateError.
The Validator Decorator Architecture
The @validator decorator is the critical component for implementing custom validators with property inflate deflate workflows. Located in neomodel/properties.py, it performs three essential functions:
- Method name validation: Ensures the decorated function is named either
inflateordeflate. - Exception translation: Catches any exception and wraps it in
InflateErrororDeflateError(orNeomodelExceptionfor unknown methods). - Rethrow control: Respects the
rethrow=Falseparameter used byArrayPropertyto suppress conversion-specific errors and allow container-level handling.
The decorator uses a match statement (lines 31-35) to map exceptions to the correct error type, ensuring consistent error handling across all property types.
Implementing Custom Validators with Inflate and Deflate
Creating a Custom Property Class
To create a property with custom validation logic, subclass Property (or NormalizedProperty) and decorate your inflate and deflate methods with @validator:
from neomodel import StructuredNode
from neomodel.properties import Property, validator
class UpperCaseString(Property):
"""Stores strings as uppercase in Neo4j but presents them as lowercase in Python."""
@validator
def inflate(self, value):
# Neo4j → Python: ensure string and convert to lowercase
if not isinstance(value, str):
raise ValueError("Expected a string from database")
return value.lower()
@validator
def deflate(self, value):
# Python → Neo4j: ensure string and convert to uppercase
if not isinstance(value, str):
raise ValueError("Expected a string value")
return value.upper()
Usage in a node class:
class Person(StructuredNode):
name = UpperCaseString()
# Writing: "Bob" is stored as "BOB" in Neo4j
person = Person(name="Bob").save()
# Reading: retrieved as "bob" in Python
retrieved = Person.nodes.get(uuid=person.uuid)
assert retrieved.name == "bob"
Adding Validators to Existing Properties
You can inject custom validation into existing property classes without subclassing by applying the decorator at runtime:
from neomodel.properties import StringProperty, validator
def non_empty_inflate(self, value):
if value == "":
raise ValueError("Empty strings are not allowed")
return value
# Wrap the custom logic with the validator decorator
StringProperty.inflate = validator(non_empty_inflate)
class Article(StructuredNode):
title = StringProperty(required=True)
Now, attempting to inflate an empty string from Neo4j raises an InflateError rather than a raw ValueError, maintaining consistency with neomodel's error hierarchy.
Handling Array Properties with rethrow=False
When working with container properties like ArrayProperty, use rethrow=False to allow the container to handle individual item failures:
from neomodel.properties import ArrayProperty, validator
class ValidatedArrayProperty(ArrayProperty):
@validator
def inflate(self, value, rethrow=False):
# Process array items, allowing container to handle individual failures
return super().inflate(value, rethrow=False)
The rethrow=False parameter suppresses the conversion-specific InflateError or DeflateError, allowing ArrayProperty to catch exceptions and handle them appropriately for individual array elements.
Error Handling and Exception Types
When using custom validators with property inflate deflate methods, the @validator decorator ensures all exceptions are mapped to specific neomodel exception types:
InflateError: Raised wheninflatefails to convert a Neo4j value to Python.DeflateError: Raised whendeflatefails to convert a Python value to Neo4j.NeomodelException: Generic fallback for unknown method names (should not occur in practice).
These exceptions are defined in the neomodel exception hierarchy and provide consistent error handling across your application, regardless of which specific property implementation raises the error.
Summary
- Use the
@validatordecorator fromneomodel.propertieswhen implementing custominflateanddeflatemethods to ensure proper error handling. - Implement custom validators by subclassing
Propertyand decorating conversion methods, or apply the decorator to existing properties at runtime. - Handle exceptions consistently through
InflateErrorandDeflateErrortypes, which the validator automatically raises when conversion fails. - Use
rethrow=Falsewhen implementing container properties like arrays to allow fine-grained error handling at the item level.
Frequently Asked Questions
What is the difference between inflate and deflate in neomodel?
Inflate converts values from Neo4j driver types to Python-native types when reading from the database, while deflate converts Python values back to Neo4j-compatible types when writing to the database. Both methods are defined in the base Property class in neomodel/properties.py and are wrapped by the @validator decorator to ensure consistent error handling.
How do I create a custom validator for a neomodel property?
Create a subclass of Property (or NormalizedProperty), implement inflate and/or deflate methods with your validation logic, and decorate each method with @validator imported from neomodel.properties. The decorator ensures any raised exceptions are converted to InflateError or DeflateError. Alternatively, you can apply the decorator to existing property classes at runtime to inject validation without subclassing.
What exceptions are raised when custom validators fail?
The @validator decorator catches any exception raised inside your inflate or deflate implementation and re-raises it as an InflateError (for inflate failures) or DeflateError (for deflate failures). These are specific neomodel exception types defined in the library's exception hierarchy, providing consistent error handling regardless of which property type fails.
Can I add validation to existing neomodel properties without subclassing?
Yes. You can monkey-patch existing property classes by assigning a decorated method to the class. Define your custom inflate or deflate function, wrap it with validator() from neomodel.properties, and assign it to the property class (e.g., StringProperty.inflate = validator(my_custom_inflate)). This approach allows you to inject validation logic into built-in property types without creating subclasses.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →