Wednesday, 4 August 2010

Django and Neo4J

So, today I was tempted by the approach taken in these slides about Neo4J and Django. My hope was/is that whilst the Neo4J implementation isn't complete, because I imagine many of the concepts simply won't easily map from a graph database to a SQL one, at least I might be able to work in a more familiar way in order to be able to learn about neo4j.

I started by grabbing the files from the model folder from here.... adding...

NEO4J_RESOURCE_URI = '/Users/tomsmith/neo4django_db' my file and then adding this code to a file...

from neo4j.model import django_model as models

class Actor(models.NodeModel):
    name= models.Property(indexed=True)
    href = property(lambda self:('/actor/%s/' %

    def __unicode__(self):

class Movie(models.NodeModel):
    title = models.Property(indexed=True)
    year = models.Property()
    href = property(lambda self:('/movies/%s/' %

    actors = models.Relationship(Actor,type=models.Outgoing.ACTS_IN,related_name="acts_in",)

    def title_length(self):
return len(self.title)

    def __unicode__(self):
return self.title

... and then from within python shell I could...

>> import stuff.models as m

>> movie = m.Movie(title="Fear and Loathing in Las Vegas",year=1998)

>>>>> movie = m.Movie(title="The Brothers Grimm",year=2005) 
>>> movie = m.Movie(title="Twelve Monkeys",year=1995) 
>>> movie = m.Movie(title="Memento",year=2000) 

... now I can...

>> movies = m.Movie.objects.all()
>>> for movie in movies:print, movie, movie.href
7 Memento /movies/7/
6 Twelve Monkeys /movies/6/
5 The Brothers Grimm /movies/5/
4 Fear and Loathing in Las Vegas /movies/4/
3 The Book of Eli /movies/3/
1 10,000 years B.C. /movies/1/

... which is fantastic! Don't you think?! The django_model is handling the db.transaction stuff. I can even do a ...

>> reload( m ) 

... and it doesn't break (because the database is already connected) as my earlier code did. (I think I can even run my django instance AND be connected to the neo4j database at the same time.. phew!).

 What I think is fantastic about it is that using this approach I get to work with objects in a familiar way. And by that, I mean, having a schema-less database is all well and good, but almost the first thing I seem to find myself doing is creating similar types of objects. So being able to create classes (maybe even with subclasses) seems perfect for my needs ... in theory. In theory, I'd like to be able to define classes with properties but then on-the-fly maybe add extra properties to objects (without changing my class definition).

AND I can add methods to my classes... or can I ?  I added a simple function to my Movie class...

     def title_length(self):
return len(self.title)

...and ran... 

>>> movies = m.Movie.objects.all()
>>> for movie in movies:print, movie, movie.href, movie.title_len()

...and got...

AttributeError: 'Movie' object has no attribute 'title_len'

... Boo! At this point after doing looking at the Movie objects, I found ...

>>> dir( m.Movie.objects )
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', '_copy_to_model', '_inherited', '_insert', '_set_creation_counter', '_update', 'aggregate', 'all', 'annotate', 'complex_filter', 'contribute_to_class', 'count', 'create', 'creation_counter', 'dates', 'defer', 'distinct', 'exclude', 'extra', 'filter', 'get', 'get_empty_query_set', 'get_or_create', 'get_query_set', 'in_bulk', 'iterator', 'latest', 'model', 'none', 'only', 'order_by', 'reverse', 'select_related', 'update', 'values', 'values_list'] I did a ...

>>> movies = m.Movie.objects.all().order_by('year')

...and got ...

Traceback (most recent call last):
  File "", line 1, in
AttributeError: 'NodeQuerySet' object has no attribute 'order_by'

...and the same is true for .get_or_create() or .values() or many other methods.

... so (and this isn't a criticism) it seems that the Django model is a "work in progress" (the cat ate his source code) with some of the methods still to be completed. At this point I should probably start looking at the django_model source code and begin adding to the functionality it offers... except ...

  1. I probably don't have the ability to create a database adapter, especially for one I don't understand
  2. I don't know if shoe-horning neo4j into Django is a good idea. I'm not saying it isn't ( so far, it's tantalising ) I'm just saying I don't know.
  3. When I find errors I'm not sure if it's me or not...
...for example, I tried...

>>> actor = m.Actor(name="Joseph Melito")
>>> actor = m.Actor(name="Bruce Willis")
>>> actor = m.Actor(name="Jon Seda")

...and all was fine and dandy and then I tried ...

>>> movie = m.Movie.objects.get(title="Twelve Monkeys")
>>> movie.actors.add ( actor )

... and got...

Traceback (most recent call last):
  File "", line 1, in
  File "/opt/local/lib/python2.5/site-packages/", line 623, in __get__
    return self._get_relationship(obj, self.__state_for(obj))
  File "/opt/local/lib/python2.5/site-packages/", line 708, in _get_relationship
    states[] = state = RelationshipInstance(self, obj)
  File "/opt/local/lib/python2.5/site-packages/", line 750, in __init__
    self.__removed = pyneo.python.set() # contains relationships
AttributeError: 'module' object has no attribute 'set'

... I wonder if Tobias has done more work on the Relationships class at all?

No comments:

Post a Comment