├── .coveragerc ├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── _static │ └── flask-mongoalchemy.png ├── _themes │ ├── .gitignore │ ├── LICENSE │ ├── README │ ├── flask │ │ ├── layout.html │ │ ├── relations.html │ │ ├── static │ │ │ ├── flasky.css_t │ │ │ └── small_flask.css │ │ └── theme.conf │ ├── flask_small │ │ ├── layout.html │ │ ├── static │ │ │ └── flasky.css_t │ │ └── theme.conf │ └── flask_theme_support.py ├── conf.py └── index.rst ├── examples ├── README.rst ├── books_collection │ ├── README.rst │ ├── collection │ │ ├── __init__.py │ │ ├── documents.py │ │ ├── forms.py │ │ ├── templates │ │ │ ├── base.html │ │ │ └── books │ │ │ │ ├── edit.html │ │ │ │ ├── list.html │ │ │ │ ├── list_all.html │ │ │ │ ├── list_filtered.html │ │ │ │ └── new.html │ │ └── views.py │ ├── requirements.txt │ ├── run.py │ └── test_collection.py └── library │ └── library.py ├── flask_mongoalchemy ├── __init__.py └── meta.py ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── helpers.py ├── test_document.py ├── test_meta.py ├── test_mongoalchemy_object.py ├── test_mongodb_uri.py ├── test_pagination.py └── test_query.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source=flask_mongoalchemy 3 | omit=examples 4 | 5 | [html] 6 | directory=cover 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | .coverage 4 | cover 5 | build 6 | docs/_build 7 | dist 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 2.7 4 | install: 5 | - make bootstrap 6 | script: 7 | - make test 8 | services: 9 | - mongodb 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of flask-mongoalchemy authors for copyright purposes. 2 | Aditya Mukerjee 3 | Artur Rodrigues 4 | Francisco Souza 5 | Jake Schmitz 6 | Jesaja Everling 7 | Leonardo da Mata 8 | Marco Pesenti Gritti 9 | Matt Hartstonge 10 | Misja Hoebe 11 | Tres Bailey 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2014, flask-mongoalchemy authors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 15 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 18 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | include LICENSE 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: bootstrap develop test coverage 2 | 3 | release: zip_docs 4 | python setup.py sdist upload 5 | 6 | zip_docs: documentation 7 | cd docs/_build/html && zip -r docs.zip * 8 | 9 | bootstrap: 10 | @pip install -qr requirements.txt 11 | 12 | test: bootstrap clean 13 | @coverage run -m unittest discover 14 | @coverage report 15 | 16 | coverage: 17 | coverage html 18 | 19 | documentation: clean 20 | cd docs && make html 21 | 22 | clean: 23 | @git clean -qdfX 24 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Flask-MongoAlchemy 2 | ================== 3 | 4 | .. image:: https://travis-ci.org/cobrateam/flask-mongoalchemy.png?branch=master 5 | :target: https://travis-ci.org/cobrateam/flask-mongoalchemy 6 | 7 | `Flask `_ support for `MongoDB `_ using `MongoAlchemy `_. 8 | 9 | Documentation 10 | +++++++++++++ 11 | 12 | If you want to get started, check the `example sourcecode `_ out. 13 | 14 | For full documentation, see the online docs at: ``_. 15 | 16 | Development 17 | +++++++++++ 18 | 19 | * Source hosted at `Github `_ 20 | * Report issues on `Github Issues `_ 21 | 22 | Bootstraping the development environment 23 | ---------------------------------------- 24 | 25 | If you are using a virtualenv, bootstrap your development environment by running: 26 | 27 | :: 28 | 29 | $ make bootstrap 30 | 31 | Running tests 32 | ------------- 33 | 34 | With all dependencies installed (after bootstrap development env), just run: 35 | 36 | :: 37 | 38 | $ make test 39 | 40 | Community 41 | --------- 42 | 43 | #cobrateam on chanel irc.freenode.net 44 | 45 | Changelog 46 | +++++++++ 47 | 48 | Flask-MongoAlchemy 0.7.2 49 | ------------------------ 50 | 51 | * Pin pymongo version to ensure driver compatibility. 52 | 53 | Flask-MongoAlchemy 0.7.1 54 | ------------------------ 55 | 56 | * Support for specifying the full connection string, via the 57 | MONGOALCHEMY_CONNECTION_STRING configuration value. 58 | 59 | Flask-MongoAlchemy 0.7.0 60 | ------------------------ 61 | 62 | * Multiple database support (thanks Misja Hoebe) 63 | 64 | Flask-MongoAlchemy 0.6.1 65 | ------------------------ 66 | 67 | * Replica set support, via the MONGOALCHEMY_REPLICA_SET configuration value. 68 | 69 | Flask-MongoAlchemy 0.6.0 70 | ------------------------ 71 | 72 | * Use the not-so-new extension scheme for Flask, users now should import the 73 | extension using the flask.ext metapackage 74 | * Some fixes in the docs, regarding other extensions usage in examples 75 | 76 | Flask-MongoAlchemy 0.5.4 77 | ------------------------ 78 | 79 | * [bugfix] fix compatibility with pymongo 2.2 80 | 81 | Flask-MongoAlchemy 0.5.3 82 | ------------------------ 83 | 84 | * [bugfix] fixed a bug on setup.py 85 | 86 | Flask-MongoAlchemy 0.5.2 87 | ------------------------ 88 | 89 | * added a configuration flag for user authentication based either on database or server 90 | * [bugfix] fixed server based authentication 91 | 92 | Flask-MongoAlchemy 0.5.1 93 | ------------------------ 94 | 95 | * [bugfix] fixed the subpackage structure 96 | 97 | Flask-MongoAlchemy 0.5 98 | ---------------------- 99 | 100 | * Support for multiple MongoDB sessions 101 | 102 | Flask-MongoAlchemy 0.4.3 103 | ------------------------ 104 | 105 | * [bugfix] added database to MongoDB URI for authenticated connectinos 106 | 107 | Flask-MongoAlchemy 0.4.2 108 | ------------------------ 109 | 110 | * Fixed pymongo dependency in setup.py 111 | 112 | Flask-MongoAlchemy 0.4.1 113 | ------------------------ 114 | 115 | * MongoAlchemy 0.9 as dependency 116 | * [bugfix] safe session operations on connect, save and remove 117 | 118 | Flask-MongoAlchemy 0.4 119 | ---------------------- 120 | 121 | * Documentation improvements 122 | * Support for safe or unsafe sessions and operations 123 | 124 | Flask-MongoAlchemy 0.3.3 125 | ------------------------ 126 | 127 | * Fixed dependencies on ``setup.py`` 128 | 129 | Flask-MongoAlchemy 0.3.2 130 | ------------------------ 131 | 132 | * Compatibility with Flask 0.7 133 | 134 | Flask-MongoAlchemy 0.3.1 135 | ------------------------ 136 | 137 | * [bugfix] method ``get`` on ``Query`` objects was never returning the object 138 | 139 | Flask-MongoAlchemy 0.3 140 | ---------------------- 141 | 142 | * Introduced update queries support 143 | 144 | Flask-MongoAlchemy 0.2 145 | ---------------------- 146 | 147 | * Reverse compatibility broken on ``Document`` class. The ``get()`` method was moved to ``BaseQuery`` class. 148 | Here the old code, on version ``0.1``: :: 149 | 150 | >>> Document.get(mongo_id) 151 | 152 | And the new code, on version ``0.2``: :: 153 | 154 | >>> Document.query.get(mongo_id) 155 | 156 | * Added ``get_or_404``, ``first_or_404`` and ``paginate`` methods on ``BaseQuery`` class. Check the `documentation `_ to know how to use them :) 157 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FlaskMongoAlchemy.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FlaskMongoAlchemy.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/FlaskMongoAlchemy" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/FlaskMongoAlchemy" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/_static/flask-mongoalchemy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cobrateam/flask-mongoalchemy/66ab6f857cae69e35d37035880c1dfaf1dc9bd15/docs/_static/flask-mongoalchemy.png -------------------------------------------------------------------------------- /docs/_themes/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /docs/_themes/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 by Armin Ronacher. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms of the theme, with or 6 | without modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | We kindly ask you to only use these themes in an unmodified manner just 22 | for Flask and Flask-related products, not for unrelated projects. If you 23 | like the visual style and want to use it for your own projects, please 24 | consider making some larger changes to the themes (such as changing 25 | font faces, sizes, colors or margins). 26 | 27 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 28 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 31 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE 37 | POSSIBILITY OF SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /docs/_themes/README: -------------------------------------------------------------------------------- 1 | Flask Sphinx Styles 2 | =================== 3 | 4 | This repository contains sphinx styles for Flask and Flask related 5 | projects. To use this style in your Sphinx documentation, follow 6 | this guide: 7 | 8 | 1. put this folder as _themes into your docs folder. Alternatively 9 | you can also use git submodules to check out the contents there. 10 | 2. add this to your conf.py: 11 | 12 | sys.path.append(os.path.abspath('_themes')) 13 | html_theme_path = ['_themes'] 14 | html_theme = 'flask' 15 | 16 | The following themes exist: 17 | 18 | - 'flask' - the standard flask documentation theme for large 19 | projects 20 | - 'flask_small' - small one-page theme. Intended to be used by 21 | very small addon libraries for flask. 22 | 23 | The following options exist for the flask_small theme: 24 | 25 | [options] 26 | index_logo = '' filename of a picture in _static 27 | to be used as replacement for the 28 | h1 in the index.rst file. 29 | index_logo_height = 120px height of the index logo 30 | github_fork = '' repository name on github for the 31 | "fork me" badge 32 | -------------------------------------------------------------------------------- /docs/_themes/flask/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {% if theme_touch_icon %} 5 | 6 | {% endif %} 7 | 9 | {% endblock %} 10 | {%- block relbar2 %}{% endblock %} 11 | {%- block footer %} 12 | 16 | {%- endblock %} 17 | -------------------------------------------------------------------------------- /docs/_themes/flask/relations.html: -------------------------------------------------------------------------------- 1 |

Related Topics

