Tutorial updates

This commit is contained in:
Ross Lawley 2013-04-23 20:37:05 +00:00
parent 88f96b0838
commit 8a7b619b77
3 changed files with 63 additions and 31 deletions

View File

@ -74,9 +74,13 @@ to point across databases and collections. Below is an example schema, using
Switch Database Context Manager Switch Database Context Manager
=============================== ===============================
Sometimes you might want to switch the database to query against for a class. Sometimes you may want to switch the database to query against for a class,
for example, you archive older data into a separate database for performance
reasons.
The :class:`~mongoengine.context_managers.switch_db` context manager allows The :class:`~mongoengine.context_managers.switch_db` context manager allows
you to change the database alias for a class eg :: you to change the database alias for a given class allowing quick and easy
access to the same User document across databases.eg ::
from mongoengine.context_managers import switch_db from mongoengine.context_managers import switch_db
@ -87,3 +91,6 @@ you to change the database alias for a class eg ::
with switch_db(User, 'archive-user-db') as User: with switch_db(User, 'archive-user-db') as User:
User(name="Ross").save() # Saves the 'archive-user-db' User(name="Ross").save() # Saves the 'archive-user-db'
.. note:: Make sure any aliases have been registered with
:func:`~mongoengine.register_connection` before using the context manager.

View File

@ -7,7 +7,7 @@ MongoDB. To install it, simply run
.. code-block:: console .. code-block:: console
# pip install -U mongoengine $ pip install -U mongoengine
:doc:`tutorial` :doc:`tutorial`
Start here for a quick overview. Start here for a quick overview.

View File

