How to Integrate neomodel with Django Using django_neomodel: A Complete Guide
You can integrate neomodel with Django by installing the django-neomodel package, configuring Neo4j connection settings in your Django settings.py, and inheriting from DjangoNode instead of Django's Model to create graph-aware models that support Django-style queries and signals.
The neo4j-contrib/neomodel library provides a powerful Object-Graph Mapper (OGM) for Neo4j, but integrating it into a Django project requires a bridge to handle settings, connections, and the familiar ORM API. The django-neomodel package serves as that bridge, allowing you to define StructuredNode models that behave like Django models while leveraging Neo4j's graph capabilities.
What Is django_neomodel and Why Use It?
django_neomodel acts as a thin architectural bridge between Django's request/response cycle and neomodel's graph-mapping layer. The integration works through three distinct layers:
- Django: Manages the traditional request cycle, settings, and deployment workflow.
- django_neomodel: Supplies the
DjangoNodebase class, reads Neo4j credentials fromsettings.py, and registers Django signals (pre_save,post_save) that map to neomodel hooks. - neomodel: Implements the actual OGM logic, Cypher query building, and transaction handling in
neomodel/core.pyandneomodel/match.py.
The bridge ensures a singleton Database object reuses the driver created by Django, maintaining a single connection pool throughout the process. This design allows you to write Django-style code while neomodel handles the underlying Cypher generation.
Installation and Configuration
Install both packages using pip. The extras flag for neomodel ensures optional dependencies are available:
pip install neomodel[extras]
pip install django-neomodel
Configure your Neo4j connection in settings.py. django_neomodel reads these variables automatically via django_neomodel.connection.get_connection():
# settings.py
NEO4J_BOLT_URL = "bolt://neo4j:password@localhost:7687"
NEO4J_ENCRYPTED = False # Optional, defaults to True
NEO4J_MAX_CONNECTION_POOL_SIZE = 50 # Optional
Add django_neomodel to your INSTALLED_APPS if you need its management commands or admin integration:
INSTALLED_APPS = [
# ...
'django_neomodel',
]
Defining Graph Models with DjangoNode
Instead of inheriting from django.db.models.Model, your models inherit from django_neomodel.models.DjangoNode. This class extends neomodel's StructuredNode (defined in neomodel/core.py) to work within Django's ecosystem.
# myapp/models.py
from django_neomodel import DjangoNode
from neomodel import StringProperty, FloatProperty, ArrayProperty, VectorIndex
class Product(DjangoNode):
"""
A product node stored in Neo4j, accessible via Django's API.
"""
name = StringProperty()
description = StringProperty()
# Semantic index for vector similarity search
description_embedding = ArrayProperty(
FloatProperty(),
vector_index=VectorIndex(dimensions=2)
)
class Meta:
app_label = 'myapp'
The DjangoNode base class automatically registers the model with Django's app registry while preserving all neomodel functionality, including schema enforcement and relationship definitions.
Querying Neo4j with Django-Style Syntax
django_neomodel translates Django-style lookups into Cypher queries. You can use familiar double-underscore syntax (__gt, __lte, __contains) alongside neomodel's advanced features like vector similarity filters.
# views.py
from .models import Product
from neomodel.semantic_filters import VectorFilter
# Standard Django lookup translated to Cypher WHERE clause
expensive_products = Product.nodes.filter(price__gt=100).all()
# Combine vector similarity with Django numeric filters
# This demonstrates the seamless API integration tested in test/sync_/test_vectorfilter.py
similar_products = Product.nodes.filter(
vector_filter=VectorFilter(
topk=5,
vector_attribute_name="description_embedding",
candidate_vector=[0.12, 0.34],
),
price__lte=200,
).all()
As implemented in neomodel/semantic_filters.py, the VectorFilter class constructs the appropriate Cypher for vector index queries, while the standard lookups are handled by neomodel's query builder in neomodel/match.py. The test file test/sync_/test_vectorfilter.py (lines 91-100) demonstrates this exact pattern of mixing vector filters with Django-style numeric lookups.
Leveraging Django Signals and Hooks
One of the primary benefits of django_neomodel is the automatic wiring of Django's signal system to neomodel's lifecycle hooks. As documented in doc/source/hooks.rst, you can use standard Django signals while operating on graph data.
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Product
import logging
logger = logging.getLogger(__name__)
@receiver(post_save, sender=Product)
def product_saved_handler(sender, instance, **kwargs):
"""
Executed after a Product node is persisted to Neo4j.
The django_neomodel bridge ensures this fires correctly.
"""
logger.info(f"Product {instance.name} saved to Neo4j graph")
The django_neomodel module registers these signal handlers internally, mapping pre_save and post_save to neomodel's native hooks. This allows you to reuse existing Django signal logic without modification when switching from a relational database to Neo4j.
Key Source Files and Implementation Details
Understanding the integration architecture requires examining specific files in the neo4j-contrib/neomodel repository:
-
neomodel/properties.py: Defines property classes likeStringPropertyandArrayPropertywith optionallabelandhelp_textarguments consumed bydjango_neomodelfor form generation. -
doc/source/index.rst: Documents the Django integration and links to the externaldjango_neomodelrepository, confirming the supported integration pattern. -
doc/source/hooks.rst: Explicitly states that Django signals are supported via thedjango_neomodelmodule, detailing the signal registration mechanism. -
test/sync_/test_vectorfilter.py(lines 91-100): Contains the test casetest_django_filter_w_vector_filterdemonstrating the combination ofVectorFilterwith Django-style numeric lookups likenumber__gt=5. -
neomodel/semantic_filters.py: Implements theVectorFilterclass used for semantic similarity queries within Django-style filter calls.
These files demonstrate how neomodel is designed to be consumed by external frameworks, with django_neomodel acting as the official bridge for Django projects.
Summary
- django_neomodel bridges Django's ecosystem with neomodel's graph mapping, allowing you to use Django settings, signals, and query syntax with Neo4j.
- Configure the integration by setting
NEO4J_BOLT_URLand other variables insettings.py, whichdjango_neomodel.connection.get_connection()reads automatically. - Inherit from
DjangoNodeinstead of Django'sModelto create graph-aware classes that support both neomodel properties and Django's app registry. - Query using Django-style lookups (
__gt,__lte) alongside advanced neomodel features likeVectorFilterfor semantic search, as demonstrated intest/sync_/test_vectorfilter.py. - Leverage existing Django signals (
pre_save,post_save) without modification, as the bridge automatically wires them to neomodel's lifecycle hooks according todoc/source/hooks.rst.
Frequently Asked Questions
What is the difference between neomodel and django_neomodel?
neomodel is the core Object-Graph Mapper for Neo4j that provides the StructuredNode base class, property definitions, and Cypher query building. django_neomodel is a separate package that acts as a bridge, providing the DjangoNode base class, reading Django settings for Neo4j connections, and registering Django signals with neomodel hooks. You need both packages to use Neo4j within a Django project while maintaining Django's familiar patterns.
Can I use Django's admin interface with neomodel models?
Yes, but with limitations. Because DjangoNode inherits from neomodel's StructuredNode rather than Django's Model, it does not automatically integrate with Django's built-in admin interface for relational databases. However, you can create custom admin views or use third-party packages that bridge neomodel with Django admin. The label and help_text arguments in neomodel/properties.py are specifically designed to support form generation when such integrations are built.
How do I handle migrations with django_neomodel?
django_neomodel does not use Django's migration system because Neo4j is schema-optional and neomodel handles schema enforcement through its own install_labels and install_all_labels utilities. Instead of running makemigrations and migrate, you use neomodel's built-in commands to create indexes and constraints in Neo4j. The connection settings in settings.py are read by django_neomodel.connection.get_connection(), but database schema changes are managed through neomodel's native OGM methods rather than Django's relational migration framework.
Is it possible to use both Django ORM and neomodel in the same project?
Yes, this is a common pattern for polyglot persistence. You can use Django's standard ORM for relational data (PostgreSQL, MySQL, etc.) while using django_neomodel for graph data in Neo4j. Simply define traditional models inheriting from django.db.models.Model alongside graph models inheriting from django_neomodel.DjangoNode. The two systems operate independently, allowing you to store user sessions and transactional data in SQL while keeping complex relationship data in Neo4j, leveraging the strengths of each database within the same Django project.
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 →