Table Of Contents

Previous topic

Delete Cascade

Next topic

Pylons integration support

This Page

Using relational

When you describe your MongoDocument, you can add some information about the relations beetween your objects. This will let you have a simple api to query all related objects from document.

A simple example

Let’s create a blog post object :

>>> class BlogPost(MongoDocument):
...    db_name = "test"
...    collection_name = "mongokit"
...    structure = {
...        "blog_post":{
...            "blog_id":unicode,
...            "content":unicode,
...        },
...    }

now, let’s create the blog object.

>>> class Blog(MongoDocument):
...    db_name = "test"
...    collection_name = "mongokit"
...    structure = {
...        "blog":{"title":unicode}
...    }
...    related_to = {
...      'blog_posts':{'class':BlogPost, 'target':'blog_post.blog_id'},
...    }

The related_to descriptor will tell MongoKit that we want to be able to get all blog post from a blog object. The syntax is :

related_to = {
‘name_of_the_relation’:{
‘class’:RelatedClassObject, ‘target’: ‘field_where_the_id_is’,

}

For instance, let’s create a blog and some blog post:

>>> blog = Blog()
>>> blog['_id'] = u'my blog'
>>> blog['title'] = u'My Blog'
>>> blog.save()
>>> blog.related
>>> blog_post1 = BlogPost()
>>> blog_post1['_id'] = 1
>>> blog_post1['blog_post']['blog_id'] = blog['_id']
>>> blog_post1['blog_post']['content'] = u'a great blog post'
>>> blog_post1.save()
>>> blog_post2 = BlogPost()
>>> blog_post2['_id'] = 2
>>> blog_post2['blog_post']['blog_id'] = blog['_id']
>>> blog_post2['blog_post']['content'] = u'another great blog post'
>>> blog_post2.save()

Now, we want all blog post related to blog:

>>> blog_posts = blog.related.blog_posts()
>>> list(blog_posts)
[{u'blog_post': {u'content': u'a great blog post', u'blog_id': u'my blog'}, u'_id': 1}, {u'blog_post': {u'content': u'another great blog post', u'blog_id': u'my blog'}, u'_id': 2}]

Passing extra query to relation

You can filter all the related document by passing a query to the relation:

>>> blog_posts = blog.related.blog_posts({'blog_post.content':'a great blog post'})
>>> list(blog_posts)
[{u'blog_post': {u'content': u'a great blog post', u'blog_id': u'my blog'}, u'_id': 1}]

Multiple relations

The MongoDocument.related property can handle many relations.

Let’s create another object related to our Blog object:

>>> class Author(MongoDocument):
...     db_name = 'test'
...     collection_name = 'mongokit'
...     structure = {
...         'author':{
...             'name':unicode,
...             'blog_id':unicode,
...         },
...     }

The related property is set at MongoDocument‘s __init__ so we can update the Blog.related_to to handle Author object:

>>> Blog.related_to.update({'authors':{'class':Author, 'target':'author.blog_id'}})

let’s create another blog:

>>> blog = Blog()
>>> author1 = Author()
>>> author1['_id'] = u'me'
>>> author1['author']['name'] = u'Me'
>>> author1['author']['blog_id'] = blog['_id']
>>> author1.save()
>>> author2 = Author()
>>> author2['_id'] = u'you'
>>> author2['author']['name'] = u'You'
>>> author2['author']['blog_id'] = blog['_id']
>>> author2.save()

Now let’s get our blog (remember, it must get another instaciation to update the related_to property) :

>>> blog = Blog.get_from_id('my blog')

blog has now two relations attached to the related property

>>> authors =blog.related.authors()
>>> list(authors)
[{u'_id': u'me', u'author': {u'blog_id': u'my blog', u'name': u'Me'}}, {u'_id': u'you', u'author': {u'blog_id': u'my blog', u'name': u'You'}}]
>>> blog_posts = list(blog.related.blog_posts())
>>> list(blog_posts)
[{u'blog_post': {u'content': u'a great blog post', u'blog_id': u'my blog'}, u'_id': 1}, {u'blog_post': {u'content': u'another great blog post', u'blog_id': u'my blog'}, u'_id': 2}]

Relations with autoref (or how to have full relationed api)

Relations can handle autoref documents. This brings a full featured relational layer to MongoKit.

let’s create a Blog:

>>> class Blog(MongoDocument):
...     db_name = "test"
...     collection_name = "mongokit"
...     structure = {
...         "blog":{"title":unicode}
...     }

and a BlogPost object:

>>> class BlogPost(MongoDocument):
...     db_name = "test"
...    collection_name = "mongokit"
...    structure = {
...        "blog_post":{
...            "blog":Blog,
...            "content":unicode,
...        },
...    }
...    use_autorefs = True

BlogPost is declared after Blog so we have to fill related_to after BlogPost declaration

>>> Blog.related_to = {
...   'blog_posts':{'class':BlogPost, 'target':'blog_post.blog', 'autoref':True}
... }
>>> blog = Blog()
>>> blog['_id'] = u'my blog'
>>> blog['title'] = u'My Blog'
>>> blog.save()
>>> blog_post1 = BlogPost()
>>> blog_post1['_id'] = 1

we pass now the entired blog object:

>>> blog_post1['blog_post']['blog'] = blog
>>> blog_post1['blog_post']['content'] = u'a great blog post'
>>> blog_post1.save()
>>> blog_post2 = BlogPost()
>>> blog_post2['_id'] = 2
>>> blog_post2['blog_post']['blog'] = blog
>>> blog_post2['blog_post']['content'] = u'another great blog post'
>>> blog_post2.save()

Retrive relations is the same:

>>> blog_posts = list(blog.related.blog_posts())
[{u'blog_post': {u'blog': {u'blog': {u'title': None}, u'_id': u'my blog', u'title': u'My Blog'}, u'content': u'a great blog post'}, u'_id': 1}, {u'blog_post': {u'blog': {u'blog': {u'title': None}, u'_id': u'my blog', u'title': u'My Blog'}, u'content': u'another great blog post'}, u'_id': 2}]

Create complexe relations

Some time, structures is complexe and we need to describes the relation deeply. lambda is use to do this.

>>> class Article(MongoDocument):
...     db_name = 'test'
...     collection_name = 'mongokit'
...     structure = {
...         'article': {
...             'title':unicode,
...             'tags':{unicode:int},
...         }
...     }
>>> class Tag(MongoDocument):
...     db_name = 'test'
...     collection_name = 'mongokit'
...     structure = {
...         'tag':{
...             'title':unicode,
...         }
...     }
...     related_to = {'articles':{'class':Article, 'target':lambda x:{'article.tags.%s' % x:{'$gt':1}}}}

the ‘target’ must be a string or a lambda expression. The lambda expression takes a parameter wich represent the object ‘_id’. In this example, the results of the lambda expression with the tag ‘foo’ will be :

{‘article.tags.foo’:{‘$gt’:1}}

this mean that only articles which have the tag ‘foo’ > 1 will be returned.

Now, let’s create some tags :

>>> foo = Tag()
>>> foo['_id'] = u'foo'
>>> foo['tag']['title'] = u'Foo'
>>> foo.save()
>>> bar = Tag()
>>> bar['_id'] = u'bar'
>>> bar['tag']['title'] = u'Bar'
>>> bar.save()

Let’s associate those tags with articles :

>>> article1 = Article()
>>> article1['_id'] = u'article1'
>>> article1['article']['title'] = u'First article'
>>> article1['article']['tags'][u'foo'] = 2
>>> article1['article']['tags'][u'bar'] = 3
>>> article1.save()
>>> article2 = Article()
>>> article2['_id'] = u'article2'
>>> article2['article']['title'] = u'Second article'
>>> article2['article']['tags'][u'foo'] = 0
>>> article2['article']['tags'][u'bar'] = 2
>>> article2.save()

we want all articles which have the tag ‘bar’ > 1:

>>> articles = bar.related.articles()
>>> list(articles)
[{u'article': {u'tags': {u'foo': 2, u'bar': 3}, u'title': u'First article'}, u'_id': u'article1'}, {u'article': {u'tags': {u'foo': 0, u'bar': 2}, u'title': u'Second article'}, u'_id': u'article2'}]

idem with the tag ‘foo’:

>>> articles = foo.related.articles()
>>> list(articles)
[{u'article': {u'tags': {u'foo': 2, u'bar': 3}, u'title': u'First article'}, u'_id': u'article1'}]