@ -1,6 +1,7 @@
======== ========
Tutorial Tutorial
======== ========
This tutorial introduces **MongoEngine** by means of example --- we will walk This tutorial introduces **MongoEngine** by means of example --- we will walk
through how to create a simple **Tumblelog** application. A Tumblelog is a type through how to create a simple **Tumblelog** application. A Tumblelog is a type
of blog where posts are not constrained to being conventional text-based posts. of blog where posts are not constrained to being conventional text-based posts.
@ -12,23 +13,29 @@ interface.
Getting started Getting started
=============== ===============
Before we start, make sure that a copy of MongoDB is running in an accessible Before we start, make sure that a copy of MongoDB is running in an accessible
location --- running it locally will be easier, but if that is not an option location --- running it locally will be easier, but if that is not an option
then it may be run on a remote server. then it may be run on a remote server. If you haven't installed mongoengine,
simply use pip to install it like so::
$ pip install mongoengine
Before we can start using MongoEngine, we need to tell it how to connect to our Before we can start using MongoEngine, we need to tell it how to connect to our
instance of :program:`mongod`. For this we use the :func:`~mongoengine.connect` instance of :program:`mongod`. For this we use the :func:`~mongoengine.connect`
function. The only argument we need to provide is the name of the MongoDB function. If running locally the only argument we need to provide is the name
database to use:: of the MongoDB database to use::
from mongoengine import * from mongoengine import *
connect('tumblelog') connect('tumblelog')
For more information about connecting to MongoDB see :ref:`guide-connecting`. There are lots of options for connecting to MongoDB, for more information about
them see the :ref:`guide-connecting` guide.
Defining our documents Defining our documents
====================== ======================
MongoDB is *schemaless*, which means that no schema is enforced by the database MongoDB is *schemaless*, which means that no schema is enforced by the database
--- we may add and remove fields however we want and MongoDB won't complain. --- we may add and remove fields however we want and MongoDB won't complain.
This makes life a lot easier in many regards, especially when there is a change This makes life a lot easier in many regards, especially when there is a change
@ -39,17 +46,19 @@ define utility methods on our documents in the same way that traditional
In our Tumblelog application we need to store several different types of In our Tumblelog application we need to store several different types of
information. We will need to have a collection of **users**, so that we may information. We will need to have a collection of **users**, so that we may
link posts to an individual. We also need to store our different types link posts to an individual. We also need to store our different types of
**posts** (text, image and link) in the database. To aid navigation of our **posts** (eg: text, image and link) in the database. To aid navigation of our
Tumblelog, posts may have **tags** associated with them, so that the list of Tumblelog, posts may have **tags** associated with them, so that the list of
posts shown to the user may be limited to posts that have been assigned a posts shown to the user may be limited to posts that have been assigned a
specified tag. Finally, it would be nice if **comments** could be added to specific tag. Finally, it would be nice if **comments** could be added to
posts. We'll start with **users**, as the others are slightly more involved. posts. We'll start with **users**, as the other document models are slightly
more involved.
Users Users
----- -----
Just as if we were using a relational database with an ORM, we need to define Just as if we were using a relational database with an ORM, we need to define
which fields a :class:`User` may have, and what their types will be:: which fields a :class:`User` may have, and what types of data they might store::
class User(Document): class User(Document):
email = StringField(required=True) email = StringField(required=True)
@ -58,11 +67,13 @@ which fields a :class:`User` may have, and what their types will be::
This looks similar to how a the structure of a table would be defined in a This looks similar to how a the structure of a table would be defined in a
regular ORM. The key difference is that this schema will never be passed on to regular ORM. The key difference is that this schema will never be passed on to
MongoDB --- this will only be enforced at the application level. Also, the User MongoDB --- this will only be enforced at the application level, making future
documents will be stored in a MongoDB *collection* rather than a table. changes easy to manage. Also, the User documents will be stored in a
MongoDB *collection* rather than a table.
Posts, Comments and Tags Posts, Comments and Tags
------------------------ ------------------------
Now we'll think about how to store the rest of the information. If we were Now we'll think about how to store the rest of the information. If we were
using a relational database, we would most likely have a table of **posts**, a using a relational database, we would most likely have a table of **posts**, a
table of **comments** and a table of **tags**. To associate the comments with table of **comments** and a table of **tags**. To associate the comments with
@ -75,16 +86,17 @@ of them stand out as particularly intuitive solutions.
Posts Posts
^^^^^ ^^^^^
But MongoDB *isn't* a relational database, so we're not going to do it that
Happily mongoDB *isn't* a relational database, so we're not going to do it that
way. As it turns out, we can use MongoDB's schemaless nature to provide us with way. As it turns out, we can use MongoDB's schemaless nature to provide us with
a much nicer solution. We will store all of the posts in *one collection* --- a much nicer solution. We will store all of the posts in *one collection* and
each post type will just have the fields it needs. If we later want to add each post type will only store the fields it needs. If we later want to add
video posts, we don't have to modify the collection at all, we just *start video posts, we don't have to modify the collection at all, we just *start
using* the new fields we need to support video posts. This fits with the using* the new fields we need to support video posts. This fits with the
Object-Oriented principle of *inheritance* nicely. We can think of Object-Oriented principle of *inheritance* nicely. We can think of
:class:`Post` as a base class, and :class:`TextPost`, :class:`ImagePost` and :class:`Post` as a base class, and :class:`TextPost`, :class:`ImagePost` and
:class:`LinkPost` as subclasses of :class:`Post`. In fact, MongoEngine supports :class:`LinkPost` as subclasses of :class:`Post`. In fact, MongoEngine supports
this kind of modelling out of the box - all you need do is turn on inheritance this kind of modelling out of the box --- all you need do is turn on inheritance
by setting :attr:`allow_inheritance` to True in the :attr:`meta`:: by setting :attr:`allow_inheritance` to True in the :attr:`meta`::
class Post(Document): class Post(Document):
@ -109,6 +121,7 @@ when they are saved, and dereferenced when they are loaded.
Tags Tags
^^^^ ^^^^
Now that we have our Post models figured out, how will we attach tags to them? Now that we have our Post models figured out, how will we attach tags to them?
MongoDB allows us to store lists of items natively, so rather than having a MongoDB allows us to store lists of items natively, so rather than having a
link table, we can just store a list of tags in each post. So, for both link table, we can just store a list of tags in each post. So, for both
@ -126,11 +139,14 @@ size of our database. So let's take a look that the code our modified
The :class:`~mongoengine.ListField` object that is used to define a Post's tags The :class:`~mongoengine.ListField` object that is used to define a Post's tags
takes a field object as its first argument --- this means that you can have takes a field object as its first argument --- this means that you can have
lists of any type of field (including lists). Note that we don't need to lists of any type of field (including lists).
modify the specialised post types as they all inherit from :class:`Post`.
.. note:: We don't need to modify the specialised post types as they all
inherit from :class:`Post`.
Comments Comments
^^^^^^^^ ^^^^^^^^
A comment is typically associated with *one* post. In a relational database, to A comment is typically associated with *one* post. In a relational database, to
display a post with its comments, we would have to retrieve the post from the display a post with its comments, we would have to retrieve the post from the
database, then query the database again for the comments associated with the database, then query the database again for the comments associated with the
@ -181,15 +197,15 @@ Now that we've defined how our documents will be structured, let's start adding
some documents to the database. Firstly, we'll need to create a :class:`User` some documents to the database. Firstly, we'll need to create a :class:`User`
object:: object::
john = User(email='jdoe@example.com', first_name='John', last_name='Doe') ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').save()
john.save()
Note that we could have also defined our user using attribute syntax:: .. note::
We could have also defined our user using attribute syntax::
john = User(email='jdoe@example.com') ross = User(email='ross@example.com')
john.first_name = 'John' ross.first_name = 'Ross'
john.last_name = 'Doe' ross.last_name = 'Lawley'
john.save() ross.save()
Now that we've got our user in the database, let's add a couple of posts:: Now that we've got our user in the database, let's add a couple of posts::
@ -198,16 +214,17 @@ Now that we've got our user in the database, let's add a couple of posts::
post1.tags = ['mongodb', 'mongoengine'] post1.tags = ['mongodb', 'mongoengine']
post1.save() post1.save()
post2 = LinkPost(title='MongoEngine Documentation', author=john) post2 = LinkPost(title='MongoEngine Documentation', author=ross)
post2.link_url = 'http://tractiondigital.com/labs/mongoengine/docs' post2.link_url = 'http://docs.mongoengine.com/'
post2.tags = ['mongoengine'] post2.tags = ['mongoengine']
post2.save() post2.save()
Note that if you change a field on a object that has already been saved, then .. note:: If you change a field on a object that has already been saved, then
call :meth:`save` again, the document will be updated. call :meth:`save` again, the document will be updated.
Accessing our data Accessing our data
================== ==================
So now we've got a couple of posts in our database, how do we display them? So now we've got a couple of posts in our database, how do we display them?
Each document class (i.e. any class that inherits either directly or indirectly Each document class (i.e. any class that inherits either directly or indirectly
from :class:`~mongoengine.Document`) has an :attr:`objects` attribute, which is from :class:`~mongoengine.Document`) has an :attr:`objects` attribute, which is
@ -219,6 +236,7 @@ class. So let's see how we can get our posts' titles::
Retrieving type-specific information Retrieving type-specific information
------------------------------------ ------------------------------------
This will print the titles of our posts, one on each line. But What if we want This will print the titles of our posts, one on each line. But What if we want
to access the type-specific data (link_url, content, etc.)? One way is simply to access the type-specific data (link_url, content, etc.)? One way is simply
to use the :attr:`objects` attribute of a subclass of :class:`Post`:: to use the :attr:`objects` attribute of a subclass of :class:`Post`::
@ -257,6 +275,7 @@ text post, and "Link: <url>" if it was a link post.
Searching our posts by tag Searching our posts by tag
-------------------------- --------------------------
The :attr:`objects` attribute of a :class:`~mongoengine.Document` is actually a The :attr:`objects` attribute of a :class:`~mongoengine.Document` is actually a
:class:`~mongoengine.queryset.QuerySet` object. This lazily queries the :class:`~mongoengine.queryset.QuerySet` object. This lazily queries the
database only when you need the data. It may also be filtered to narrow down database only when you need the data. It may also be filtered to narrow down
@ -275,3 +294,9 @@ used on :class:`~mongoengine.queryset.QuerySet` objects::
num_posts = Post.objects(tags='mongodb').count() num_posts = Post.objects(tags='mongodb').count()
print 'Found %d posts with tag "mongodb"' % num_posts print 'Found %d posts with tag "mongodb"' % num_posts
Learning more about mongoengine
-------------------------------
If you got this far you've made a great start, so well done! The next step on
your mongoengine journey is the `full user guide <guide/index>`_, where you
can learn indepth about how to use mongoengine and mongodb.