2 | 20 | -------------------------------------------------------------------------------- /docs/_themes/flask/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | {% set page_width = '940px' %} 10 | {% set sidebar_width = '220px' %} 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | background-color: white; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | width: {{ page_width }}; 27 | margin: 30px auto 0 auto; 28 | } 29 | 30 | div.documentwrapper { 31 | float: left; 32 | width: 100%; 33 | } 34 | 35 | div.bodywrapper { 36 | margin: 0 0 0 {{ sidebar_width }}; 37 | } 38 | 39 | div.sphinxsidebar { 40 | width: {{ sidebar_width }}; 41 | } 42 | 43 | hr { 44 | border: 1px solid #B1B4B6; 45 | } 46 | 47 | div.body { 48 | background-color: #ffffff; 49 | color: #3E4349; 50 | padding: 0 30px 0 30px; 51 | } 52 | 53 | img.floatingflask { 54 | padding: 0 0 10px 10px; 55 | float: right; 56 | } 57 | 58 | div.footer { 59 | width: {{ page_width }}; 60 | margin: 20px auto 30px auto; 61 | font-size: 14px; 62 | color: #888; 63 | text-align: right; 64 | } 65 | 66 | div.footer a { 67 | color: #888; 68 | } 69 | 70 | div.related { 71 | display: none; 72 | } 73 | 74 | div.sphinxsidebar a { 75 | color: #444; 76 | text-decoration: none; 77 | border-bottom: 1px dotted #999; 78 | } 79 | 80 | div.sphinxsidebar a:hover { 81 | border-bottom: 1px solid #999; 82 | } 83 | 84 | div.sphinxsidebar { 85 | font-size: 14px; 86 | line-height: 1.5; 87 | } 88 | 89 | div.sphinxsidebarwrapper { 90 | padding: 18px 10px; 91 | } 92 | 93 | div.sphinxsidebarwrapper p.logo { 94 | padding: 0 0 20px 0; 95 | margin: 0; 96 | text-align: center; 97 | } 98 | 99 | div.sphinxsidebar h3, 100 | div.sphinxsidebar h4 { 101 | font-family: 'Garamond', 'Georgia', serif; 102 | color: #444; 103 | font-size: 24px; 104 | font-weight: normal; 105 | margin: 0 0 5px 0; 106 | padding: 0; 107 | } 108 | 109 | div.sphinxsidebar h4 { 110 | font-size: 20px; 111 | } 112 | 113 | div.sphinxsidebar h3 a { 114 | color: #444; 115 | } 116 | 117 | div.sphinxsidebar p.logo a, 118 | div.sphinxsidebar h3 a, 119 | div.sphinxsidebar p.logo a:hover, 120 | div.sphinxsidebar h3 a:hover { 121 | border: none; 122 | } 123 | 124 | div.sphinxsidebar p { 125 | color: #555; 126 | margin: 10px 0; 127 | } 128 | 129 | div.sphinxsidebar ul { 130 | margin: 10px 0; 131 | padding: 0; 132 | color: #000; 133 | } 134 | 135 | div.sphinxsidebar input { 136 | border: 1px solid #ccc; 137 | font-family: 'Georgia', serif; 138 | font-size: 1em; 139 | } 140 | 141 | /* -- body styles ----------------------------------------------------------- */ 142 | 143 | a { 144 | color: #004B6B; 145 | text-decoration: underline; 146 | } 147 | 148 | a:hover { 149 | color: #6D4100; 150 | text-decoration: underline; 151 | } 152 | 153 | div.body h1, 154 | div.body h2, 155 | div.body h3, 156 | div.body h4, 157 | div.body h5, 158 | div.body h6 { 159 | font-family: 'Garamond', 'Georgia', serif; 160 | font-weight: normal; 161 | margin: 30px 0px 10px 0px; 162 | padding: 0; 163 | } 164 | 165 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 166 | div.body h2 { font-size: 180%; } 167 | div.body h3 { font-size: 150%; } 168 | div.body h4 { font-size: 130%; } 169 | div.body h5 { font-size: 100%; } 170 | div.body h6 { font-size: 100%; } 171 | 172 | a.headerlink { 173 | color: #ddd; 174 | padding: 0 4px; 175 | text-decoration: none; 176 | } 177 | 178 | a.headerlink:hover { 179 | color: #444; 180 | background: #eaeaea; 181 | } 182 | 183 | div.body p, div.body dd, div.body li { 184 | line-height: 1.4em; 185 | } 186 | 187 | div.admonition { 188 | background: #fafafa; 189 | margin: 20px -30px; 190 | padding: 10px 30px; 191 | border-top: 1px solid #ccc; 192 | border-bottom: 1px solid #ccc; 193 | } 194 | 195 | div.admonition tt.xref, div.admonition a tt { 196 | border-bottom: 1px solid #fafafa; 197 | } 198 | 199 | dd div.admonition { 200 | margin-left: -60px; 201 | padding-left: 60px; 202 | } 203 | 204 | div.admonition p.admonition-title { 205 | font-family: 'Garamond', 'Georgia', serif; 206 | font-weight: normal; 207 | font-size: 24px; 208 | margin: 0 0 10px 0; 209 | padding: 0; 210 | line-height: 1; 211 | } 212 | 213 | div.admonition p.last { 214 | margin-bottom: 0; 215 | } 216 | 217 | div.highlight { 218 | background-color: white; 219 | } 220 | 221 | dt:target, .highlight { 222 | background: #FAF3E8; 223 | } 224 | 225 | div.note { 226 | background-color: #eee; 227 | border: 1px solid #ccc; 228 | } 229 | 230 | div.seealso { 231 | background-color: #ffc; 232 | border: 1px solid #ff6; 233 | } 234 | 235 | div.topic { 236 | background-color: #eee; 237 | } 238 | 239 | p.admonition-title { 240 | display: inline; 241 | } 242 | 243 | p.admonition-title:after { 244 | content: ":"; 245 | } 246 | 247 | pre, tt { 248 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 249 | font-size: 0.9em; 250 | } 251 | 252 | img.screenshot { 253 | } 254 | 255 | tt.descname, tt.descclassname { 256 | font-size: 0.95em; 257 | } 258 | 259 | tt.descname { 260 | padding-right: 0.08em; 261 | } 262 | 263 | img.screenshot { 264 | -moz-box-shadow: 2px 2px 4px #eee; 265 | -webkit-box-shadow: 2px 2px 4px #eee; 266 | box-shadow: 2px 2px 4px #eee; 267 | } 268 | 269 | table.docutils { 270 | border: 1px solid #888; 271 | -moz-box-shadow: 2px 2px 4px #eee; 272 | -webkit-box-shadow: 2px 2px 4px #eee; 273 | box-shadow: 2px 2px 4px #eee; 274 | } 275 | 276 | table.docutils td, table.docutils th { 277 | border: 1px solid #888; 278 | padding: 0.25em 0.7em; 279 | } 280 | 281 | table.field-list, table.footnote { 282 | border: none; 283 | -moz-box-shadow: none; 284 | -webkit-box-shadow: none; 285 | box-shadow: none; 286 | } 287 | 288 | table.footnote { 289 | margin: 15px 0; 290 | width: 100%; 291 | border: 1px solid #eee; 292 | background: #fdfdfd; 293 | font-size: 0.9em; 294 | } 295 | 296 | table.footnote + table.footnote { 297 | margin-top: -15px; 298 | border-top: none; 299 | } 300 | 301 | table.field-list th { 302 | padding: 0 0.8em 0 0; 303 | } 304 | 305 | table.field-list td { 306 | padding: 0; 307 | } 308 | 309 | table.footnote td.label { 310 | width: 0px; 311 | padding: 0.3em 0 0.3em 0.5em; 312 | } 313 | 314 | table.footnote td { 315 | padding: 0.3em 0.5em; 316 | } 317 | 318 | dl { 319 | margin: 0; 320 | padding: 0; 321 | } 322 | 323 | dl dd { 324 | margin-left: 30px; 325 | } 326 | 327 | blockquote { 328 | margin: 0 0 0 30px; 329 | padding: 0; 330 | } 331 | 332 | ul, ol { 333 | margin: 10px 0 10px 30px; 334 | padding: 0; 335 | } 336 | 337 | pre { 338 | background: #eee; 339 | padding: 7px 30px; 340 | margin: 15px -30px; 341 | line-height: 1.3em; 342 | } 343 | 344 | dl pre, blockquote pre, li pre { 345 | margin-left: -60px; 346 | padding-left: 60px; 347 | } 348 | 349 | dl dl pre { 350 | margin-left: -90px; 351 | padding-left: 90px; 352 | } 353 | 354 | tt { 355 | background-color: #ecf0f3; 356 | color: #222; 357 | /* padding: 1px 2px; */ 358 | } 359 | 360 | tt.xref, a tt { 361 | background-color: #FBFBFB; 362 | border-bottom: 1px solid white; 363 | } 364 | 365 | a.reference { 366 | text-decoration: none; 367 | border-bottom: 1px dotted #004B6B; 368 | } 369 | 370 | a.reference:hover { 371 | border-bottom: 1px solid #6D4100; 372 | } 373 | 374 | a.footnote-reference { 375 | text-decoration: none; 376 | font-size: 0.7em; 377 | vertical-align: top; 378 | border-bottom: 1px dotted #004B6B; 379 | } 380 | 381 | a.footnote-reference:hover { 382 | border-bottom: 1px solid #6D4100; 383 | } 384 | 385 | a:hover tt { 386 | background: #EEE; 387 | } 388 | -------------------------------------------------------------------------------- /docs/_themes/flask/static/small_flask.css: -------------------------------------------------------------------------------- 1 | /* 2 | * small_flask.css_t 3 | * ~~~~~~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | body { 10 | margin: 0; 11 | padding: 20px 30px; 12 | } 13 | 14 | div.documentwrapper { 15 | float: none; 16 | background: white; 17 | } 18 | 19 | div.sphinxsidebar { 20 | display: block; 21 | float: none; 22 | width: 102.5%; 23 | margin: 50px -30px -20px -30px; 24 | padding: 10px 20px; 25 | background: #333; 26 | color: white; 27 | } 28 | 29 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 30 | div.sphinxsidebar h3 a { 31 | color: white; 32 | } 33 | 34 | div.sphinxsidebar a { 35 | color: #aaa; 36 | } 37 | 38 | div.sphinxsidebar p.logo { 39 | display: none; 40 | } 41 | 42 | div.document { 43 | width: 100%; 44 | margin: 0; 45 | } 46 | 47 | div.related { 48 | display: block; 49 | margin: 0; 50 | padding: 10px 0 20px 0; 51 | } 52 | 53 | div.related ul, 54 | div.related ul li { 55 | margin: 0; 56 | padding: 0; 57 | } 58 | 59 | div.footer { 60 | display: none; 61 | } 62 | 63 | div.bodywrapper { 64 | margin: 0; 65 | } 66 | 67 | div.body { 68 | min-height: 0; 69 | padding: 0; 70 | } 71 | -------------------------------------------------------------------------------- /docs/_themes/flask/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | pygments_style = flask_theme_support.FlaskyStyle 5 | 6 | [options] 7 | touch_icon = 8 | -------------------------------------------------------------------------------- /docs/_themes/flask_small/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% block header %} 3 | {{ super() }} 4 | {% if pagename == 'index' %} 5 |
6 | {% endif %} 7 | {% endblock %} 8 | {% block footer %} 9 | {% if pagename == 'index' %} 10 |
11 | {% endif %} 12 | {% endblock %} 13 | {# do not display relbars #} 14 | {% block relbar1 %}{% endblock %} 15 | {% block relbar2 %} 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {% endblock %} 21 | {% block sidebar1 %}{% endblock %} 22 | {% block sidebar2 %}{% endblock %} 23 | -------------------------------------------------------------------------------- /docs/_themes/flask_small/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- flasky theme based on nature theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | color: #000; 20 | background: white; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 40px auto 0 auto; 32 | width: 700px; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #ffffff; 41 | color: #3E4349; 42 | padding: 0 30px 30px 30px; 43 | } 44 | 45 | img.floatingflask { 46 | padding: 0 0 10px 10px; 47 | float: right; 48 | } 49 | 50 | div.footer { 51 | text-align: right; 52 | color: #888; 53 | padding: 10px; 54 | font-size: 14px; 55 | width: 650px; 56 | margin: 0 auto 40px auto; 57 | } 58 | 59 | div.footer a { 60 | color: #888; 61 | text-decoration: underline; 62 | } 63 | 64 | div.related { 65 | line-height: 32px; 66 | color: #888; 67 | } 68 | 69 | div.related ul { 70 | padding: 0 0 0 10px; 71 | } 72 | 73 | div.related a { 74 | color: #444; 75 | } 76 | 77 | /* -- body styles ----------------------------------------------------------- */ 78 | 79 | a { 80 | color: #004B6B; 81 | text-decoration: underline; 82 | } 83 | 84 | a:hover { 85 | color: #6D4100; 86 | text-decoration: underline; 87 | } 88 | 89 | div.body { 90 | padding-bottom: 40px; /* saved for footer */ 91 | } 92 | 93 | div.body h1, 94 | div.body h2, 95 | div.body h3, 96 | div.body h4, 97 | div.body h5, 98 | div.body h6 { 99 | font-family: 'Garamond', 'Georgia', serif; 100 | font-weight: normal; 101 | margin: 30px 0px 10px 0px; 102 | padding: 0; 103 | } 104 | 105 | {% if theme_index_logo %} 106 | div.indexwrapper h1 { 107 | text-indent: -999999px; 108 | background: url({{ theme_index_logo }}) no-repeat center center; 109 | height: {{ theme_index_logo_height }}; 110 | } 111 | {% endif %} 112 | 113 | div.body h2 { font-size: 180%; } 114 | div.body h3 { font-size: 150%; } 115 | div.body h4 { font-size: 130%; } 116 | div.body h5 { font-size: 100%; } 117 | div.body h6 { font-size: 100%; } 118 | 119 | a.headerlink { 120 | color: white; 121 | padding: 0 4px; 122 | text-decoration: none; 123 | } 124 | 125 | a.headerlink:hover { 126 | color: #444; 127 | background: #eaeaea; 128 | } 129 | 130 | div.body p, div.body dd, div.body li { 131 | line-height: 1.4em; 132 | } 133 | 134 | div.admonition { 135 | background: #fafafa; 136 | margin: 20px -30px; 137 | padding: 10px 30px; 138 | border-top: 1px solid #ccc; 139 | border-bottom: 1px solid #ccc; 140 | } 141 | 142 | div.admonition p.admonition-title { 143 | font-family: 'Garamond', 'Georgia', serif; 144 | font-weight: normal; 145 | font-size: 24px; 146 | margin: 0 0 10px 0; 147 | padding: 0; 148 | line-height: 1; 149 | } 150 | 151 | div.admonition p.last { 152 | margin-bottom: 0; 153 | } 154 | 155 | div.highlight{ 156 | background-color: white; 157 | } 158 | 159 | dt:target, .highlight { 160 | background: #FAF3E8; 161 | } 162 | 163 | div.note { 164 | background-color: #eee; 165 | border: 1px solid #ccc; 166 | } 167 | 168 | div.seealso { 169 | background-color: #ffc; 170 | border: 1px solid #ff6; 171 | } 172 | 173 | div.topic { 174 | background-color: #eee; 175 | } 176 | 177 | div.warning { 178 | background-color: #ffe4e4; 179 | border: 1px solid #f66; 180 | } 181 | 182 | p.admonition-title { 183 | display: inline; 184 | } 185 | 186 | p.admonition-title:after { 187 | content: ":"; 188 | } 189 | 190 | pre, tt { 191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 192 | font-size: 0.85em; 193 | } 194 | 195 | img.screenshot { 196 | } 197 | 198 | tt.descname, tt.descclassname { 199 | font-size: 0.95em; 200 | } 201 | 202 | tt.descname { 203 | padding-right: 0.08em; 204 | } 205 | 206 | img.screenshot { 207 | -moz-box-shadow: 2px 2px 4px #eee; 208 | -webkit-box-shadow: 2px 2px 4px #eee; 209 | box-shadow: 2px 2px 4px #eee; 210 | } 211 | 212 | table.docutils { 213 | border: 1px solid #888; 214 | -moz-box-shadow: 2px 2px 4px #eee; 215 | -webkit-box-shadow: 2px 2px 4px #eee; 216 | box-shadow: 2px 2px 4px #eee; 217 | } 218 | 219 | table.docutils td, table.docutils th { 220 | border: 1px solid #888; 221 | padding: 0.25em 0.7em; 222 | } 223 | 224 | table.field-list, table.footnote { 225 | border: none; 226 | -moz-box-shadow: none; 227 | -webkit-box-shadow: none; 228 | box-shadow: none; 229 | } 230 | 231 | table.footnote { 232 | margin: 15px 0; 233 | width: 100%; 234 | border: 1px solid #eee; 235 | } 236 | 237 | table.field-list th { 238 | padding: 0 0.8em 0 0; 239 | } 240 | 241 | table.field-list td { 242 | padding: 0; 243 | } 244 | 245 | table.footnote td { 246 | padding: 0.5em; 247 | } 248 | 249 | dl { 250 | margin: 0; 251 | padding: 0; 252 | } 253 | 254 | dl dd { 255 | margin-left: 30px; 256 | } 257 | 258 | pre { 259 | padding: 0; 260 | margin: 15px -30px; 261 | padding: 8px; 262 | line-height: 1.3em; 263 | padding: 7px 30px; 264 | background: #eee; 265 | border-radius: 2px; 266 | -moz-border-radius: 2px; 267 | -webkit-border-radius: 2px; 268 | } 269 | 270 | dl pre { 271 | margin-left: -60px; 272 | padding-left: 60px; 273 | } 274 | 275 | tt { 276 | background-color: #ecf0f3; 277 | color: #222; 278 | /* padding: 1px 2px; */ 279 | } 280 | 281 | tt.xref, a tt { 282 | background-color: #FBFBFB; 283 | } 284 | 285 | a:hover tt { 286 | background: #EEE; 287 | } 288 | -------------------------------------------------------------------------------- /docs/_themes/flask_small/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | nosidebar = true 5 | pygments_style = flask_theme_support.FlaskyStyle 6 | 7 | [options] 8 | index_logo = '' 9 | index_logo_height = 120px 10 | github_fork = '' 11 | -------------------------------------------------------------------------------- /docs/_themes/flask_theme_support.py: -------------------------------------------------------------------------------- 1 | # flasky extensions. flasky pygments style based on tango style 2 | from pygments.style import Style 3 | from pygments.token import (Keyword, Name, Comment, String, Error, 4 | Number, Operator, Generic, Whitespace, 5 | Punctuation, Other, Literal) 6 | 7 | 8 | class FlaskyStyle(Style): 9 | background_color = "#f8f8f8" 10 | default_style = "" 11 | 12 | styles = { 13 | # No corresponding class for the following: 14 | # Text: "", # class: '' 15 | Whitespace: "underline #f8f8f8", # class: 'w' 16 | Error: "#a40000 border:#ef2929", # class: 'err' 17 | Other: "#000000", # class 'x' 18 | 19 | Comment: "italic #8f5902", # class: 'c' 20 | Comment.Preproc: "noitalic", # class: 'cp' 21 | 22 | Keyword: "bold #004461", # class: 'k' 23 | Keyword.Constant: "bold #004461", # class: 'kc' 24 | Keyword.Declaration: "bold #004461", # class: 'kd' 25 | Keyword.Namespace: "bold #004461", # class: 'kn' 26 | Keyword.Pseudo: "bold #004461", # class: 'kp' 27 | Keyword.Reserved: "bold #004461", # class: 'kr' 28 | Keyword.Type: "bold #004461", # class: 'kt' 29 | 30 | Operator: "#582800", # class: 'o' 31 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 32 | 33 | Punctuation: "bold #000000", # class: 'p' 34 | 35 | # because special names such as Name.Class, Name.Function, etc. 36 | # are not recognized as such later in the parsing, we choose them 37 | # to look the same as ordinary variables. 38 | Name: "#000000", # class: 'n' 39 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 40 | Name.Builtin: "#004461", # class: 'nb' 41 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 42 | Name.Class: "#000000", # class: 'nc' - to be revised 43 | Name.Constant: "#000000", # class: 'no' - to be revised 44 | Name.Decorator: "#888", # class: 'nd' - to be revised 45 | Name.Entity: "#ce5c00", # class: 'ni' 46 | Name.Exception: "bold #cc0000", # class: 'ne' 47 | Name.Function: "#000000", # class: 'nf' 48 | Name.Property: "#000000", # class: 'py' 49 | Name.Label: "#f57900", # class: 'nl' 50 | Name.Namespace: "#000000", # class: 'nn' - to be revised 51 | Name.Other: "#000000", # class: 'nx' 52 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 53 | Name.Variable: "#000000", # class: 'nv' - to be revised 54 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 55 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 56 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 57 | 58 | Number: "#990000", # class: 'm' 59 | 60 | Literal: "#000000", # class: 'l' 61 | Literal.Date: "#000000", # class: 'ld' 62 | 63 | String: "#4e9a06", # class: 's' 64 | String.Backtick: "#4e9a06", # class: 'sb' 65 | String.Char: "#4e9a06", # class: 'sc' 66 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 67 | String.Double: "#4e9a06", # class: 's2' 68 | String.Escape: "#4e9a06", # class: 'se' 69 | String.Heredoc: "#4e9a06", # class: 'sh' 70 | String.Interpol: "#4e9a06", # class: 'si' 71 | String.Other: "#4e9a06", # class: 'sx' 72 | String.Regex: "#4e9a06", # class: 'sr' 73 | String.Single: "#4e9a06", # class: 's1' 74 | String.Symbol: "#4e9a06", # class: 'ss' 75 | 76 | Generic: "#000000", # class: 'g' 77 | Generic.Deleted: "#a40000", # class: 'gd' 78 | Generic.Emph: "italic #000000", # class: 'ge' 79 | Generic.Error: "#ef2929", # class: 'gr' 80 | Generic.Heading: "bold #000080", # class: 'gh' 81 | Generic.Inserted: "#00A000", # class: 'gi' 82 | Generic.Output: "#888", # class: 'go' 83 | Generic.Prompt: "#745334", # class: 'gp' 84 | Generic.Strong: "bold #000000", # class: 'gs' 85 | Generic.Subheading: "bold #800080", # class: 'gu' 86 | Generic.Traceback: "bold #a40000", # class: 'gt' 87 | } 88 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Flask MongoAlchemy documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Nov 20 17:56:16 2010. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import os 15 | import sys 16 | 17 | sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) 18 | sys.path.append(os.path.abspath('_themes')) 19 | 20 | # If extensions (or modules to document with autodoc) are in another directory, 21 | # add these directories to sys.path here. If the directory is relative to the 22 | # documentation root, use os.path.abspath to make it absolute, like shown here. 23 | # sys.path.insert(0, os.path.abspath('.')) 24 | 25 | # -- General configuration ----------------------------------------------------- 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be extensions 31 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 32 | extensions = ['sphinx.ext.autodoc'] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix of source filenames. 38 | source_suffix = '.rst' 39 | 40 | # The encoding of source files. 41 | # source_encoding = 'utf-8-sig' 42 | 43 | # The master toctree document. 44 | master_doc = 'index' 45 | 46 | # General information about the project. 47 | project = u'Flask MongoAlchemy' 48 | copyright = u'2015, Francisco Souza' 49 | 50 | # The version info for the project you're documenting, acts as replacement for 51 | # |version| and |release|, also used in various other places throughout the 52 | # built documents. 53 | # 54 | # The short X.Y version. 55 | version = '0.7' 56 | # The full version, including alpha/beta/rc tags. 57 | release = '0.7.2' 58 | 59 | # The language for content autogenerated by Sphinx. Refer to documentation 60 | # for a list of supported languages. 61 | # language = None 62 | 63 | # There are two options for replacing |today|: either, you set today to some 64 | # non-false value, then it is used: 65 | # today = '' 66 | # Else, today_fmt is used as the format for a strftime call. 67 | # today_fmt = '%B %d, %Y' 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | exclude_patterns = ['_build'] 72 | 73 | # The reST default role (used for this markup: `text`) to use for all documents. 74 | # default_role = None 75 | 76 | # If true, '()' will be appended to :func: etc. cross-reference text. 77 | # add_function_parentheses = True 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | # add_module_names = True 82 | 83 | # If true, sectionauthor and moduleauthor directives will be shown in the 84 | # output. They are ignored by default. 85 | # show_authors = False 86 | 87 | # The name of the Pygments (syntax highlighting) style to use. 88 | # pygments_style = 'sphinx' 89 | 90 | # A list of ignored prefixes for module index sorting. 91 | # modindex_common_prefix = [] 92 | 93 | 94 | # -- Options for HTML output --------------------------------------------------- 95 | 96 | # The theme to use for HTML and HTML Help pages. See the documentation for 97 | # a list of builtin themes. 98 | html_theme = 'flask_small' 99 | 100 | # Theme options are theme-specific and customize the look and feel of a theme 101 | # further. For a list of options available for each theme, see the 102 | # documentation. 103 | html_theme_options = { 104 | 'index_logo': 'flask-mongoalchemy.png', 105 | 'github_fork': 'cobrateam/flask-mongoalchemy' 106 | } 107 | 108 | # Add any paths that contain custom themes here, relative to this directory. 109 | html_theme_path = ['_themes'] 110 | 111 | # The name for this set of Sphinx documents. If None, it defaults to 112 | # " v documentation". 113 | # html_title = None 114 | 115 | # A shorter title for the navigation bar. Default is the same as html_title. 116 | # html_short_title = None 117 | 118 | # The name of an image file (relative to this directory) to place at the top 119 | # of the sidebar. 120 | # html_logo = None 121 | 122 | # The name of an image file (within the static path) to use as favicon of the 123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 124 | # pixels large. 125 | # html_favicon = None 126 | 127 | # Add any paths that contain custom static files (such as style sheets) here, 128 | # relative to this directory. They are copied after the builtin static files, 129 | # so a file named "default.css" will overwrite the builtin "default.css". 130 | html_static_path = ['_static'] 131 | 132 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 133 | # using the given strftime format. 134 | # html_last_updated_fmt = '%b %d, %Y' 135 | 136 | # If true, SmartyPants will be used to convert quotes and dashes to 137 | # typographically correct entities. 138 | # html_use_smartypants = True 139 | 140 | # Custom sidebar templates, maps document names to template names. 141 | # html_sidebars = {} 142 | 143 | # Additional templates that should be rendered to pages, maps page names to 144 | # template names. 145 | # html_additional_pages = {} 146 | 147 | # If false, no module index is generated. 148 | # html_domain_indices = True 149 | 150 | # If false, no index is generated. 151 | # html_use_index = True 152 | 153 | # If true, the index is split into individual pages for each letter. 154 | # html_split_index = False 155 | 156 | # If true, links to the reST sources are added to the pages. 157 | # html_show_sourcelink = True 158 | 159 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 160 | # html_show_sphinx = True 161 | 162 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 163 | # html_show_copyright = True 164 | 165 | # If true, an OpenSearch description file will be output, and all pages will 166 | # contain a tag referring to it. The value of this option must be the 167 | # base URL from which the finished HTML is served. 168 | # html_use_opensearch = '' 169 | 170 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 171 | # html_file_suffix = None 172 | 173 | # Output file base name for HTML help builder. 174 | htmlhelp_basename = 'FlaskMongoAlchemydoc' 175 | 176 | 177 | # -- Options for LaTeX output -------------------------------------------------- 178 | 179 | # The paper size ('letter' or 'a4'). 180 | # latex_paper_size = 'letter' 181 | 182 | # The font size ('10pt', '11pt' or '12pt'). 183 | # latex_font_size = '10pt' 184 | 185 | # Grouping the document tree into LaTeX files. List of tuples 186 | # (source start file, target name, title, author, documentclass [howto/manual]). 187 | latex_documents = [ 188 | ('index', 'FlaskMongoAlchemy.tex', u'Flask MongoAlchemy Documentation', 189 | u'Francisco Souza', 'manual'), 190 | ] 191 | 192 | # The name of an image file (relative to this directory) to place at the top of 193 | # the title page. 194 | # latex_logo = None 195 | 196 | # For "manual" documents, if this is true, then toplevel headings are parts, 197 | # not chapters. 198 | # latex_use_parts = False 199 | 200 | # If true, show page references after internal links. 201 | # latex_show_pagerefs = False 202 | 203 | # If true, show URL addresses after external links. 204 | # latex_show_urls = False 205 | 206 | # Additional stuff for the LaTeX preamble. 207 | # latex_preamble = '' 208 | 209 | # Documents to append as an appendix to all manuals. 210 | # latex_appendices = [] 211 | 212 | # If false, no module index is generated. 213 | # latex_domain_indices = True 214 | 215 | 216 | # -- Options for manual page output -------------------------------------------- 217 | 218 | # One entry per manual page. List of tuples 219 | # (source start file, name, description, authors, manual section). 220 | man_pages = [ 221 | ('index', 'flaskmongoalchemy', u'Flask MongoAlchemy Documentation', 222 | [u'Francisco Souza'], 1) 223 | ] 224 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Flask MongoAlchemy documentation master file, created by 2 | sphinx-quickstart on Sat Nov 20 17:56:16 2010. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Flask MongoAlchemy's documentation! 7 | ============================================== 8 | 9 | .. module:: flask.ext.mongoalchemy 10 | 11 | Flask-MongoAlchemy adds support for `MongoDB`_ on `Flask`_ using `MongoAlchemy`_. Source code and issue tracking are available at `Github`_. If you want to get started, check out the `example source code `_. 12 | 13 | Installation 14 | ------------ 15 | 16 | You can easily install using `pip`: 17 | 18 | :: 19 | 20 | $ [sudo] pip install Flask-MongoAlchemy 21 | 22 | If you prefer, you may use the latest source version by cloning the following git repository: 23 | 24 | :: 25 | 26 | $ git clone https://github.com/cobrateam/flask-mongoalchemy.git 27 | $ cd flask-mongoalchemy 28 | $ [sudo] python setup.py develop 29 | 30 | Make sure you have MongoDB installed to use it. 31 | 32 | Usage 33 | ----- 34 | 35 | It is very easy and fun to use Flask-MongoAlchemy to proxy between Python and MongoDB. 36 | 37 | All you have to do is create a MongoAlchemy object and use it to declare documents. Here is a complete example: 38 | 39 | :: 40 | 41 | from flask import Flask 42 | from flask.ext.mongoalchemy import MongoAlchemy 43 | app = Flask(__name__) 44 | app.config['MONGOALCHEMY_DATABASE'] = 'library' 45 | db = MongoAlchemy(app) 46 | 47 | class Author(db.Document): 48 | name = db.StringField() 49 | 50 | class Book(db.Document): 51 | title = db.StringField() 52 | author = db.DocumentField(Author) 53 | year = db.IntField() 54 | 55 | As you can see, extending the :class:`Document` is all you need to create a document. 56 | 57 | Now you can create authors and books: 58 | 59 | :: 60 | 61 | >>> from application import Author, Book 62 | >>> mark_pilgrim = Author(name='Mark Pilgrim') 63 | >>> dive = Book(title='Dive Into Python', author=mark_pilgrim, year=2004) 64 | 65 | And save them: 66 | 67 | :: 68 | 69 | >>> mark_pilgrim.save() 70 | >>> dive.save() 71 | 72 | If you make any changes on a document, you may call :meth:`~Document.save` again: 73 | 74 | :: 75 | 76 | >>> mark_pilgrim.name = 'Mark Stalone' 77 | >>> mark_pilgrim.save() 78 | 79 | And you can remove a document from the database by calling its :meth:`~Document.remove` method: 80 | 81 | :: 82 | 83 | >>> dive.remove() 84 | 85 | Another basic operation is querying for documents. Every document has a ``query`` class property. It's very simple to use it: 86 | 87 | :: 88 | 89 | >>> mark = Author.query.get('76726') 90 | >>> mark.name = 'Mark Pilgrim' 91 | >>> mark.save() 92 | 93 | You also can use the ``filter`` method instead of the :meth:`~BaseQuery.get` method: 94 | 95 | :: 96 | 97 | >>> mark = Author.query.filter(Author.name == 'Mark Pilgrim').first() 98 | >>> mark.name = 'Steve Jobs' 99 | >>> mark.save() 100 | 101 | Do you want to go further? Dive deep into the `API`_ docs. 102 | 103 | Using authenticated connections 104 | ------------------------------- 105 | 106 | It's possible to use authentication to connect to a MongoDB server. The authentication can be server based or database based. 107 | 108 | The default behavior is to use server based authentication, to use database based authentication, you need to turn off the config 109 | value ``MONGOALCHEMY_SERVER_AUTH`` (see the next section for more detail on configuration values): 110 | 111 | :: 112 | 113 | >>> app.config['MONGOALCHEMY_SERVER_AUTH'] = False 114 | 115 | Configuration values 116 | -------------------- 117 | 118 | The following configuration values are present in Flask-MongoAlchemy: 119 | 120 | .. tabularcolumns:: |p{6.5cm}|p{8.5cm}| 121 | 122 | ==================================== ================================================== 123 | ``MONGOALCHEMY_DATABASE`` The database name that should be used for 124 | the connection. 125 | 126 | ``MONGOALCHEMY_SERVER`` The MongoDB server. 127 | 128 | *Default value:* ``localhost`` 129 | 130 | ``MONGOALCHEMY_PORT`` Listening port of the MongoDB server. 131 | 132 | *Default value:* ``27017`` 133 | 134 | ``MONGOALCHEMY_USER`` User for database connection. 135 | 136 | *Default value:* ``None`` 137 | 138 | ``MONGOALCHEMY_PASSWORD`` Password for database connection. 139 | 140 | *Default value:* ``None`` 141 | 142 | ``MONGOALCHEMY_SAFE_SESSION`` Use session in safe mode. When in safe 143 | mode, all methods like ``save`` and 144 | ``delete`` wait for the operation to 145 | complete. 146 | 147 | *Default value:* ``False`` 148 | 149 | ``MONGOALCHEMY_OPTIONS`` Pass extra options to the MongoDB server 150 | when connecting. 151 | 152 | *e.g.:* safe=true 153 | *Default value:* ``None`` 154 | 155 | ``MONGOALCHEMY_SERVER_AUTH`` Boolean value indicating to use server based 156 | authentication or not. When ``False``, will use 157 | database based authentication. 158 | 159 | *Default value:* ``True`` 160 | 161 | ``MONGOALCHEMY_REPLICA_SET`` Name of the replica set to be used. Empty for 162 | no replica sets. 163 | 164 | *Default value:* ``''`` 165 | 166 | ``MONGOALCHEMY_CONNECTION_STRING`` The connection string to use, when it's 167 | defined, Flask MongoAlchemy will ignore other 168 | connection settings previously specified. 169 | 170 | ==================================== ================================================== 171 | 172 | When :class:`~flask_mongoalchemy.MongoAlchemy` or 173 | :meth:`~flask_mongoalchemy.MongoAlchemy.init_app` are invoked with only one argument 174 | (the :class:`~flask.Flask` instance), a configuration value prefix of 175 | ``MONGOALCHEMY`` is assumed; this can be overridden with the `config_prefix` 176 | argument, for example: 177 | 178 | :: 179 | 180 | app = Flask(__name__) 181 | 182 | # defaulting to MONGOENGINE prefix 183 | mongo1 = MongoAlchemy(app) 184 | 185 | # using another database config 186 | app.config['OTHER_DBNAME'] = 'other' 187 | mongo2 = MongoAlchemy(app, config_prefix='OTHER') 188 | 189 | API 190 | --- 191 | 192 | This part of the documentation documents all the public classes and 193 | functions in Flask-MongoAlchemy. 194 | 195 | Configuration 196 | +++++++++++++ 197 | 198 | .. autoclass:: MongoAlchemy 199 | :members: 200 | 201 | Documents 202 | +++++++++ 203 | 204 | .. autoclass:: Document 205 | :members: 206 | 207 | Querying 208 | ++++++++ 209 | 210 | .. autoclass:: BaseQuery 211 | :members: 212 | 213 | Utilities 214 | +++++++++ 215 | 216 | .. autoclass:: Pagination 217 | :members: 218 | 219 | Indices and tables 220 | ------------------ 221 | 222 | * :ref:`genindex` 223 | * :ref:`modindex` 224 | * :ref:`search` 225 | 226 | .. _Flask: http://flask.pocoo.org 227 | .. _Github: http://github.com/cobrateam/flask-mongoalchemy 228 | .. _MongoDB: http://mongodb.org 229 | .. _MongoAlchemy: http://mongoalchemy.org 230 | -------------------------------------------------------------------------------- /examples/README.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | In this directory you can find some example Flask applications using Flask-MongoAlchemy: 5 | 6 | 1. **library** is a very simple application which saves and lists books authors 7 | 2. **books_collection** is a books CRUD 8 | -------------------------------------------------------------------------------- /examples/books_collection/README.rst: -------------------------------------------------------------------------------- 1 | Simple CRUD sample 2 | ================== 3 | 4 | A CRUD example using Flask and MongoAlchemy, through Flask-MongoAlchemy extension. 5 | 6 | It's a books collection, so we can add, edit, delete and view books. 7 | 8 | Dependencies 9 | ------------ 10 | 11 | You can install all dependencies by running: 12 | 13 | :: 14 | 15 | $ [sudo] pip install -r requirements.txt 16 | -------------------------------------------------------------------------------- /examples/books_collection/collection/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from flask import Flask 8 | from flask.ext.mongoalchemy import MongoAlchemy 9 | import string 10 | 11 | app = Flask(__name__) 12 | app.config['MONGOALCHEMY_DATABASE'] = 'books_collection' 13 | app.config['SECRET_KEY'] = 'very secret, do you believe?' 14 | app.config['DEBUG'] = True 15 | db = MongoAlchemy(app) 16 | 17 | 18 | @app.context_processor 19 | def put_letters_on_request(): 20 | return {'letters': string.ascii_uppercase} 21 | 22 | import views 23 | _ = views 24 | -------------------------------------------------------------------------------- /examples/books_collection/collection/documents.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from collection import db 8 | from flask.ext.mongoalchemy import BaseQuery 9 | import re 10 | 11 | 12 | class BookQuery(BaseQuery): 13 | 14 | def starting_with(self, letter): 15 | regex = r'^' + letter 16 | return self.filter({'title': re.compile(regex, re.IGNORECASE)}) 17 | 18 | 19 | class Book(db.Document): 20 | query_class = BookQuery 21 | 22 | title = db.StringField() 23 | year = db.IntField() 24 | -------------------------------------------------------------------------------- /examples/books_collection/collection/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | import wtforms 8 | from flask.ext import wtf 9 | from wtforms import validators 10 | 11 | from collection.documents import Book 12 | 13 | 14 | class BookForm(wtf.Form): 15 | document_class = Book 16 | title = wtforms.TextField(validators=[validators.Required()]) 17 | year = wtforms.IntegerField(validators=[validators.Required()]) 18 | instance = None 19 | 20 | def __init__(self, document=None, *args, **kwargs): 21 | super(BookForm, self).__init__(*args, **kwargs) 22 | if document is not None: 23 | self.instance = document 24 | self._copy_data_to_form() 25 | 26 | def _copy_data_to_form(self): 27 | self.title.data = self.instance.title 28 | self.year.data = self.instance.year 29 | 30 | def save(self): 31 | if self.instance is None: 32 | self.instance = self.document_class() 33 | self.instance.title = self.title.data 34 | self.instance.year = self.year.data 35 | self.instance.save() 36 | return self.instance 37 | -------------------------------------------------------------------------------- /examples/books_collection/collection/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block title %}{% endblock %} | Books collection 5 | 6 | 7 | 16 | {% block content %} 17 | {% endblock %} 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/books_collection/collection/templates/books/edit.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | Edit book {{ book.title }} 5 | {% endblock %} 6 | 7 | {% block content %} 8 |

Edit book {{ book.title }}

9 |
10 | {{ form.csrf_token }} 11 |

{{ form.title.label }}: {{ form.title }}

12 |

{{ form.year.label }}: {{ form.year }}

13 |

14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/books_collection/collection/templates/books/list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | {{ title }} 5 | {% endblock %} 6 | 7 | {% block content %} 8 |

{{ title }}

9 | 10 |
    11 | {% for book in pagination.items %} 12 |
  • Title: {{ book.title }}; Year: {{ book.year }} (Edit | Delete)
  • 13 | {% endfor %} 14 |
15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /examples/books_collection/collection/templates/books/list_all.html: -------------------------------------------------------------------------------- 1 | {% extends "books/list.html" %} 2 | 3 | {% block content %} 4 | {{ super() }} 5 | 6 |

7 | {% if pagination.has_prev() %} 8 | < Previous page 9 | {% endif %} 10 |   11 | {% if pagination.has_next() %} 12 | Next page > 13 | {% endif %} 14 |

15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/books_collection/collection/templates/books/list_filtered.html: -------------------------------------------------------------------------------- 1 | {% extends "books/list.html" %} 2 | 3 | {% block content %} 4 | {{ super() }} 5 | 6 |

7 | {% if pagination.has_prev() %} 8 | < Previous page 9 | {% endif %} 10 |   11 | {% if pagination.has_next() %} 12 | Next page > 13 | {% endif %} 14 |

15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/books_collection/collection/templates/books/new.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %} 4 | New book 5 | {% endblock %} 6 | 7 | {% block content %} 8 |

New book

9 |
10 | {{ form.csrf_token }} 11 |

{{ form.title.label }}: {{ form.title }}

12 |

{{ form.year.label }}: {{ form.year }}

13 |

14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/books_collection/collection/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from flask import render_template, redirect, url_for 8 | 9 | from collection import app 10 | from collection.forms import BookForm 11 | from collection.documents import Book 12 | 13 | 14 | @app.route('/books/new', methods=['GET', 'POST']) 15 | def new_book(): 16 | form = BookForm() 17 | if form.validate_on_submit(): 18 | form.save() 19 | return redirect(url_for('list_books')) 20 | return render_template('/books/new.html', form=form) 21 | 22 | 23 | @app.route('/books') 24 | @app.route('/books/') 25 | def list_books(page=1): 26 | title = u'Books list' 27 | pagination = Book.query.paginate(page=page, per_page=5) 28 | return render_template('/books/list_all.html', pagination=pagination, title=title) 29 | 30 | 31 | @app.route('/books/') 32 | @app.route('/books//') 33 | def list_books_filtering(letter, page=1): 34 | title = u'Books starting with %s' % letter.upper() 35 | pagination = Book.query.starting_with(letter).paginate(page=page, per_page=5) 36 | return render_template('/books/list_filtered.html', pagination=pagination, 37 | title=title, letter=letter) 38 | 39 | 40 | @app.route('/books/delete/') 41 | def delete_book(id): 42 | book = Book.query.get_or_404(id) 43 | book.remove() 44 | return redirect(url_for('list_books')) 45 | 46 | 47 | @app.route('/books/edit/') 48 | def edit_book(id): 49 | book = Book.query.get(id) 50 | form = BookForm(document=book) 51 | return render_template('/books/edit.html', form=form, book=book) 52 | 53 | 54 | @app.route('/books/edit/', methods=['POST']) 55 | def update_book(id): 56 | book = Book.query.get(id) 57 | form = BookForm() 58 | if form.validate_on_submit(): 59 | form.instance = book 60 | form.save() 61 | return redirect(url_for('list_books')) 62 | form = BookForm(document=book) 63 | return render_template('/books/edit.html', form=form, book=book) 64 | -------------------------------------------------------------------------------- /examples/books_collection/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Flask-MongoAlchemy 3 | Flask-WTF 4 | -------------------------------------------------------------------------------- /examples/books_collection/run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2010 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from collection import app 8 | 9 | if __name__ == '__main__': 10 | app.run() 11 | -------------------------------------------------------------------------------- /examples/books_collection/test_collection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2010 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | import unittest 8 | import collection 9 | 10 | 11 | class BooksCollectionTestCase(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self.app = collection.app.test_client() 15 | 16 | if __name__ == '__main__': 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /examples/library/library.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from flask import Flask, request 8 | from flask.ext.mongoalchemy import MongoAlchemy 9 | app = Flask(__name__) 10 | app.config['DEBUG'] = True 11 | app.config['MONGOALCHEMY_DATABASE'] = 'library' 12 | db = MongoAlchemy(app) 13 | 14 | 15 | class Author(db.Document): 16 | name = db.StringField() 17 | 18 | 19 | class Book(db.Document): 20 | title = db.StringField() 21 | author = db.DocumentField(Author) 22 | year = db.IntField() 23 | 24 | 25 | @app.route('/author/new') 26 | def new_author(): 27 | """Creates a new author by a giving name (via GET parameter) 28 | 29 | e.g.: GET /author/new?name=Francisco creates a author named Francisco 30 | """ 31 | author = Author(name=request.args.get('name', '')) 32 | author.save() 33 | return 'Saved :)' 34 | 35 | 36 | @app.route('/authors/') 37 | def list_authors(): 38 | """List all authors. 39 | 40 | e.g.: GET /authors""" 41 | authors = Author.query.all() 42 | content = '

Authors:

' 43 | for author in authors: 44 | content += '

%s

' % author.name 45 | return content 46 | 47 | if __name__ == '__main__': 48 | app.run() 49 | -------------------------------------------------------------------------------- /flask_mongoalchemy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2015 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from math import ceil 8 | from mongoalchemy import document, exceptions, fields, session, query 9 | from flask import abort 10 | import pytz 11 | 12 | from .meta import make_document_class 13 | 14 | 15 | def _include_mongoalchemy(obj): 16 | for key in dir(fields): 17 | if not hasattr(obj, key): 18 | setattr(obj, key, getattr(fields, key)) 19 | key = 'DocumentField' 20 | setattr(obj, key, getattr(document, key)) 21 | 22 | 23 | def _get_mongo_uri(app, key=lambda x:'MONGOALCHEMY_%s' % x): 24 | app.config.setdefault(key('SERVER'), 'localhost') 25 | app.config.setdefault(key('PORT'), '27017') 26 | app.config.setdefault(key('USER'), None) 27 | app.config.setdefault(key('PASSWORD'), None) 28 | app.config.setdefault(key('OPTIONS'), None) 29 | app.config.setdefault(key('REPLICA_SET'), '') 30 | 31 | auth = '' 32 | database = '' 33 | 34 | uri = app.config.get(key('CONNECTION_STRING')) 35 | if uri: 36 | return uri 37 | 38 | if app.config.get(key('USER')) is not None: 39 | auth = app.config.get(key('USER')) 40 | if app.config.get(key('PASSWORD')) is not None: 41 | auth = '%s:%s' % (auth, app.config.get(key('PASSWORD'))) 42 | auth += '@' 43 | 44 | if not app.config.get(key('SERVER_AUTH'), True): 45 | database = app.config.get(key('DATABASE')) 46 | 47 | options = '' 48 | 49 | if app.config.get(key('OPTIONS')) is not None: 50 | options = "?%s" % app.config.get(key('OPTIONS')) 51 | 52 | uri = 'mongodb://%s%s:%s/%s%s' % (auth, app.config.get(key('SERVER')), 53 | app.config.get(key('PORT')), database, options) 54 | 55 | return uri 56 | 57 | 58 | class ImproperlyConfiguredError(Exception): 59 | """Exception for error on configurations.""" 60 | pass 61 | 62 | 63 | class _QueryField(object): 64 | 65 | def __init__(self, db): 66 | self.db = db 67 | 68 | def __get__(self, obj, cls): 69 | try: 70 | return cls.query_class(cls, self.db.session) 71 | except Exception: 72 | return None 73 | 74 | 75 | class MongoAlchemy(object): 76 | """Class used to control the MongoAlchemy integration to a Flask application. 77 | 78 | You can use this by providing the Flask app on instantiation or by calling 79 | an :meth:`init_app` method an instance object of `MongoAlchemy`. Here an 80 | example of providing the application on instantiation: :: 81 | 82 | app = Flask(__name__) 83 | db = MongoAlchemy(app) 84 | 85 | And here calling the :meth:`init_app` method: :: 86 | 87 | db = MongoAlchemy() 88 | 89 | def init_app(): 90 | app = Flask(__name__) 91 | db.init_app(app) 92 | return app 93 | """ 94 | 95 | def __init__(self, app=None, config_prefix='MONGOALCHEMY'): 96 | self.Document = make_document_class(self, Document) 97 | self.Document.query = _QueryField(self) 98 | 99 | _include_mongoalchemy(self) 100 | 101 | if app is not None: 102 | self.init_app(app, config_prefix) 103 | else: 104 | self.session = None 105 | 106 | def init_app(self, app, config_prefix='MONGOALCHEMY'): 107 | """This callback can be used to initialize an application for the use with this 108 | MongoDB setup. Never use a database in the context of an application not 109 | initialized that way or connections will leak.""" 110 | 111 | self.config_prefix = config_prefix 112 | def key(suffix): 113 | return '%s_%s' % (config_prefix, suffix) 114 | 115 | if key('DATABASE') not in app.config: 116 | raise ImproperlyConfiguredError("You should provide a database name " 117 | "(the %s setting)." % key('DATABASE')) 118 | 119 | uri = _get_mongo_uri(app, key) 120 | rs = app.config.get(key('REPLICA_SET')) 121 | timezone = None 122 | if key('TIMEZONE') in app.config: 123 | timezone = pytz.timezone(app.config.get(key('TIMEZONE'))) 124 | self.session = session.Session.connect(app.config.get(key('DATABASE')), 125 | safe=app.config.get(key('SAFE_SESSION'), 126 | False), 127 | timezone = timezone, 128 | host=uri, replicaSet=rs) 129 | self.Document._session = self.session 130 | 131 | 132 | class Pagination(object): 133 | """Internal helper class returned by :meth:`~BaseQuery.paginate`.""" 134 | 135 | def __init__(self, query, page, per_page, total, items): 136 | #: query object used to create this 137 | #: pagination object. 138 | self.query = query 139 | #: current page number 140 | self.page = page 141 | #: number of items to be displayed per page 142 | self.per_page = per_page 143 | #: total number of items matching the query 144 | self.total = total 145 | #: list of items for the current page 146 | self.items = items 147 | 148 | @property 149 | def pages(self): 150 | """The total number of pages""" 151 | return int(ceil(self.total / float(self.per_page))) 152 | 153 | @property 154 | def next_num(self): 155 | """The next page number.""" 156 | return self.page + 1 157 | 158 | def has_next(self): 159 | """Returns ``True`` if a next page exists.""" 160 | return self.page < self.pages 161 | 162 | def next(self, error_out=False): 163 | """Return a :class:`Pagination` object for the next page.""" 164 | return self.query.paginate(self.page + 1, self.per_page, error_out) 165 | 166 | @property 167 | def prev_num(self): 168 | """The previous page number.""" 169 | return self.page - 1 170 | 171 | def has_prev(self): 172 | """Returns ``True`` if a previous page exists.""" 173 | return self.page > 1 174 | 175 | def prev(self, error_out=False): 176 | """Return a :class:`Pagination` object for the previous page.""" 177 | return self.query.paginate(self.page - 1, self.per_page, error_out) 178 | 179 | 180 | class BaseQuery(query.Query): 181 | """ 182 | Base class for custom user query classes. 183 | 184 | This class provides some methods and can be extended to provide a 185 | customized query class to a user document. 186 | 187 | Here an example: :: 188 | 189 | from flask.ext.mongoalchemy import BaseQuery 190 | from application import db 191 | 192 | class MyCustomizedQuery(BaseQuery): 193 | 194 | def get_johns(self): 195 | return self.filter(self.type.first_name == 'John') 196 | 197 | class Person(db.Document): 198 | query_class = MyCustomizedQuery 199 | name = db.StringField() 200 | 201 | And you will be able to query the Person model this way: :: 202 | 203 | >>> johns = Person.query.get_johns().first() 204 | 205 | *Note:* If you are extending BaseQuery and writing an ``__init__`` method, 206 | you should **always** call this class __init__ via ``super`` keyword. 207 | 208 | Here an example: :: 209 | 210 | class MyQuery(BaseQuery): 211 | 212 | def __init__(self, *args, **kwargs): 213 | super(MyQuery, self).__init__(*args, **kwargs) 214 | 215 | This class is instantiated automatically by Flask-MongoAlchemy, don't 216 | provide anything new to your ``__init__`` method. 217 | """ 218 | 219 | def __init__(self, type, session): 220 | super(BaseQuery, self).__init__(type, session) 221 | 222 | def get(self, mongo_id): 223 | """Returns a :class:`Document` instance from its ``mongo_id`` or ``None`` 224 | if not found""" 225 | try: 226 | return self.filter(self.type.mongo_id == mongo_id).first() 227 | except exceptions.BadValueException: 228 | return None 229 | 230 | def get_or_404(self, mongo_id): 231 | """Like :meth:`get` method but aborts with 404 if not found instead of 232 | returning `None`""" 233 | document = self.get(mongo_id) 234 | if document is None: 235 | abort(404) 236 | return document 237 | 238 | def first_or_404(self): 239 | """Returns the first result of this query, or aborts with 404 if the result 240 | doesn't contain any row""" 241 | document = self.first() 242 | if document is None: 243 | abort(404) 244 | return document 245 | 246 | def paginate(self, page, per_page=20, error_out=True): 247 | """Returns ``per_page`` items from page ``page`` By default, it will 248 | abort with 404 if no items were found and the page was larger than 1. 249 | This behaviour can be disabled by setting ``error_out`` to ``False``. 250 | 251 | Returns a :class:`Pagination` object.""" 252 | if page < 1 and error_out: 253 | abort(404) 254 | 255 | items = self.skip((page - 1) * per_page).limit(per_page).all() 256 | 257 | if len(items) < 1 and page != 1 and error_out: 258 | abort(404) 259 | 260 | return Pagination(self, page, per_page, self.count(), items) 261 | 262 | 263 | class Document(document.Document): 264 | "Base class for custom user documents." 265 | 266 | #: the query class used. The :attr:`query` attribute is an instance 267 | #: of this class. By default :class:`BaseQuery` is used. 268 | query_class = BaseQuery 269 | 270 | #: an instance of :attr:`query_class`. Used to query the database 271 | #: for instances of this document. 272 | query = None 273 | 274 | def save(self, safe=None): 275 | """Saves the document itself in the database. 276 | 277 | The optional ``safe`` argument is a boolean that specifies if the 278 | remove method should wait for the operation to complete. 279 | """ 280 | self._session.insert(self, safe=safe) 281 | self._session.flush() 282 | 283 | def remove(self, safe=None): 284 | """Removes the document itself from database. 285 | 286 | The optional ``safe`` argument is a boolean that specifies if the 287 | remove method should wait for the operation to complete. 288 | """ 289 | self._session.remove(self, safe=None) 290 | self._session.flush() 291 | 292 | def __eq__(self, other): 293 | return isinstance(other, type(self)) and \ 294 | self.has_id() and other.has_id() and \ 295 | self.mongo_id == other.mongo_id 296 | -------------------------------------------------------------------------------- /flask_mongoalchemy/meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from mongoalchemy import document 8 | 9 | 10 | def make_document_class(session, document_class): 11 | class_dict = document_class.__dict__.copy() 12 | class_dict.update({'_session': session}) 13 | return type("Document", (document.Document,), class_dict) 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Testing 2 | coverage==3.7.1 3 | mocker==1.1.1 4 | 5 | # Development 6 | flask==0.10.1 7 | mongoalchemy==0.19 8 | pymongo==2.8.1 9 | 10 | # Documentation 11 | sphinx 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2015 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | """ 8 | Flask MongoAlchemy 9 | ------------------ 10 | 11 | Adds Flask support for MongoDB using Mongo-Alchemy. 12 | 13 | Links 14 | ````` 15 | 16 | * `documentation `_ 17 | * `development version 18 | `_ 19 | 20 | """ 21 | from setuptools import setup 22 | 23 | readme = __doc__ 24 | with open('README.rst') as readme_file: 25 | readme = readme_file.read() 26 | 27 | setup( 28 | name='Flask-MongoAlchemy', 29 | version='0.7.2', 30 | url='http://github.com/cobrateam/flask-mongoalchemy', 31 | license='BSD', 32 | author='Francisco Souza', 33 | author_email='francisco@franciscosouza.net', 34 | description='Add Flask support for MongoDB using MongoAlchemy.', 35 | long_description=readme, 36 | packages=['flask_mongoalchemy'], 37 | zip_safe=False, 38 | platforms='any', 39 | install_requires=[ 40 | 'Flask>=0.9', 41 | 'MongoAlchemy==0.19', 42 | 'pymongo==2.8.1', 43 | ], 44 | classifiers=[ 45 | 'Environment :: Web Environment', 46 | 'Intended Audience :: Developers', 47 | 'License :: OSI Approved :: BSD License', 48 | 'Operating System :: OS Independent', 49 | 'Programming Language :: Python', 50 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 51 | 'Topic :: Software Development :: Libraries :: Python Modules' 52 | ] 53 | ) 54 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | import flask_mongoalchemy as mongoalchemy 8 | from mocker import MockerTestCase 9 | from flask import Flask 10 | from werkzeug.exceptions import NotFound 11 | from tests.helpers import _make_todo_document 12 | 13 | 14 | class BaseTestCase(MockerTestCase): 15 | 16 | def setUp(self): 17 | super(BaseTestCase, self).setUp() 18 | self.setup() 19 | 20 | def tearDown(self): 21 | super(BaseTestCase, self).tearDown() 22 | self.teardown() 23 | 24 | def setup(self): 25 | pass 26 | 27 | def teardown(self): 28 | pass 29 | 30 | 31 | class BaseAppTestCase(BaseTestCase): 32 | 33 | def setup(self): 34 | self.app = Flask(__name__) 35 | self.app.config['MONGOALCHEMY_DATABASE'] = 'testing' 36 | self.app.config['TESTING'] = True 37 | self.db = mongoalchemy.MongoAlchemy(self.app) 38 | self.Todo = _make_todo_document(self.db) 39 | 40 | def teardown(self): 41 | for todo in self.Todo.query.all(): 42 | todo.remove() 43 | 44 | def _replace_flask_abort(self, calls=1): 45 | """Replaces flask.abort function using mocker""" 46 | abort = self.mocker.replace('flask.abort') 47 | abort(404) 48 | self.mocker.count(calls) 49 | self.mocker.replay() 50 | 51 | def _replace_flask_abort_raising_exception(self, calls=1): 52 | """Replaces flask.abort function using mocker""" 53 | abort = self.mocker.replace('flask.abort') 54 | abort(404) 55 | self.mocker.count(calls) 56 | self.mocker.throw(NotFound) 57 | self.mocker.replay() 58 | -------------------------------------------------------------------------------- /tests/helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | 8 | def _make_todo_document(db): 9 | class Todo(db.Document): 10 | description = db.StringField() 11 | return Todo 12 | -------------------------------------------------------------------------------- /tests/test_document.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from tests import BaseAppTestCase 8 | 9 | 10 | class MongoAlchemyDocumentTestCase(BaseAppTestCase): 11 | 12 | def test_should_be_able_to_save_a_document_on_database_by_calling_its_save_method(self): 13 | todo = self.Todo(description=u'Reinvent the world') 14 | todo.save() 15 | self.assertEqual(self.Todo.query.count(), 1) 16 | 17 | def test_should_be_able_to_remove_a_document_on_database_by_calling_its_remove_method(self): 18 | todo = self.Todo(description=u'Reinvent the world') 19 | todo.save() 20 | self.assertEqual(self.Todo.query.count(), 1) 21 | todo.remove() 22 | self.assertEqual(self.Todo.query.count(), 0) 23 | 24 | def test_should_be_equal_by_the_id(self): 25 | todo = self.Todo(description=u'Reinvent the world') 26 | todo.save() 27 | my_new_todo = self.Todo(description=u'Save the world') 28 | my_new_todo.mongo_id = todo.mongo_id 29 | self.assertEqual(todo, my_new_todo) 30 | another_todo = self.Todo(description=u'Destroy the world') 31 | self.assertNotEqual(todo, another_todo) 32 | 33 | def test_should_be_able_update_a_document_by_calling_its_save_method(self): 34 | todo = self.Todo(description=u'Reinvent the world') 35 | todo.save() 36 | mongo_id = todo.mongo_id 37 | todo.description = u'Destroy the world.' 38 | todo.save() 39 | searched_todo = self.Todo.query.get(mongo_id) 40 | self.assertEqual(searched_todo.description, u'Destroy the world.') 41 | self.assertEqual(self.Todo.query.count(), 1) 42 | -------------------------------------------------------------------------------- /tests/test_meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2010 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from __future__ import absolute_import 8 | 9 | from mongoalchemy import document 10 | 11 | from flask.ext.mongoalchemy import Document 12 | from flask.ext.mongoalchemy.meta import make_document_class 13 | from tests import BaseAppTestCase 14 | 15 | 16 | class MetaTestCase(BaseAppTestCase): 17 | "meta.py test case" 18 | 19 | def test_should_be_able_to_create_a_new_document(self): 20 | MyDocument = make_document_class(self.db, Document) 21 | assert issubclass(MyDocument, document.Document) 22 | -------------------------------------------------------------------------------- /tests/test_mongoalchemy_object.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from flask import Flask 8 | from mongoalchemy import fields 9 | from mongoalchemy.session import Session 10 | 11 | from flask.ext.mongoalchemy import BaseQuery, ImproperlyConfiguredError, MongoAlchemy 12 | from . import BaseTestCase 13 | 14 | 15 | class MongoAlchemyObjectTestCase(BaseTestCase): 16 | "MongoAlchemy itself object tests" 17 | 18 | def setUp(self): 19 | self.app = Flask(__name__) 20 | self.app.config['MONGOALCHEMY_DATABASE'] = 'testing' 21 | self.app.config['TESTING'] = True 22 | 23 | def tearDown(self): 24 | del(self.app) 25 | 26 | def test_should_provide_a_Document_class_to_be_extended_inside_the_MongoAlchemy_object(self): 27 | db = MongoAlchemy() 28 | assert db.Document is not None 29 | 30 | def test_should_provide_a_query_object_for_queries_on_a_document(self): 31 | db = MongoAlchemy(self.app) 32 | 33 | class Todo(db.Document): 34 | description = db.StringField() 35 | 36 | self.assertIsInstance(Todo.query, BaseQuery) 37 | 38 | def test_should_provide_a_session_object_on_mongoalchemy_instance(self): 39 | db = MongoAlchemy(self.app) 40 | self.assertIsInstance(db.session, Session) 41 | 42 | def test_should_be_possible_to_create_a_customized_query_class(self): 43 | db = MongoAlchemy(self.app) 44 | 45 | class Query(BaseQuery): 46 | pass 47 | 48 | class Todo(db.Document): 49 | description = db.StringField() 50 | query_class = Query 51 | 52 | self.assertIsInstance(Todo.query, Query) 53 | 54 | def test_invalid_query_is_none(self): 55 | db = MongoAlchemy() 56 | 57 | class Query(object): 58 | pass 59 | 60 | class Todo(db.Document): 61 | description = db.StringField() 62 | query_class = Query 63 | 64 | assert Todo.query is None 65 | 66 | def test_should_include_all_mongo_alchemy_fields_objects(self): 67 | db = MongoAlchemy() 68 | for key in dir(fields): 69 | assert hasattr(db, key), "should have the %s attribute" % key 70 | assert hasattr(db, 'DocumentField'), "should have the DocumentField attribute" 71 | 72 | def test_should_be_able_to_instantiate_passing_the_app(self): 73 | db = MongoAlchemy(self.app) 74 | assert db.session is not None 75 | 76 | def test_should_be_able_to_instantiate_without_passing_the_app_and_set_it_later(self): 77 | db = MongoAlchemy() 78 | assert db.session is None 79 | db.init_app(self.app) 80 | assert db.session is not None 81 | 82 | def test_should_contain_a_not_none_query(self): 83 | "Document.query should never be None" 84 | db = MongoAlchemy() 85 | db.init_app(self.app) 86 | 87 | class Person(db.Document): 88 | name = db.StringField() 89 | 90 | p = Person() 91 | assert p.query is not None 92 | 93 | def test_should_not_be_able_to_work_without_providing_a_database_name(self): 94 | with self.assertRaises(ImproperlyConfiguredError): 95 | app = Flask(__name__) 96 | MongoAlchemy(app) 97 | 98 | def test_loads_without_database_connection_data(self): 99 | app = Flask(__name__) 100 | app.config['MONGOALCHEMY_DATABASE'] = 'my_database' 101 | MongoAlchemy(app) 102 | self.assertEqual(app.config['MONGOALCHEMY_SERVER'], 'localhost') 103 | self.assertEqual(app.config['MONGOALCHEMY_PORT'], '27017') 104 | self.assertEqual(app.config['MONGOALCHEMY_USER'], None) 105 | self.assertEqual(app.config['MONGOALCHEMY_PASSWORD'], None) 106 | self.assertEqual(app.config['MONGOALCHEMY_REPLICA_SET'], '') 107 | 108 | def test_should_be_able_to_create_two_decoupled_mongoalchemy_instances(self): 109 | app = Flask(__name__) 110 | app.config['MONGOALCHEMY_DATABASE'] = 'my_database' 111 | db1 = MongoAlchemy(app) 112 | db2 = MongoAlchemy(app) 113 | assert db1.Document is not db2.Document, "two documents should not be the same object" 114 | -------------------------------------------------------------------------------- /tests/test_mongodb_uri.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2015 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from tests import BaseTestCase 8 | from flask import Flask 9 | 10 | 11 | class MongoDBURITestCase(BaseTestCase): 12 | "MongoDB URI generation" 13 | 14 | def setup(self): 15 | self.app = Flask(__name__) 16 | self.app.config['MONGOALCHEMY_DATABASE'] = 'test' 17 | 18 | def test_uri_without_database_name(self): 19 | from flask.ext.mongoalchemy import _get_mongo_uri 20 | self.assertEqual(_get_mongo_uri(self.app), 'mongodb://localhost:27017/') 21 | 22 | def test_uri_with_user_only(self): 23 | self.app.config['MONGOALCHEMY_USER'] = 'luke' 24 | from flask.ext.mongoalchemy import _get_mongo_uri 25 | self.assertEqual(_get_mongo_uri(self.app), 'mongodb://luke@localhost:27017/') 26 | 27 | def test_uri_with_user_and_password(self): 28 | self.app.config['MONGOALCHEMY_USER'] = 'luke' 29 | self.app.config['MONGOALCHEMY_PASSWORD'] = 'father' 30 | self.app.config['MONGOALCHEMY_SERVER_AUTH'] = False 31 | from flask.ext.mongoalchemy import _get_mongo_uri 32 | self.assertEqual(_get_mongo_uri(self.app), 'mongodb://luke:father@localhost:27017/test') 33 | 34 | def test_mongodb_uri_with_external_server(self): 35 | self.app.config['MONGOALCHEMY_SERVER'] = 'database.lukehome.com' 36 | self.app.config['MONGOALCHEMY_PORT'] = '42' 37 | from flask.ext.mongoalchemy import _get_mongo_uri 38 | self.assertEqual(_get_mongo_uri(self.app), 'mongodb://database.lukehome.com:42/') 39 | 40 | def test_mongodb_uri_with_options(self): 41 | self.app.config['MONGOALCHEMY_SERVER'] = 'database.lukehome.com' 42 | self.app.config['MONGOALCHEMY_OPTIONS'] = 'safe=true' 43 | from flask.ext.mongoalchemy import _get_mongo_uri 44 | self.assertEqual(_get_mongo_uri(self.app), 45 | 'mongodb://database.lukehome.com:27017/?safe=true') 46 | 47 | def test_mongodb_uri_connection_string(self): 48 | self.app.config['MONGOALCHEMY_CONNECTION_STRING'] = uri = 'mongodb://luke@rhost:27018/test' 49 | from flask.ext.mongoalchemy import _get_mongo_uri 50 | self.assertEqual(_get_mongo_uri(self.app), uri) 51 | -------------------------------------------------------------------------------- /tests/test_pagination.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2010 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from tests import BaseAppTestCase 8 | from werkzeug.exceptions import NotFound 9 | 10 | 11 | class FlaskMongoAlchemyPaginationTestCase(BaseAppTestCase): 12 | "Flask-MongoAlchemy Pagination class" 13 | 14 | def setup(self): 15 | super(FlaskMongoAlchemyPaginationTestCase, self).setup() 16 | 17 | # saving 30 Todo's 18 | for i in range(4, 34): 19 | todo = self.Todo(description=u'Write my %dth book' % i) 20 | todo.save() 21 | 22 | def test_should_provide_a_pages_property(self): 23 | pagination = self.Todo.query.filter({}).paginate(page=1) 24 | self.assertEqual(pagination.pages, 2) 25 | pagination = self.Todo.query.filter({}).paginate(page=1, per_page=50) 26 | self.assertEqual(pagination.pages, 1) 27 | 28 | def test_should_provide_a_has_next_method(self): 29 | pagination = self.Todo.query.filter({}).paginate(page=1) 30 | assert pagination.has_next() 31 | pagination = self.Todo.query.filter({}).paginate(page=1, per_page=50) 32 | assert pagination.has_next() is False 33 | 34 | def test_should_provide_a_next_method(self): 35 | pagination = self.Todo.query.filter({}).paginate(page=1) 36 | next_page = pagination.next() 37 | self.assertEqual(next_page.page, 2) 38 | self.assertEqual(len(next_page.items), 10) 39 | self._replace_flask_abort() 40 | next_page.next(error_out=True) 41 | self.mocker.verify() 42 | 43 | def test_should_provide_a_has_prev_method(self): 44 | pagination = self.Todo.query.filter({}).paginate(page=2) 45 | assert pagination.has_prev() 46 | pagination = self.Todo.query.filter({}).paginate(page=1, per_page=50) 47 | assert pagination.has_prev() is False 48 | pagination = self.Todo.query.filter({}).paginate(page=1) 49 | assert pagination.has_prev() is False 50 | 51 | def test_should_provide_prev_method(self): 52 | pagination = self.Todo.query.filter({}).paginate(page=2) 53 | previous_page = pagination.prev() 54 | self.assertEqual(pagination.page, 2) 55 | self.assertEqual(previous_page.page, 1) 56 | self.assertEqual(len(previous_page.items), 20) 57 | with self.assertRaises(NotFound): 58 | self._replace_flask_abort_raising_exception() 59 | previous_page.prev(error_out=True) 60 | self.mocker.verify() 61 | 62 | def test_should_provide_the_number_of_the_next_page(self): 63 | pagination = self.Todo.query.filter({}).paginate(page=1) 64 | self.assertEqual(pagination.next_num, 2) 65 | 66 | def test_should_provide_the_number_of_the_previous_page(self): 67 | pagination = self.Todo.query.filter({}).paginate(page=2) 68 | self.assertEqual(pagination.prev_num, 1) 69 | -------------------------------------------------------------------------------- /tests/test_query.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2010 flask-mongoalchemy authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | from tests import BaseAppTestCase 8 | from werkzeug.exceptions import NotFound 9 | 10 | 11 | class FlaskMongoAlchemyQueryTestCase(BaseAppTestCase): 12 | "Flask-MongoAlchemy BaseQuery class" 13 | 14 | def test_should_provide_a_get_method_on_query_object(self): 15 | "Should provide a \"get()\" method on Query object" 16 | todo = self.Todo(description=u'Start something very new') 17 | todo.save() 18 | searched_todo = self.Todo.query.get(str(todo.mongo_id)) 19 | self.assertEqual(todo, searched_todo) 20 | 21 | def test_should_return_None_when_querying_for_a_non_existing_document_on_database(self): 22 | "\"get()\" method should return None when querying for a non-existing document" 23 | searched_todo = self.Todo.query.get('4e038a23e4206650da0000df') 24 | assert searched_todo is None 25 | 26 | def test_should_provide_a_get_or_404_method_on_query_object(self): 27 | "Should provide a \"get_or_404()\" method on Query object" 28 | self._replace_flask_abort() 29 | searched_todo = self.Todo.query.get_or_404('4e038a23e4206650da0000df') 30 | self.mocker.verify() 31 | 32 | todo = self.Todo(description=u'Start something') 33 | todo.save() 34 | searched_todo = self.Todo.query.get_or_404(str(todo.mongo_id)) 35 | self.assertEqual(todo, searched_todo) 36 | 37 | def test_should_provide_a_first_or_404_method_on_query_object(self): 38 | "Should provide a \"first_or_404()\" method on Query object" 39 | self._replace_flask_abort() 40 | searched_todo = self.Todo.query.filter({}).first_or_404() 41 | self.mocker.verify() 42 | 43 | todo1 = self.Todo(description=u'Start something new') 44 | todo1.save() 45 | todo2 = self.Todo(description=u'Clean the room') 46 | todo2.save() 47 | searched_todo = self.Todo.query.filter({}).first_or_404() 48 | self.assertEqual(todo1, searched_todo) 49 | 50 | def test_should_provide_a_paginate_method_on_query_object(self): 51 | for i in range(4, 20): 52 | todo = self.Todo(description=u'Try something for the %dth time' % i) 53 | todo.save() 54 | from flask.ext.mongoalchemy import Pagination 55 | assert isinstance(self.Todo.query.paginate(page=1, per_page=5), Pagination) 56 | 57 | def test_should_abort_with_404_when_paginating_an_empty_query(self): 58 | todo = self.Todo(description=u'Do anything weird') 59 | todo.save() 60 | 61 | self._replace_flask_abort_raising_exception(calls=2) 62 | self.assertRaises(NotFound, 63 | self.Todo.query.filter(self.Todo.description == 64 | u'Do anything weird').paginate, page=2) 65 | self.assertRaises(NotFound, 66 | self.Todo.query.filter(self.Todo.description == 67 | u'Do anything good').paginate, page=0) 68 | self.mocker.verify() 69 | 70 | def test_should_return_None_for_wrong_formated_objectids(self): 71 | todo = self.Todo.query.get("blasphemy") 72 | assert todo is None 73 | --------------------------------------------------------------------------------