Skip to content

Instantly share code, notes, and snippets.

@mhluongo
Last active December 17, 2015 19:59
Show Gist options
  • Save mhluongo/5664051 to your computer and use it in GitHub Desktop.
Save mhluongo/5664051 to your computer and use it in GitHub Desktop.
Some relationship magic to make modeling easier for situations where you want to aggregate across multiple rel-types in one relationship field. This is a very not-supported / non-standard approach, and could be broken in future versions of neo4django.
>>> from .models import Family, Person
>>>
>>> family_1 = Family.objects.create(status='married')
>>> family_2 = Family.objects.create()
>>>
>>> pete = Person.objects.create(name='Pete')
>>> mary = Person.objects.create(name='Mary')
>>>
>>> family_1.spouses.add(pete, mary)
>>> family_2.children.add(pete)
>>> mary.families.all()
[<Family: Family object>]
>>> pete.families.all()
[<Family: Family object>, <Family: Family object>]
>>> pete.spouse_families.all()
[<Family: Family object>]
from neo4django.db import models
#############################
# CUSTOM RELATIONSHIP MAGIC #
#############################
from neo4django.db.models.query import (Clauses, Start, With)
from neo4django.db.models.relationships import (Relationship,
RelationshipQuerySet,
MultipleNodes,
RelationshipInstance)
class FamilyRelationshipQuerySet(RelationshipQuerySet):
def _get_start_clause(self):
return Clauses([
Start({'m':'node({startParam})'}, ['startParam']),
'MATCH (m)<-[:SPOUSE|CHILD]-(n)',
With({'n':'n', 'typeNode':'typeNode'})
])
class FamilyRelationshipInstance(RelationshipInstance):
"""
The manager for the `person.families` side of the spouse|child / families
relationship.
"""
def remove(self, *args, **kwargs):
raise RuntimeError("This relationship is read-only.")
def clear(self, *args, **kwargs):
raise RuntimeError("This relationship is read-only.")
def add(self, *args, **kwargs):
raise RuntimeError("This relationship is read-only.")
def get_query_set(self):
return FamilyRelationshipQuerySet(self, self._rel, self._obj)
class MultipleFamilyNodes(MultipleNodes):
def _get_state(self, obj, states):
state = states.get(self.name)
if state is None:
states[self.name] = state = FamilyRelationshipInstance(self, obj)
return state
def _setup_reversed(self, target):
# don't set up a reverse relationship at all- target should be none
pass
class FamiliesRelationship(Relationship):
def _get_bound_relationship_type(self):
return MultipleFamilyNodes
#####################
# MODEL DEFINITIONS #
#####################
class Person(models.NodeModel):
class Meta:
app_label = 'test'
name = models.StringProperty()
born = models.DateTimeProperty()
died = models.DateTimeProperty()
sex = models.StringProperty()
friends = models.Relationship('self', rel_type='friends_with')
families = FamiliesRelationship('Family', rel_type='<<UNUSED_REL_TYPE>>')
class Evidence(models.NodeModel):
class Meta:
app_label = 'test'
name = models.StringProperty()
as_of = models.DateTimeProperty()
families = models.Relationship('Family',rel_type='FAMILY',related_name='evidence')
persons = models.Relationship('Person',rel_type='PERSON',related_name='evidence')
next = models.Relationship('self',rel_type='NEXT',related_name='prev')
class Family(models.NodeModel):
class Meta:
app_label = 'test'
status = models.StringProperty() # married, etc.
spouses = models.Relationship('Person',rel_type='SPOUSE', related_name='spouse_families')
children = models.Relationship('Person',rel_type='CHILD', related_name='children_families')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment