Tutorial updates
This commit is contained in:
parent
88f96b0838
commit
8a7b619b77
@ -74,9 +74,13 @@ to point across databases and collections. Below is an example schema, using
|
||||
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
|
||||
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
|
||||
|
||||
@ -87,3 +91,6 @@ you to change the database alias for a class eg ::
|
||||
|
||||
with switch_db(User, 'archive-user-db') as User:
|
||||
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.
|
||||
|
@ -7,7 +7,7 @@ MongoDB. To install it, simply run
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# pip install -U mongoengine
|
||||
$ pip install -U mongoengine
|
||||
|
||||
:doc:`tutorial`
|
||||
Start here for a quick overview.
|
||||
|
@ -1,6 +1,7 @@
|
||||
========
|
||||
Tutorial
|
||||
========
|
||||
|
||||
This tutorial introduces **MongoEngine** by means of example --- we will walk
|
||||
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.
|
||||
@ -12,23 +13,29 @@ interface.
|
||||
|
||||
Getting started
|
||||
===============
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
database to use::
|
||||
function. If running locally the only argument we need to provide is the name
|
||||
of the MongoDB database to use::
|
||||
|
||||
from mongoengine import *
|
||||
|
||||
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
|
||||
======================
|
||||
|
||||
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.
|
||||
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
|
||||
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
|
||||
**posts** (text, image and link) in the database. To aid navigation of our
|
||||
link posts to an individual. We also need to store our different types of
|
||||
**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
|
||||
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
|
||||
posts. We'll start with **users**, as the others are slightly more involved.
|
||||
specific tag. Finally, it would be nice if **comments** could be added to
|
||||
posts. We'll start with **users**, as the other document models are slightly
|
||||
more involved.
|
||||
|
||||
Users
|
||||
-----
|
||||
|
||||
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):
|
||||
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
|
||||
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
|
||||
documents will be stored in a MongoDB *collection* rather than a table.
|
||||
MongoDB --- this will only be enforced at the application level, making future
|
||||
changes easy to manage. Also, the User documents will be stored in a
|
||||
MongoDB *collection* rather than a table.
|
||||
|
||||
Posts, Comments and Tags
|
||||
------------------------
|
||||
|
||||
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
|
||||
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
|
||||
^^^^^
|
||||
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
|
||||
a much nicer solution. We will store all of the posts in *one collection* ---
|
||||
each post type will just have the fields it needs. If we later want to add
|
||||
a much nicer solution. We will store all of the posts in *one collection* and
|
||||
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
|
||||
using* the new fields we need to support video posts. This fits with the
|
||||
Object-Oriented principle of *inheritance* nicely. We can think of
|
||||
:class:`Post` as a base class, and :class:`TextPost`, :class:`ImagePost` and
|
||||
: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`::
|
||||
|
||||
class Post(Document):
|
||||
@ -109,6 +121,7 @@ when they are saved, and dereferenced when they are loaded.
|
||||
|
||||
Tags
|
||||
^^^^
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
modify the specialised post types as they all inherit from :class:`Post`.
|
||||
lists of any type of field (including lists).
|
||||
|
||||
.. note:: We don't need to modify the specialised post types as they all
|
||||
inherit from :class:`Post`.
|
||||
|
||||
Comments
|
||||
^^^^^^^^
|
||||
|
||||
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
|
||||
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`
|
||||
object::
|
||||
|
||||
john = User(email='jdoe@example.com', first_name='John', last_name='Doe')
|
||||
john.save()
|
||||
ross = User(email='ross@example.com', first_name='Ross', last_name='Lawley').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')
|
||||
john.first_name = 'John'
|
||||
john.last_name = 'Doe'
|
||||
john.save()
|
||||
ross = User(email='ross@example.com')
|
||||
ross.first_name = 'Ross'
|
||||
ross.last_name = 'Lawley'
|
||||
ross.save()
|
||||
|
||||
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.save()
|
||||
|
||||
post2 = LinkPost(title='MongoEngine Documentation', author=john)
|
||||
post2.link_url = 'http://tractiondigital.com/labs/mongoengine/docs'
|
||||
post2 = LinkPost(title='MongoEngine Documentation', author=ross)
|
||||
post2.link_url = 'http://docs.mongoengine.com/'
|
||||
post2.tags = ['mongoengine']
|
||||
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.
|
||||
|
||||
Accessing our data
|
||||
==================
|
||||
|
||||
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
|
||||
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
|
||||
------------------------------------
|
||||
|
||||
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 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
|
||||
--------------------------
|
||||
|
||||
The :attr:`objects` attribute of a :class:`~mongoengine.Document` is actually a
|
||||
:class:`~mongoengine.queryset.QuerySet` object. This lazily queries the
|
||||
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()
|
||||
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.
|
Loading…
x
Reference in New Issue
Block a user