Updated tutorial, added tutorial link to readme
This commit is contained in:
parent
1529fd901d
commit
c3ca3bd97c
@ -2,5 +2,7 @@ MongoEngine
|
|||||||
===========
|
===========
|
||||||
MongoEngine is an ORM-like layer on top of PyMongo.
|
MongoEngine is an ORM-like layer on top of PyMongo.
|
||||||
|
|
||||||
|
Tutorial available at http://hmarr.com/mongoengine/
|
||||||
|
|
||||||
**Warning:** this software is still in development and should *not* be used
|
**Warning:** this software is still in development and should *not* be used
|
||||||
in production.
|
in production.
|
||||||
|
@ -11,9 +11,9 @@ interface.
|
|||||||
|
|
||||||
Connecting to MongoDB
|
Connecting to MongoDB
|
||||||
---------------------
|
---------------------
|
||||||
Before we start, you should make sure that you have a copy of MongoDB running
|
Before we start, make sure that a copy of MongoDB is running in an accessible
|
||||||
in an accessible location --- running it locally will be easier, but if that is
|
location --- running it locally will be easier, but if that is not an option
|
||||||
not an option then it may be run on a remote server.
|
then it may be run on a remote server.
|
||||||
|
|
||||||
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 **mongod**. For this we use the :func:`mongoengine.connect`
|
instance of **mongod**. For this we use the :func:`mongoengine.connect`
|
||||||
@ -25,8 +25,8 @@ database to use::
|
|||||||
connect('tumblelog')
|
connect('tumblelog')
|
||||||
|
|
||||||
This will connect to a mongod instance running locally on the default port. To
|
This will connect to a mongod instance running locally on the default port. To
|
||||||
connect to a mongod instance running elsewhere, we may specify the host and
|
connect to a mongod instance running elsewhere, specify the host and port
|
||||||
port explicitly::
|
explicitly::
|
||||||
|
|
||||||
connect('tumblelog', host='192.168.1.35', port=12345)
|
connect('tumblelog', host='192.168.1.35', port=12345)
|
||||||
|
|
||||||
@ -34,20 +34,20 @@ 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 it comes to
|
This makes life a lot easier in many regards, especially when there is a change
|
||||||
migrations. However, defining schemata for our documents can help to iron out
|
to the data model. However, defining schemata for our documents can help to
|
||||||
bugs involving incorrect types or missing fields, and also allow us to define
|
iron out bugs involving incorrect types or missing fields, and also allow us to
|
||||||
utility methods on our documents in the same way that traditional :abbr:`ORMs
|
define utility methods on our documents in the same way that traditional
|
||||||
(Object-Relational Mappers)` do.
|
:abbr:`ORMs (Object-Relational Mappers)` do.
|
||||||
|
|
||||||
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
|
||||||
**posts** (text, image and link) in the database. For to aid navigation of our
|
**posts** (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 a specified tag.
|
posts shown to the user may be limited to posts that have been assigned a
|
||||||
Finally, it would be nice if **comments** could be added to posts. We'll start
|
specified tag. Finally, it would be nice if **comments** could be added to
|
||||||
with **users**, as the others are slightly more involved.
|
posts. We'll start with **users**, as the others are slightly more involved.
|
||||||
|
|
||||||
Users
|
Users
|
||||||
^^^^^
|
^^^^^
|
||||||
@ -66,16 +66,15 @@ documents will be stored in a MongoDB *collection* rather than a table.
|
|||||||
|
|
||||||
Posts, Comments and Tags
|
Posts, Comments and Tags
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Well that wasn't too bad, was it? Now we'll think about how to store the rest
|
Now we'll think about how to store the rest of the information. If we were
|
||||||
of the information. If we were using a relational database, we would most
|
using a relational database, we would most likely have a table of **posts**, a
|
||||||
likely have a table of **posts**, a table of **comments** and a table of
|
table of **comments** and a table of **tags**. To associate the comments with
|
||||||
**tags**. To associate the comments with individual posts, we would put a
|
individual posts, we would put a column in the comments table that contained a
|
||||||
column in the comments table that contained a foreign key to the posts table.
|
foreign key to the posts table. We'd also need a link table to provide the
|
||||||
We'd also need a link table to provide the many-to-many relationship between
|
many-to-many relationship between posts and tags. Then we'd need to address the
|
||||||
posts and tags. Then we'd need to address the problem of storing the
|
problem of storing the specialised post-types (text, image and link). There are
|
||||||
specialised post-types (text, image and link). There are several ways we can
|
several ways we can achieve this, but each of them have their problems --- none
|
||||||
achieve this, but each of them have their problems --- none of them stand out
|
of them stand out as particularly intuitive solutions.
|
||||||
as particularly intuitive solutions.
|
|
||||||
|
|
||||||
Posts
|
Posts
|
||||||
"""""
|
"""""
|
||||||
@ -86,7 +85,7 @@ each post type will just have 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 an 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::
|
this kind of modelling out of the box::
|
||||||
|
|
||||||
@ -112,10 +111,10 @@ 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. Also, for both
|
link table, we can just store a list of tags in each post. So, for both
|
||||||
efficiency and simplicity's sake, we'll store the tags as strings directly
|
efficiency and simplicity's sake, we'll store the tags as strings directly
|
||||||
within the post, rather than storing references to tags in a separate
|
within the post, rather than storing references to tags in a separate
|
||||||
collection. Especially as tags are generally very short (often even shorted
|
collection. Especially as tags are generally very short (often even shorter
|
||||||
than a document's id), this denormalisation won't impact very strongly on the
|
than a document's id), this denormalisation won't impact very strongly on the
|
||||||
size of our database. So let's take a look that the code our modified
|
size of our database. So let's take a look that the code our modified
|
||||||
:class:`Post` class::
|
:class:`Post` class::
|
||||||
@ -138,11 +137,11 @@ database, then query the database again for the comments associated with the
|
|||||||
post. This works, but there is no real reason to be storing the comments
|
post. This works, but there is no real reason to be storing the comments
|
||||||
separately from their associated posts, other than to work around the
|
separately from their associated posts, other than to work around the
|
||||||
relational model. Using MongoDB we can store the comments as a list of
|
relational model. Using MongoDB we can store the comments as a list of
|
||||||
*embedded documents* directly on the post document. An embedded document should
|
*embedded documents* directly on a post document. An embedded document should
|
||||||
be treated no differently that a regular document; it just doesn't have its own
|
be treated no differently that a regular document; it just doesn't have its own
|
||||||
collection. Using MongoEngine, we can define the structure of embedded
|
collection in the database. Using MongoEngine, we can define the structure of
|
||||||
documents, along with utility methods, in exactly the same way we do with
|
embedded documents, along with utility methods, in exactly the same way we do
|
||||||
regular documents::
|
with regular documents::
|
||||||
|
|
||||||
class Comment(EmbeddedDocument):
|
class Comment(EmbeddedDocument):
|
||||||
content = StringField()
|
content = StringField()
|
||||||
@ -158,23 +157,22 @@ We can then store a list of comment documents in our post document::
|
|||||||
|
|
||||||
Adding data to our Tumblelog
|
Adding data to our Tumblelog
|
||||||
----------------------------
|
----------------------------
|
||||||
Now that we've defined how our documents will be structured, lets start adding
|
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')
|
john = User(email='jdoe@example.com', first_name='John', last_name='Doe')
|
||||||
john.save()
|
john.save()
|
||||||
|
|
||||||
Simple, eh? Note that only fields with ``required=True`` need to be specified
|
Note that only fields with ``required=True`` need to be specified in the
|
||||||
in the constructor, we could have also defined our user using attribute
|
constructor, we could have also defined our user using attribute syntax::
|
||||||
syntax::
|
|
||||||
|
|
||||||
john = User(email='jdoe@example.com')
|
john = User(email='jdoe@example.com')
|
||||||
john.first_name = 'John'
|
john.first_name = 'John'
|
||||||
john.last_name = 'Doe'
|
john.last_name = 'Doe'
|
||||||
john.save()
|
john.save()
|
||||||
|
|
||||||
Now that we've got our user in the database, lets add a couple of posts::
|
Now that we've got our user in the database, let's add a couple of posts::
|
||||||
|
|
||||||
post1 = TextPost(title='Fun with MongoEngine', author=john)
|
post1 = TextPost(title='Fun with MongoEngine', author=john)
|
||||||
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
|
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
|
||||||
@ -194,8 +192,8 @@ 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
|
||||||
used to access the documents in the database associated with that class. So
|
used to access the documents in the database collection associated with that
|
||||||
lets see how we can get our posts' titles::
|
class. So let's see how we can get our posts' titles::
|
||||||
|
|
||||||
for post in Post.objects:
|
for post in Post.objects:
|
||||||
print post.title
|
print post.title
|
||||||
@ -216,12 +214,12 @@ only looks for documents that were created using that subclass or one of its
|
|||||||
subclasses.
|
subclasses.
|
||||||
|
|
||||||
So how would we display all of our posts, showing only the information that
|
So how would we display all of our posts, showing only the information that
|
||||||
corresponds to each post's specific type? As you might have guessed, there is a
|
corresponds to each post's specific type? There is a better way than just using
|
||||||
better way than just using each of the subclasses individually. When we used
|
each of the subclasses individually. When we used :class:`Post`'s
|
||||||
:class:`Post`'s :attr:`objects` attribute earlier, the objects being returned
|
:attr:`objects` attribute earlier, the objects being returned weren't actually
|
||||||
weren't actually instances of :class:`Post` --- they were instances of the
|
instances of :class:`Post` --- they were instances of the subclass of
|
||||||
subclass of :class:`Post` that matches the post's type. Lets look at how this
|
:class:`Post` that matches the post's type. Let's look at how this works in
|
||||||
works in practice::
|
practice::
|
||||||
|
|
||||||
for post in Post.objects:
|
for post in Post.objects:
|
||||||
print post.title
|
print post.title
|
||||||
@ -242,8 +240,8 @@ 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` object. This lazily queries the database only
|
:class:`mongoengine.QuerySet` object. This lazily queries the database only
|
||||||
when you need the data. It may also be filtered to narrow down your query. Lets
|
when you need the data. It may also be filtered to narrow down your query.
|
||||||
adjust our query so that only posts with the tag "mongodb" are returned::
|
Let's adjust our query so that only posts with the tag "mongodb" are returned::
|
||||||
|
|
||||||
for post in Post.objects(tags='mongodb'):
|
for post in Post.objects(tags='mongodb'):
|
||||||
print post.title
|
print post.title
|
||||||
|
Loading…
x
Reference in New Issue
Block a user