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:

  1. Method name validation: Ensures the decorated function is named either inflate or deflate.
  2. Exception translation: Catches any exception and wraps it in InflateError or DeflateError (or NeomodelException for unknown methods).
  3. Rethrow control: Respects the rethrow=False parameter used by ArrayProperty to 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 when inflate fails to convert a Neo4j value to Python.
  • DeflateError: Raised when deflate fails 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 @validator decorator from neomodel.properties when implementing custom inflate and deflate methods to ensure proper error handling.
  • Implement custom validators by subclassing Property and decorating conversion methods, or apply the decorator to existing properties at runtime.
  • Handle exceptions consistently through InflateError and DeflateError types, which the validator automatically raises when conversion fails.
  • Use rethrow=False when 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →