Added docs directory, started tutorial
This commit is contained in:
parent
f2d4ffa091
commit
75f737ade8
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
.*.swp
|
.*.swp
|
||||||
|
docs/.build
|
||||||
|
229
docs/.themes/nature/static/nature.css_t
Normal file
229
docs/.themes/nature/static/nature.css_t
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/**
|
||||||
|
* Sphinx stylesheet -- default theme
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import url("basic.css");
|
||||||
|
|
||||||
|
/* -- page layout ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-size: 100%;
|
||||||
|
background-color: #111;
|
||||||
|
color: #555;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.documentwrapper {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bodywrapper {
|
||||||
|
margin: 0 0 0 230px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr{
|
||||||
|
border: 1px solid #B1B4B6;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.document {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #3E4349;
|
||||||
|
padding: 0 30px 30px 30px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.footer {
|
||||||
|
color: #555;
|
||||||
|
width: 100%;
|
||||||
|
padding: 13px 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.footer a {
|
||||||
|
color: #444;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related {
|
||||||
|
background-color: #6BA81E;
|
||||||
|
line-height: 32px;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0px 1px 0 #444;
|
||||||
|
font-size: 0.80em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related a {
|
||||||
|
color: #E2F3CC;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar {
|
||||||
|
font-size: 0.75em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebarwrapper{
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar h3,
|
||||||
|
div.sphinxsidebar h4 {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
color: #222;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: #ddd;
|
||||||
|
text-shadow: 1px 1px 0 white
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar h4{
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar h3 a {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.sphinxsidebar p {
|
||||||
|
color: #888;
|
||||||
|
padding: 5px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar p.topless {
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul {
|
||||||
|
margin: 10px 20px;
|
||||||
|
padding: 0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar a {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar input {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar input[type=text]{
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -- body styles ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #005B81;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #E32E00;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body h1,
|
||||||
|
div.body h2,
|
||||||
|
div.body h3,
|
||||||
|
div.body h4,
|
||||||
|
div.body h5,
|
||||||
|
div.body h6 {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #BED4EB;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #212224;
|
||||||
|
margin: 30px 0px 10px 0px;
|
||||||
|
padding: 5px 0 5px 10px;
|
||||||
|
text-shadow: 0px 1px 0 white
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
|
||||||
|
div.body h2 { font-size: 150%; background-color: #C8D5E3; }
|
||||||
|
div.body h3 { font-size: 120%; background-color: #D8DEE3; }
|
||||||
|
div.body h4 { font-size: 110%; background-color: #D8DEE3; }
|
||||||
|
div.body h5 { font-size: 100%; background-color: #D8DEE3; }
|
||||||
|
div.body h6 { font-size: 100%; background-color: #D8DEE3; }
|
||||||
|
|
||||||
|
a.headerlink {
|
||||||
|
color: #c60f0f;
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 0 4px 0 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.headerlink:hover {
|
||||||
|
background-color: #c60f0f;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.body p, div.body dd, div.body li {
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.admonition p.admonition-title + p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.highlight{
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.note {
|
||||||
|
background-color: #eee;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.seealso {
|
||||||
|
background-color: #ffc;
|
||||||
|
border: 1px solid #ff6;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.warning {
|
||||||
|
background-color: #ffe4e4;
|
||||||
|
border: 1px solid #f66;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.admonition-title {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.admonition-title:after {
|
||||||
|
content: ":";
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: White;
|
||||||
|
color: #222;
|
||||||
|
line-height: 1.2em;
|
||||||
|
border: 1px solid #C6C9CB;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 1.5em 0 1.5em 0;
|
||||||
|
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
|
||||||
|
-moz-box-shadow: 1px 1px 1px #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt {
|
||||||
|
background-color: #ecf0f3;
|
||||||
|
color: #222;
|
||||||
|
padding: 1px 2px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
54
docs/.themes/nature/static/pygments.css
Normal file
54
docs/.themes/nature/static/pygments.css
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
.c { color: #999988; font-style: italic } /* Comment */
|
||||||
|
.k { font-weight: bold } /* Keyword */
|
||||||
|
.o { font-weight: bold } /* Operator */
|
||||||
|
.cm { color: #999988; font-style: italic } /* Comment.Multiline */
|
||||||
|
.cp { color: #999999; font-weight: bold } /* Comment.preproc */
|
||||||
|
.c1 { color: #999988; font-style: italic } /* Comment.Single */
|
||||||
|
.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
|
||||||
|
.ge { font-style: italic } /* Generic.Emph */
|
||||||
|
.gr { color: #aa0000 } /* Generic.Error */
|
||||||
|
.gh { color: #999999 } /* Generic.Heading */
|
||||||
|
.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
|
||||||
|
.go { color: #111 } /* Generic.Output */
|
||||||
|
.gp { color: #555555 } /* Generic.Prompt */
|
||||||
|
.gs { font-weight: bold } /* Generic.Strong */
|
||||||
|
.gu { color: #aaaaaa } /* Generic.Subheading */
|
||||||
|
.gt { color: #aa0000 } /* Generic.Traceback */
|
||||||
|
.kc { font-weight: bold } /* Keyword.Constant */
|
||||||
|
.kd { font-weight: bold } /* Keyword.Declaration */
|
||||||
|
.kp { font-weight: bold } /* Keyword.Pseudo */
|
||||||
|
.kr { font-weight: bold } /* Keyword.Reserved */
|
||||||
|
.kt { color: #445588; font-weight: bold } /* Keyword.Type */
|
||||||
|
.m { color: #009999 } /* Literal.Number */
|
||||||
|
.s { color: #bb8844 } /* Literal.String */
|
||||||
|
.na { color: #008080 } /* Name.Attribute */
|
||||||
|
.nb { color: #999999 } /* Name.Builtin */
|
||||||
|
.nc { color: #445588; font-weight: bold } /* Name.Class */
|
||||||
|
.no { color: #ff99ff } /* Name.Constant */
|
||||||
|
.ni { color: #800080 } /* Name.Entity */
|
||||||
|
.ne { color: #990000; font-weight: bold } /* Name.Exception */
|
||||||
|
.nf { color: #990000; font-weight: bold } /* Name.Function */
|
||||||
|
.nn { color: #555555 } /* Name.Namespace */
|
||||||
|
.nt { color: #000080 } /* Name.Tag */
|
||||||
|
.nv { color: purple } /* Name.Variable */
|
||||||
|
.ow { font-weight: bold } /* Operator.Word */
|
||||||
|
.mf { color: #009999 } /* Literal.Number.Float */
|
||||||
|
.mh { color: #009999 } /* Literal.Number.Hex */
|
||||||
|
.mi { color: #009999 } /* Literal.Number.Integer */
|
||||||
|
.mo { color: #009999 } /* Literal.Number.Oct */
|
||||||
|
.sb { color: #bb8844 } /* Literal.String.Backtick */
|
||||||
|
.sc { color: #bb8844 } /* Literal.String.Char */
|
||||||
|
.sd { color: #bb8844 } /* Literal.String.Doc */
|
||||||
|
.s2 { color: #bb8844 } /* Literal.String.Double */
|
||||||
|
.se { color: #bb8844 } /* Literal.String.Escape */
|
||||||
|
.sh { color: #bb8844 } /* Literal.String.Heredoc */
|
||||||
|
.si { color: #bb8844 } /* Literal.String.Interpol */
|
||||||
|
.sx { color: #bb8844 } /* Literal.String.Other */
|
||||||
|
.sr { color: #808000 } /* Literal.String.Regex */
|
||||||
|
.s1 { color: #bb8844 } /* Literal.String.Single */
|
||||||
|
.ss { color: #bb8844 } /* Literal.String.Symbol */
|
||||||
|
.bp { color: #999999 } /* Name.Builtin.Pseudo */
|
||||||
|
.vc { color: #ff99ff } /* Name.Variable.Class */
|
||||||
|
.vg { color: #ff99ff } /* Name.Variable.Global */
|
||||||
|
.vi { color: #ff99ff } /* Name.Variable.Instance */
|
||||||
|
.il { color: #009999 } /* Literal.Number.Integer.Long */
|
4
docs/.themes/nature/theme.conf
Normal file
4
docs/.themes/nature/theme.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[theme]
|
||||||
|
inherit = basic
|
||||||
|
stylesheet = nature.css
|
||||||
|
pygments_style = tango
|
89
docs/Makefile
Normal file
89
docs/Makefile
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
PAPER =
|
||||||
|
BUILDDIR = .build
|
||||||
|
|
||||||
|
# Internal variables.
|
||||||
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
|
PAPEROPT_letter = -D latex_paper_size=letter
|
||||||
|
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
|
||||||
|
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
|
@echo " html to make standalone HTML files"
|
||||||
|
@echo " dirhtml to make HTML files named index.html in directories"
|
||||||
|
@echo " pickle to make pickle files"
|
||||||
|
@echo " json to make JSON files"
|
||||||
|
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||||
|
@echo " qthelp to make HTML files and a qthelp project"
|
||||||
|
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||||
|
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||||
|
@echo " linkcheck to check all external links for integrity"
|
||||||
|
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -rf $(BUILDDIR)/*
|
||||||
|
|
||||||
|
html:
|
||||||
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||||
|
|
||||||
|
dirhtml:
|
||||||
|
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||||
|
|
||||||
|
pickle:
|
||||||
|
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the pickle files."
|
||||||
|
|
||||||
|
json:
|
||||||
|
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the JSON files."
|
||||||
|
|
||||||
|
htmlhelp:
|
||||||
|
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||||
|
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||||
|
|
||||||
|
qthelp:
|
||||||
|
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||||
|
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||||
|
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MongoEngine.qhcp"
|
||||||
|
@echo "To view the help file:"
|
||||||
|
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MongoEngine.qhc"
|
||||||
|
|
||||||
|
latex:
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||||
|
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||||
|
"run these through (pdf)latex."
|
||||||
|
|
||||||
|
changes:
|
||||||
|
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||||
|
@echo
|
||||||
|
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||||
|
|
||||||
|
linkcheck:
|
||||||
|
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||||
|
@echo
|
||||||
|
@echo "Link check complete; look for any errors in the above output " \
|
||||||
|
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||||
|
|
||||||
|
doctest:
|
||||||
|
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||||
|
@echo "Testing of doctests in the sources finished, look at the " \
|
||||||
|
"results in $(BUILDDIR)/doctest/output.txt."
|
66
docs/code/tumblelog.py
Normal file
66
docs/code/tumblelog.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
from mongoengine import *
|
||||||
|
|
||||||
|
connect('tumblelog')
|
||||||
|
|
||||||
|
class Comment(EmbeddedDocument):
|
||||||
|
content = StringField()
|
||||||
|
name = StringField(max_length=120)
|
||||||
|
|
||||||
|
class User(Document):
|
||||||
|
email = StringField(required=True)
|
||||||
|
first_name = StringField(max_length=50)
|
||||||
|
last_name = StringField(max_length=50)
|
||||||
|
|
||||||
|
class Post(Document):
|
||||||
|
title = StringField(max_length=120, required=True)
|
||||||
|
author = ReferenceField(User)
|
||||||
|
tags = ListField(StringField(max_length=30))
|
||||||
|
comments = ListField(EmbeddedDocumentField(Comment))
|
||||||
|
|
||||||
|
class TextPost(Post):
|
||||||
|
content = StringField()
|
||||||
|
|
||||||
|
class ImagePost(Post):
|
||||||
|
image_path = StringField()
|
||||||
|
|
||||||
|
class LinkPost(Post):
|
||||||
|
link_url = StringField()
|
||||||
|
|
||||||
|
Post.drop_collection()
|
||||||
|
|
||||||
|
john = User(email='jdoe@example.com', first_name='John', last_name='Doe')
|
||||||
|
john.save()
|
||||||
|
|
||||||
|
post1 = TextPost(title='Fun with MongoEngine', author=john)
|
||||||
|
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
|
||||||
|
post1.tags = ['mongodb', 'mongoengine']
|
||||||
|
post1.save()
|
||||||
|
|
||||||
|
post2 = LinkPost(title='MongoEngine Documentation', author=john)
|
||||||
|
post2.link_url = 'http://tractiondigital.com/labs/mongoengine/docs'
|
||||||
|
post2.tags = ['mongoengine']
|
||||||
|
post2.save()
|
||||||
|
|
||||||
|
print 'ALL POSTS'
|
||||||
|
print
|
||||||
|
for post in Post.objects:
|
||||||
|
print post.title
|
||||||
|
print '=' * len(post.title)
|
||||||
|
|
||||||
|
if isinstance(post, TextPost):
|
||||||
|
print post.content
|
||||||
|
|
||||||
|
if isinstance(post, LinkPost):
|
||||||
|
print 'Link:', post.link_url
|
||||||
|
|
||||||
|
print
|
||||||
|
print
|
||||||
|
|
||||||
|
print 'POSTS TAGGED \'MONGODB\''
|
||||||
|
print
|
||||||
|
for post in Post.objects(tags='mongodb'):
|
||||||
|
print post.title
|
||||||
|
print
|
||||||
|
|
||||||
|
num_posts = Post.objects(tags='mongodb').count()
|
||||||
|
print 'Found %d posts with tag "mongodb"' % num_posts
|
194
docs/conf.py
Normal file
194
docs/conf.py
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# MongoEngine documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Sun Nov 22 18:14:13 2009.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#sys.path.append(os.path.abspath('.'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = []
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['.templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'MongoEngine'
|
||||||
|
copyright = u'2009, Harry Marr'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.1'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.1'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of documents that shouldn't be included in the build.
|
||||||
|
#unused_docs = []
|
||||||
|
|
||||||
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
|
# for source files.
|
||||||
|
exclude_trees = ['.build']
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
|
html_theme = 'nature'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
html_theme_path = ['.themes']
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['.static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_use_modindex = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
#html_show_sourcelink = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = ''
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'MongoEnginedoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
# The paper size ('letter' or 'a4').
|
||||||
|
latex_paper_size = 'a4'
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#latex_font_size = '10pt'
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'MongoEngine.tex', u'MongoEngine Documentation',
|
||||||
|
u'Harry Marr', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#latex_preamble = ''
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_use_modindex = True
|
22
docs/index.rst
Normal file
22
docs/index.rst
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.. MongoEngine documentation master file, created by
|
||||||
|
sphinx-quickstart on Sun Nov 22 18:14:13 2009.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to MongoEngine's documentation!
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
tutorial.rst
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
|
|
259
docs/tutorial.rst
Normal file
259
docs/tutorial.rst
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
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.
|
||||||
|
As well as text-based entries, users may post images, links, videos, etc. For
|
||||||
|
simplicity's sake, we'll stick to text, image and link entries in our
|
||||||
|
application. As the purpose of this tutorial is to introduce MongoEngine, we'll
|
||||||
|
focus on the data-modelling side of the application, leaving out a user
|
||||||
|
interface.
|
||||||
|
|
||||||
|
Connecting to MongoDB
|
||||||
|
---------------------
|
||||||
|
Before we start, you should make sure that you have a copy of MongoDB 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.
|
||||||
|
|
||||||
|
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`
|
||||||
|
function. The only argument we need to provide is the name of the MongoDB
|
||||||
|
database to use::
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
|
||||||
|
connect('tumblelog')
|
||||||
|
|
||||||
|
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
|
||||||
|
port explicitly::
|
||||||
|
|
||||||
|
connect('tumblelog', host='192.168.1.35', port=12345)
|
||||||
|
|
||||||
|
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 it comes to
|
||||||
|
migrations. However, defining schemata for our documents can help to iron out
|
||||||
|
bugs involving incorrect types or missing fields, and also allow us to define
|
||||||
|
utility methods on our documents in the same way that traditional :abbr:`ORMs
|
||||||
|
(Object-Relational Mappers)` do.
|
||||||
|
|
||||||
|
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. For 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 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.
|
||||||
|
|
||||||
|
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::
|
||||||
|
|
||||||
|
class User(Document):
|
||||||
|
email = StringField(required=True)
|
||||||
|
first_name = StringField(max_length=50)
|
||||||
|
last_name = StringField(max_length=50)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Posts, Comments and Tags
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Well that wasn't too bad, was it? 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 individual posts, we would put a
|
||||||
|
column in the comments table that contained a foreign key to the posts table.
|
||||||
|
We'd also need a link table to provide the many-to-many relationship between
|
||||||
|
posts and tags. Then we'd need to address the problem of storing the
|
||||||
|
specialised post-types (text, image and link). There are several ways we can
|
||||||
|
achieve this, but each of them have their problems --- none 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
|
||||||
|
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
|
||||||
|
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 an 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::
|
||||||
|
|
||||||
|
class Post(Document):
|
||||||
|
title = StringField(max_length=120, required=True)
|
||||||
|
author = ReferenceField(User)
|
||||||
|
|
||||||
|
class TextPost(Post):
|
||||||
|
content = StringField()
|
||||||
|
|
||||||
|
class ImagePost(Post):
|
||||||
|
image_path = StringField()
|
||||||
|
|
||||||
|
class LinkPost(Post):
|
||||||
|
link_url = StringField()
|
||||||
|
|
||||||
|
We are storing a reference to the author of the posts using a
|
||||||
|
:class:`mongoengine.ReferenceField` object. These are similar to foreign key
|
||||||
|
fields in traditional ORMs, and are automatically translated into references
|
||||||
|
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. Also, for both
|
||||||
|
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
|
||||||
|
collection. Especially as tags are generally very short (often even shorted
|
||||||
|
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
|
||||||
|
:class:`Post` class::
|
||||||
|
|
||||||
|
class Post(Document):
|
||||||
|
title = StringField(max_length=120, required=True)
|
||||||
|
author = ReferenceField(User)
|
||||||
|
tags = ListField(StringField(max_length=30))
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
relational model. Using MongoDB we can store the comments as a list of
|
||||||
|
*embedded documents* directly on the post document. An embedded document should
|
||||||
|
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
|
||||||
|
documents, along with utility methods, in exactly the same way we do with
|
||||||
|
regular documents::
|
||||||
|
|
||||||
|
class Comment(EmbeddedDocument):
|
||||||
|
content = StringField()
|
||||||
|
name = StringField(max_length=120)
|
||||||
|
|
||||||
|
We can then store a list of comment documents in our post document::
|
||||||
|
|
||||||
|
class Post(Document):
|
||||||
|
title = StringField(max_length=120, required=True)
|
||||||
|
author = ReferenceField(User)
|
||||||
|
tags = ListField(StringField(max_length=30))
|
||||||
|
comments = ListField(EmbeddedDocumentField(Comment))
|
||||||
|
|
||||||
|
Adding data to our Tumblelog
|
||||||
|
----------------------------
|
||||||
|
Now that we've defined how our documents will be structured, lets 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()
|
||||||
|
|
||||||
|
Simple, eh? Note that only fields with ``required=True`` need to be specified
|
||||||
|
in the constructor, 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()
|
||||||
|
|
||||||
|
Now that we've got our user in the database, lets add a couple of posts::
|
||||||
|
|
||||||
|
post1 = TextPost(title='Fun with MongoEngine', author=john)
|
||||||
|
post1.content = 'Took a look at MongoEngine today, looks pretty cool.'
|
||||||
|
post1.tags = ['mongodb', 'mongoengine']
|
||||||
|
post1.save()
|
||||||
|
|
||||||
|
post2 = LinkPost(title='MongoEngine Documentation', author=john)
|
||||||
|
post2.link_url = 'http://tractiondigital.com/labs/mongoengine/docs'
|
||||||
|
post2.tags = ['mongoengine']
|
||||||
|
post2.save()
|
||||||
|
|
||||||
|
Note that 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
|
||||||
|
used to access the documents in the database associated with that class. So
|
||||||
|
lets see how we can get our posts' titles::
|
||||||
|
|
||||||
|
for post in Post.objects:
|
||||||
|
print post.title
|
||||||
|
|
||||||
|
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`::
|
||||||
|
|
||||||
|
for post in TextPost.objects:
|
||||||
|
print post.content
|
||||||
|
|
||||||
|
Using TextPost's :attr:`objects` attribute only returns documents that were
|
||||||
|
created using :class:`TextPost`. Actually, there is a more general rule here:
|
||||||
|
the :attr:`objects` attribute of any subclass of :class:`mongoengine.Document`
|
||||||
|
only looks for documents that were created using that subclass or one of its
|
||||||
|
subclasses.
|
||||||
|
|
||||||
|
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
|
||||||
|
better way than just using each of the subclasses individually. When we used
|
||||||
|
:class:`Post`'s :attr:`objects` attribute earlier, the objects being returned
|
||||||
|
weren't actually instances of :class:`Post` --- they were instances of the
|
||||||
|
subclass of :class:`Post` that matches the post's type. Lets look at how this
|
||||||
|
works in practice::
|
||||||
|
|
||||||
|
for post in Post.objects:
|
||||||
|
print post.title
|
||||||
|
print '=' * len(post.title)
|
||||||
|
|
||||||
|
if isinstance(post, TextPost):
|
||||||
|
print post.content
|
||||||
|
|
||||||
|
if isinstance(post, LinkPost):
|
||||||
|
print 'Link:', post.link_url
|
||||||
|
|
||||||
|
print
|
||||||
|
|
||||||
|
This would print the title of each post, followed by the content if it was a
|
||||||
|
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` object. This lazily queries the database only
|
||||||
|
when you need the data. It may also be filtered to narrow down your query. Lets
|
||||||
|
adjust our query so that only posts with the tag "mongodb" are returned::
|
||||||
|
|
||||||
|
for post in Post.objects(tags='mongodb'):
|
||||||
|
print post.title
|
||||||
|
|
||||||
|
There are also methods available on :class:`mongoengine.QuerySet` objects that
|
||||||
|
allow different results to be returned, for example, calling :meth:`first` on
|
||||||
|
the :attr:`objects` attribute will return a single document, the first matched
|
||||||
|
by the query you provide. Aggregation functions may also be used on
|
||||||
|
:class:`mongoengine.QuerySet` objects::
|
||||||
|
|
||||||
|
num_posts = Post.objects(tags='mongodb').count()
|
||||||
|
print 'Found % posts with tag "mongodb"' % num_posts
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user