neomodel Python OGM
neomodel is an Object Graph Mapper (OGM) for the Neo4j graph database, providing a Pythonic way to interact with Neo4j. It allows developers to define graph models using Python classes, making it easier to store, retrieve, and query data in a Neo4j database. The current version is 6.1.0, with major releases approximately annually and patch releases as needed.
Common errors
-
AttributeError: module 'neomodel.config' has no attribute 'DATABASE_URL'
cause Attempting to set the Neo4j connection URL using the deprecated `neomodel.config` module, which was removed in neomodel v6.0.0.fixUse `from neomodel import db; db.set_connection('bolt://user:password@host:port')` to configure your database connection. -
neo4j.exceptions.ServiceUnavailable: Failed to establish connection
cause The Neo4j database is not running, is inaccessible from the client, or the connection details (URL, username, password) provided to `db.set_connection` are incorrect.fixVerify that your Neo4j instance is running and accessible. Double-check the connection URL, username, and password passed to `db.set_connection`. Ensure correct firewall rules or network configuration. -
TypeError: Object of type <class 'neomodel.relationship.Relationship'> is not JSON serializable
cause Attempting to directly serialize a `StructuredNode` or `Relationship` object, which are not standard Python dicts and contain complex internal structures that JSON serializers cannot handle by default.fixBefore serializing, convert neomodel objects into a JSON-friendly format. For nodes, you can often use `node.__properties__` to get a dictionary of its simple properties, or manually select attributes. For relationships, you might need to extract specific properties like `type` or related nodes. -
ValueError: No default value for property 'my_required_property' defined
cause You are trying to create a `StructuredNode` instance without providing a value for a property that is marked `required=True` and does not have a `default` value defined.fixWhen creating a new node, ensure all `required=True` properties are provided a value, or define a `default` value for them in your model definition (e.g., `my_property = StringProperty(required=True, default='N/A')`). -
neomodel.exceptions.ConstraintValidationFailed: Node (X) already exists with label '...' and property '...' = '...'
cause You are attempting to create a node with a property value (e.g., an `email` address) that is marked `unique_index=True` on your model, but another node with the same value already exists in the database.fixBefore creating a new node, check if a node with the unique property already exists using `YourModel.nodes.get_or_none(your_unique_prop=value)`. If it exists, retrieve and use it; otherwise, create a new one. Also ensure `YourModel.ensure_indexes()` or `db.install_all_labels()` has been run to create the unique constraint in Neo4j.
Warnings
- breaking The `neomodel.config` module has been entirely removed in v6.0.0. All configuration, especially database connection details, must now be managed through the `neomodel.db` object.
- breaking The `StructuredNode.refresh()` method was removed in v6.0.0. Use `StructuredNode.reload()` instead to fetch the latest state of the node from the database.
- breaking The `db.cypher_query()` method is deprecated and should be replaced with `db.run()`. While `cypher_query` might still work, `db.run` offers a more consistent interface and better handles parameters.
- gotcha Unique constraints and indexes defined on `StructuredNode` properties (e.g., `unique_index=True`, `index=True`) are not automatically created in the Neo4j database upon model definition. You must explicitly call `YourModel.ensure_indexes()` or `db.install_all_labels()`.
- gotcha neomodel versions 6.x.x require Python 3.10 or newer. Attempting to install or run on older Python versions will result in dependency resolution failures or runtime errors.
Install
-
pip install neomodel
Imports
- StructuredNode
from neomodel import StructuredNode
- StringProperty
from neomodel import StringProperty
- IntegerProperty
from neomodel import IntegerProperty
- RelationshipTo
from neomodel import RelationshipTo
- RelationshipFrom
from neomodel import RelationshipFrom
- db
from neomodel import db
- config
from neomodel import config; config.DATABASE_URL = 'bolt://...'
from neomodel import db
Quickstart
import os
from neomodel import StructuredNode, StringProperty, IntegerProperty, RelationshipTo, db
# Configure connection to Neo4j
NEO4J_URL = os.environ.get('NEO4J_BOLT_URL', 'bolt://neo4j:password@localhost:7687')
db.set_connection(NEO4J_URL)
# Define a Node model
class Person(StructuredNode):
uid = StringProperty(unique_index=True)
name = StringProperty(index=True, required=True)
age = IntegerProperty(index=True, default=0)
# Define a relationship
friends = RelationshipTo('Person', 'FRIENDS_WITH')
# Ensure labels and constraints are created in the database
Person.ensure_indexes()
# Create and save nodes
jim = Person(uid='jim001', name='Jim', age=32).save()
pam = Person(uid='pam001', name='Pam', age=30).save()
# Create a relationship
jim.friends.connect(pam)
# Retrieve and query
found_jim = Person.nodes.get(uid='jim001')
print(f"Found: {found_jim.name}, Age: {found_jim.age}")
# Get friends
print(f"{found_jim.name} is friends with:")
for friend in found_jim.friends.all():
print(f"- {friend.name}")
# Clear database for example (use with caution in production)
# db.cypher_query("MATCH (n) DETACH DELETE n")
db.close_connection()