Py2neo Is End-of-Life – A Basic Migration Guide

After a long and awesome ride, the much-loved py2neo has come to an end, by the decision of its creator and maintainer. The Neo4j community is grateful to Nigel Small for the work he put into the project and the ideas that originated there. py2neo elegantly bridged graph thinking with Pythonic principles.

The py2neo GitHub project has moved to neo4j-contrib/py2neo and the artifacts are still available on pypi, yet it is no longer maintained.

For ongoing work with Neo4j in Python, we recommend migrating to the official driver or, if your usage requires it, neomodel. In this blog, we will guide you through migrating your data from py2neo to neomodel. See below in which cases you should consider it.

Picture by Dino Reichmuth @ Unsplash

Is This a Guide for Me?

If you use py2neo for one of these use cases :

  • Connecting to a Neo4j database to push Cypher queries and read the results.
  • As an OGM.

Then, yes, this guide can be helpful for you. If you use more specific features like the Cypher lexer, admin tools, or bulk import, this is not something we cover here — see the last section to see what is not covered yet.

Our library of choice to replace py2neo for these use cases is neomodel. It is an open-source Python OGM for Neo4j and is part of Neo4j labs, which ensures a good level of code quality and maintenance.

Step 1: Installation

First things first, let’s get Neomodel installed. You can do this using pip:

pip install neomodel

Step 2: Update Imports

Replace your Py2neo imports with Neomodel imports in your Python scripts:

# Replace this Py2neo import
from py2neo import Graph, Node, Relationship

# With this Neomodel import
from neomodel import db, config, StructuredNode, RelationshipTo, RelationshipFrom

Step 3: Connect to Neo4j

Update your connection configuration to use Neomodel:

# Py2neo connection
graph = Graph("bolt://localhost:7687", auth=("username", "password"))

# Neomodel connection
config.DATABASE_URL = "bolt://username:password@localhost:7687"

Documentation — Configuration options

Step 4: Cypher querying

If you have Cypher queries pushed through py2neo, here is how to do the same using neomodel :

# Py2neo Query
results ="MATCH (p:Person) RETURN AS name")

# Neomodel Query
results, meta = db.cypher_query("MATCH (p:Person) RETURN AS name")

Note that the format of the result object will be different — you will have to update the parsing yourself, I’m afraid. Note that neomodel can project Cypher results into Python classes automatically when you have OGM classes defined — see below.

Here is a small utility function to make it easier for you should you need it :

# This returns a dictionary, the keys being the name of the Cypher variables - 'name' in our example
results_as_dict = [dict(zip(meta, row)) for row in results]

Documentation — Cypher queries

Step 5: OGM Usage: Defining Objects and Fetching Them

In Neomodel, you’ll be defining your models as classes that inherit from StructuredNode:

# py2neo models
from py2neo.ogm import GraphObject, Property, RelatedTo

class Person(GraphObject):
name = Property()
knows = RelatedTo("Person")

people = Person.match(graph).limit(3)

# neomodel
from neomodel import StructuredNode, StringProperty, RelationshipTo

class Person(StructuredNode):
name = StringProperty(unique_index=True)
knows = RelationshipTo('Person', 'KNOWS')

# Note how neomodel transforms Python native slicing into Cypher limit
people = Person.nodes.all()[:3]

Documentation — Defining nodes and relationships

Documentation — Slicing and filtering

Documentation — Advanced queries

If you have an existing database, we added a database inspection script in release 5.2.0. Running the following will write a Python class file containing imports and classes for everything found in the database : nodes and relationships with their properties, indexes and constraints.

neomodel_inspect_database -db bolt://neo4j:neo4j@localhost:7687 --write-to yourapp/

Step 6: OGM Usage — Creating Objects

Define relationships using RelationshipTo and RelationshipFrom:

# py2neo
person_node = Node("Person", name="John Doe")
friend_node = Node("Person", name="Jane Doe")
# Create a relationship
knows_relationship = Relationship(person_node, "KNOWS", friend_node)

# neomodel
class Person(StructuredNode):
name = StringProperty(unique_index=True)
knows = RelationshipTo('Person', 'KNOWS')
# Create a relationship
john = Person(name='John').save()
jane = Person(name='Jane').save()

Documentation — CRUD operations

Step 7: Indexing

Neomodel uses unique indexing differently. You can add it directly to the model properties:

class Person(StructuredNode):
name = StringProperty(unique_index=True)

This example will create a uniqueness constraint on the name property for the label Person.

Documentation — Applying indexes

Step 8: Testing

Thoroughly test your application to ensure that the migration was successful.

Out of Scope

Writing this article as one of the main maintainers of neomodel, I currently only have minimal knowledge of py2neo. So I can not, at the time of writing, give an exhaustive list of what py2neo features can be replaced by neomodel features. However, here is a partial list of what I do know is not covered — yet :

  • Converting results of a Cypher query to pandas DataFrame/Series and numpy ndarray objects is available from 5.2.0 onwards
# pandas must be installed manually
from neomodel.integration.pandas import to_dataframe
df = to_dataframe(
db.cypher_query("MATCH (a:User) RETURN AS name, AS email")
  • Converting to sympy objects is not available
  • Utility functions for Jupyter notebooks like to_table
  • Cypher Lexer
  • Admin functions
  • Command line package — native cypher-shell allows you to do the same things as py2neo run
  • A comparison of bulk import features will have to be done — neomodel does have features for this, but it might not be on par with py2neo

If you are using any of this with py2neo today, and need this to be made available in neomodel before migrating, please chime in on the Neo4j community forum or GitHub issues.


That’s it! You should now have a working Neomodel-based application. Remember to refer to the Neomodel documentation for more detailed information and advanced features. Good luck!

If you have questions or would like to provide feedback on your use cases that are not covered in this post, please refer to this post in the Neo4j community forum where we want to provide some guidance and collect feedback.

Finally, if you own a Neo4j license and feel that you need support with migrating away from py2neo. In general, with any of your Neo4j use cases, our Professional Services can help you. Contact us at

Py2neo Is End-of-Life — A Basic Migration Guide was originally published in Neo4j Developer Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.