├── docs ├── requirements.txt ├── _themes │ ├── flask_small │ │ ├── theme.conf │ │ ├── layout.html │ │ └── static │ │ │ └── flasky.css_t │ ├── README │ ├── LICENSE │ └── flask_theme_support.py ├── Makefile ├── conf.py └── index.rst ├── examples └── wiki │ ├── requirements.txt │ ├── templates │ ├── 404.html │ ├── page.html │ ├── upload.html │ └── edit.html │ └── wiki.py ├── .gitignore ├── setup.cfg ├── .travis.yml ├── MANIFEST.in ├── tests ├── test_url_converter.py ├── util.py ├── test_wrappers.py └── test_config.py ├── README.md ├── setup.py └── flask_pymongo ├── wrappers.py └── __init__.py /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask >= 0.8 2 | pymongo >= 2.1 3 | -------------------------------------------------------------------------------- /examples/wiki/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask-PyMongo 2 | markdown2 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Flask_PyMongo.egg-info 2 | nose*.egg-info 3 | nose*.egg 4 | _build 5 | .coverage 6 | dist 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity = 2 3 | detailed-errors = 1 4 | with-coverage = 1 5 | cover-package = flask_pymongo 6 | cover-inclusive = 1 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 2.6 4 | - 2.7 5 | - 3.3 6 | services: mongodb 7 | install: python setup.py -q install 8 | script: python setup.py -q test 9 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py *.cfg README.md 2 | recursive-include examples *.py *.html *.txt 3 | recursive-include docs *.py *.rst *.html *.css_t *.conf Makefile LICENSE README 4 | prune docs/_build 5 | global-exclude *.pyc 6 | -------------------------------------------------------------------------------- /examples/wiki/templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Not Found — Flask PyMongo Wiki 5 | 6 | 7 |

Not Found

8 |

Sorry about that.

9 | 10 | 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/wiki/templates/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{pagepath|totitle}} — Flask PyMongo Wiki 5 | 6 | 7 | {{page.body|wikify|safe}} 8 |
9 | Edit {{pagepath|totitle}} 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/wiki/templates/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Upload {{filename}} — Flask PyMongo Wiki 5 | 6 | 7 |

Upload {{filename}}

8 |
9 |
10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/wiki/templates/edit.html: -------------------------------------------------------------------------------- 1 | {%- if page != None %} 2 | {%- set verb = "Edit" %} 3 | {%- else %} 4 | {%- set verb = "Create" %} 5 | {%- endif %} 6 | 7 | 8 | 9 | {{verb}} {{pagepath|totitle}} — Flask PyMongo Wiki 10 | 11 | 12 |

{{verb}} {{pagepath|totitle}}

13 |
14 |
15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/test_url_converter.py: -------------------------------------------------------------------------------- 1 | import util 2 | from flask.ext.pymongo import BSONObjectIdConverter 3 | 4 | from bson import ObjectId 5 | from werkzeug.exceptions import BadRequest 6 | 7 | class UrlConverterTest(util.FlaskPyMongoTest): 8 | 9 | def test_bson_object_id_converter(self): 10 | converter = BSONObjectIdConverter("/") 11 | 12 | self.assertRaises(BadRequest, converter.to_python, ("132")) 13 | assert converter.to_python("4e4ac5cfffc84958fa1f45fb") == \ 14 | ObjectId("4e4ac5cfffc84958fa1f45fb") 15 | assert converter.to_url(ObjectId("4e4ac5cfffc84958fa1f45fb")) == \ 16 | "4e4ac5cfffc84958fa1f45fb" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask-PyMongo 2 | 3 | PyMongo support for Flask applications 4 | 5 | ## Quickstart 6 | 7 | from flask import Flask 8 | from flask.ext.pymongo import PyMongo 9 | 10 | app = Flask(__name__) 11 | mongo = PyMongo(app) 12 | 13 | @app.route('/') 14 | def home_page(): 15 | online_users = mongo.db.users.find({'online': True}) 16 | return render_template('index.html', 17 | online_users=online_users) 18 | 19 | ## More Info 20 | 21 | * [Flask-PyMongo Documentation](http://flask-pymongo.readthedocs.org/) 22 | * [PyMongo Documentation](http://api.mongodb.org/python/current/) 23 | * [Flask Documentation](http://flask.pocoo.org/docs/) 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/util.py: -------------------------------------------------------------------------------- 1 | import flask 2 | import flask.ext.pymongo 3 | import unittest 4 | 5 | class FlaskRequestTest(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.app = flask.Flask('test') 9 | self.context = self.app.test_request_context('/') 10 | self.context.push() 11 | 12 | def tearDown(self): 13 | self.context.pop() 14 | 15 | class FlaskPyMongoTest(FlaskRequestTest): 16 | 17 | def setUp(self): 18 | super(FlaskPyMongoTest, self).setUp() 19 | 20 | self.dbname = self.__class__.__name__ 21 | self.app.config['MONGO_DBNAME'] = self.dbname 22 | self.mongo = flask.ext.pymongo.PyMongo(self.app) 23 | self.mongo.cx.drop_database(self.dbname) 24 | 25 | def tearDown(self): 26 | self.mongo.cx.drop_database(self.dbname) 27 | 28 | super(FlaskPyMongoTest, self).tearDown() 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/test_wrappers.py: -------------------------------------------------------------------------------- 1 | import util 2 | from werkzeug.exceptions import HTTPException 3 | 4 | class CollectionTest(util.FlaskPyMongoTest): 5 | 6 | def test_find_one_or_404(self): 7 | self.mongo.db.things.remove() 8 | 9 | try: 10 | self.mongo.db.things.find_one_or_404({'_id': 'thing'}) 11 | except HTTPException as notfound: 12 | assert notfound.code == 404, "raised wrong exception" 13 | 14 | self.mongo.db.things.insert({'_id': 'thing', 'val': 'foo'}, safe=True) 15 | 16 | # now it should not raise 17 | thing = self.mongo.db.things.find_one_or_404({'_id': 'thing'}) 18 | assert thing['val'] == 'foo', 'got wrong thing' 19 | 20 | 21 | # also test with dotted-named collections 22 | self.mongo.db.things.morethings.remove() 23 | try: 24 | self.mongo.db.things.morethings.find_one_or_404({'_id': 'thing'}) 25 | except HTTPException as notfound: 26 | assert notfound.code == 404, "raised wrong exception" 27 | 28 | self.mongo.db.things.morethings.insert({'_id': 'thing', 'val': 'foo'}, safe=True) 29 | 30 | # now it should not raise 31 | thing = self.mongo.db.things.morethings.find_one_or_404({'_id': 'thing'}) 32 | assert thing['val'] == 'foo', 'got wrong thing' 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Flask-PyMongo 3 | ------------- 4 | 5 | PyMongo support for Flask applications. 6 | 7 | Installation 8 | ============ 9 | 10 | Flask-PyMongo is pip-installable: 11 | 12 | pip install Flask-PyMongo 13 | 14 | You can install the latest development snapshot like so: 15 | 16 | pip install http://github.com/dcrosta/flask-pymongo/tarball/master#egg=Flask-PyMongo-dev 17 | 18 | Upgrading 19 | ~~~~~~~~~ 20 | 21 | - Version 0.2.0 introduced a dependency on PyMongo version 2.4 or later, and 22 | introduced some potential backwards-breaking changes. Please review the 23 | `Changelog `_ 24 | carefully before upgrading. 25 | - Version 0.3.0 removed the `ReadPreference 26 | `_ 27 | redefinitions in ``flask_pymongo``, in favor of using the constants directly 28 | from `PyMongo `_. Please review the 29 | `Changelog `_ 30 | carefully before upgrading. 31 | 32 | Development 33 | =========== 34 | 35 | Source code is hosted in `GitHub `_ 36 | (contributions are welcome!) 37 | """ 38 | 39 | from setuptools import find_packages, setup 40 | 41 | setup( 42 | name='Flask-PyMongo', 43 | version='0.3.0', 44 | url='http://flask-pymongo.readthedocs.org/', 45 | download_url='https://github.com/dcrosta/flask-pymongo/tags', 46 | license='BSD', 47 | author='Dan Crosta', 48 | author_email='dcrosta@late.am', 49 | description='PyMongo support for Flask applications', 50 | long_description=__doc__, 51 | zip_safe=False, 52 | platforms='any', 53 | packages=find_packages(), 54 | install_requires=[ 55 | 'Flask >= 0.8', 56 | 'pymongo >= 2.4', 57 | ], 58 | classifiers=[ 59 | 'Environment :: Web Environment', 60 | 'Intended Audience :: Developers', 61 | 'License :: OSI Approved :: BSD License', 62 | 'Operating System :: OS Independent', 63 | 'Programming Language :: Python', 64 | 'Programming Language :: Python :: 2.6', 65 | 'Programming Language :: Python :: 2.7', 66 | 'Programming Language :: Python :: 3.3', 67 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 68 | 'Topic :: Software Development :: Libraries :: Python Modules' 69 | ], 70 | setup_requires=['nose'], 71 | tests_require=['nose', 'coverage'], 72 | test_suite='nose.collector', 73 | ) 74 | 75 | -------------------------------------------------------------------------------- /examples/wiki/wiki.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, redirect, render_template, request, url_for 2 | from flask.ext.pymongo import PyMongo 3 | import markdown2 4 | import re 5 | 6 | app = Flask(__name__) 7 | mongo = PyMongo(app) 8 | 9 | WIKIPART = re.compile(r'([A-Z][a-z0-9_]+)') 10 | WIKIWORD = re.compile(r'([A-Z][a-z0-9_]+(?:[A-Z][a-z0-9_]+)+)') 11 | 12 | @app.template_filter() 13 | def totitle(value): 14 | return ' '.join(WIKIPART.findall(value)) 15 | 16 | @app.template_filter() 17 | def wikify(value): 18 | parts = WIKIWORD.split(value) 19 | for i, part in enumerate(parts): 20 | if WIKIWORD.match(part): 21 | name = totitle(part) 22 | parts[i] = '[%s](%s)' % (name, url_for('show_page', pagepath=part)) 23 | return markdown2.markdown(''.join(parts)) 24 | 25 | @app.route('/') 26 | def show_page(pagepath): 27 | page = mongo.db.pages.find_one_or_404({'_id': pagepath}) 28 | return render_template('page.html', 29 | page=page, 30 | pagepath=pagepath) 31 | 32 | app.add_url_rule('/', 'homepage_redirect', redirect_to='/HomePage') 33 | 34 | @app.route('/edit/', methods=['GET']) 35 | def edit_page(pagepath): 36 | page = mongo.db.pages.find_one_or_404({'_id': pagepath}) 37 | return render_template('edit.html', 38 | page=page, 39 | pagepath=pagepath) 40 | 41 | @app.route('/edit/', methods=['POST']) 42 | def save_page(pagepath): 43 | if 'cancel' not in request.form: 44 | mongo.db.pages.update( 45 | {'_id': pagepath}, 46 | {'$set': {'body': request.form['body']}}, 47 | safe=True, upsert=True) 48 | return redirect(url_for('show_page', pagepath=pagepath)) 49 | 50 | @app.errorhandler(404) 51 | def new_page(error): 52 | pagepath = request.path.lstrip('/') 53 | if pagepath.startswith('uploads'): 54 | filename = pagepath[len('uploads'):].lstrip('/') 55 | return render_template('upload.html', filename=filename) 56 | else: 57 | return render_template('edit.html', page=None, pagepath=pagepath) 58 | 59 | @app.route('/uploads/') 60 | def get_upload(filename): 61 | return mongo.send_file(filename) 62 | 63 | @app.route('/uploads/', methods=['POST']) 64 | def save_upload(filename): 65 | if request.files.get('file'): 66 | mongo.save_file(filename, request.files['file']) 67 | return redirect(url_for('get_upload', filename=filename)) 68 | return render_template('upload.html', filename=filename) 69 | 70 | 71 | if __name__ == '__main__': 72 | import doctest 73 | doctest.testmod() 74 | app.run(debug=True) 75 | 76 | -------------------------------------------------------------------------------- /flask_pymongo/wrappers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Dan Crosta 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 met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, 8 | # this list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | # POSSIBILITY OF SUCH DAMAGE. 25 | 26 | from pymongo import collection 27 | from pymongo import mongo_client 28 | from pymongo import database 29 | from pymongo import mongo_replica_set_client 30 | 31 | from flask import abort 32 | 33 | 34 | class MongoClient(mongo_client.MongoClient): 35 | """Returns instances of :class:`flask_pymongo.wrappers.Database` instead 36 | of :class:`pymongo.database.Database` when accessed with dot notation. 37 | """ 38 | 39 | def __getattr__(self, name): 40 | attr = super(MongoClient, self).__getattr__(name) 41 | if isinstance(attr, database.Database): 42 | return Database(self, name) 43 | return attr 44 | 45 | class MongoReplicaSetClient(mongo_replica_set_client.MongoReplicaSetClient): 46 | """Returns instances of :class:`flask_pymongo.wrappers.Database` 47 | instead of :class:`pymongo.database.Database` when accessed with dot 48 | notation. """ 49 | 50 | def __getattr__(self, name): 51 | attr = super(MongoReplicaSetClient, self).__getattr__(name) 52 | if isinstance(attr, database.Database): 53 | return Database(self, name) 54 | return attr 55 | 56 | class Database(database.Database): 57 | """Returns instances of :class:`flask_pymongo.wrappers.Collection` 58 | instead of :class:`pymongo.collection.Collection` when accessed with dot 59 | notation. 60 | """ 61 | 62 | def __getattr__(self, name): 63 | attr = super(Database, self).__getattr__(name) 64 | if isinstance(attr, collection.Collection): 65 | return Collection(self, name) 66 | return attr 67 | 68 | class Collection(collection.Collection): 69 | """Custom sub-class of :class:`pymongo.collection.Collection` which 70 | adds Flask-specific helper methods. 71 | """ 72 | 73 | def __getattr__(self, name): 74 | attr = super(Collection, self).__getattr__(name) 75 | if isinstance(attr, collection.Collection): 76 | db = self._Collection__database 77 | return Collection(db, attr.name) 78 | return attr 79 | 80 | def find_one_or_404(self, *args, **kwargs): 81 | """Find and return a single document, or raise a 404 Not Found 82 | exception if no document matches the query spec. See 83 | :meth:`~pymongo.collection.Collection.find_one` for details. 84 | 85 | .. code-block:: python 86 | 87 | @app.route('/user/') 88 | def user_profile(username): 89 | user = mongo.db.users.find_one_or_404({'_id': username}) 90 | return render_template('user.html', 91 | user=user) 92 | """ 93 | found = self.find_one(*args, **kwargs) 94 | if found is None: 95 | abort(404) 96 | return found -------------------------------------------------------------------------------- /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, Punctuation, Other, Literal 5 | 6 | 7 | class FlaskyStyle(Style): 8 | background_color = "#f8f8f8" 9 | default_style = "" 10 | 11 | styles = { 12 | # No corresponding class for the following: 13 | #Text: "", # class: '' 14 | Whitespace: "underline #f8f8f8", # class: 'w' 15 | Error: "#a40000 border:#ef2929", # class: 'err' 16 | Other: "#000000", # class 'x' 17 | 18 | Comment: "italic #8f5902", # class: 'c' 19 | Comment.Preproc: "noitalic", # class: 'cp' 20 | 21 | Keyword: "bold #004461", # class: 'k' 22 | Keyword.Constant: "bold #004461", # class: 'kc' 23 | Keyword.Declaration: "bold #004461", # class: 'kd' 24 | Keyword.Namespace: "bold #004461", # class: 'kn' 25 | Keyword.Pseudo: "bold #004461", # class: 'kp' 26 | Keyword.Reserved: "bold #004461", # class: 'kr' 27 | Keyword.Type: "bold #004461", # class: 'kt' 28 | 29 | Operator: "#582800", # class: 'o' 30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 31 | 32 | Punctuation: "bold #000000", # class: 'p' 33 | 34 | # because special names such as Name.Class, Name.Function, etc. 35 | # are not recognized as such later in the parsing, we choose them 36 | # to look the same as ordinary variables. 37 | Name: "#000000", # class: 'n' 38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 39 | Name.Builtin: "#004461", # class: 'nb' 40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 41 | Name.Class: "#000000", # class: 'nc' - to be revised 42 | Name.Constant: "#000000", # class: 'no' - to be revised 43 | Name.Decorator: "#888", # class: 'nd' - to be revised 44 | Name.Entity: "#ce5c00", # class: 'ni' 45 | Name.Exception: "bold #cc0000", # class: 'ne' 46 | Name.Function: "#000000", # class: 'nf' 47 | Name.Property: "#000000", # class: 'py' 48 | Name.Label: "#f57900", # class: 'nl' 49 | Name.Namespace: "#000000", # class: 'nn' - to be revised 50 | Name.Other: "#000000", # class: 'nx' 51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 52 | Name.Variable: "#000000", # class: 'nv' - to be revised 53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 56 | 57 | Number: "#990000", # class: 'm' 58 | 59 | Literal: "#000000", # class: 'l' 60 | Literal.Date: "#000000", # class: 'ld' 61 | 62 | String: "#4e9a06", # class: 's' 63 | String.Backtick: "#4e9a06", # class: 'sb' 64 | String.Char: "#4e9a06", # class: 'sc' 65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 66 | String.Double: "#4e9a06", # class: 's2' 67 | String.Escape: "#4e9a06", # class: 'se' 68 | String.Heredoc: "#4e9a06", # class: 'sh' 69 | String.Interpol: "#4e9a06", # class: 'si' 70 | String.Other: "#4e9a06", # class: 'sx' 71 | String.Regex: "#4e9a06", # class: 'sr' 72 | String.Single: "#4e9a06", # class: 's1' 73 | String.Symbol: "#4e9a06", # class: 'ss' 74 | 75 | Generic: "#000000", # class: 'g' 76 | Generic.Deleted: "#a40000", # class: 'gd' 77 | Generic.Emph: "italic #000000", # class: 'ge' 78 | Generic.Error: "#ef2929", # class: 'gr' 79 | Generic.Heading: "bold #000080", # class: 'gh' 80 | Generic.Inserted: "#00A000", # class: 'gi' 81 | Generic.Output: "#888", # class: 'go' 82 | Generic.Prompt: "#745334", # class: 'gp' 83 | Generic.Strong: "bold #000000", # class: 'gs' 84 | Generic.Subheading: "bold #800080", # class: 'gu' 85 | Generic.Traceback: "bold #a40000", # class: 'gt' 86 | } 87 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | import flask 2 | import flask.ext.pymongo 3 | import util 4 | import warnings 5 | 6 | 7 | class CustomDict(dict): 8 | pass 9 | 10 | 11 | class FlaskPyMongoConfigTest(util.FlaskRequestTest): 12 | 13 | def setUp(self): 14 | self.app = flask.Flask('test') 15 | self.context = self.app.test_request_context('/') 16 | self.context.push() 17 | 18 | def tearDown(self): 19 | self.context.pop() 20 | 21 | def test_default_config_prefix(self): 22 | self.app.config['MONGO_DBNAME'] = 'flask_pymongo_test_db' 23 | self.app.config['MONGO_HOST'] = 'localhost' 24 | self.app.config['MONGO_PORT'] = 27017 25 | 26 | mongo = flask.ext.pymongo.PyMongo(self.app) 27 | assert mongo.db.name == 'flask_pymongo_test_db', 'wrong dbname: %s' % mongo.db.name 28 | assert mongo.cx.host == 'localhost' 29 | assert mongo.cx.port == 27017 30 | 31 | def test_custom_config_prefix(self): 32 | self.app.config['CUSTOM_DBNAME'] = 'flask_pymongo_test_db' 33 | self.app.config['CUSTOM_HOST'] = 'localhost' 34 | self.app.config['CUSTOM_PORT'] = 27017 35 | 36 | mongo = flask.ext.pymongo.PyMongo(self.app, 'CUSTOM') 37 | assert mongo.db.name == 'flask_pymongo_test_db', 'wrong dbname: %s' % mongo.db.name 38 | assert mongo.cx.host == 'localhost' 39 | assert mongo.cx.port == 27017 40 | 41 | def test_converts_str_to_int(self): 42 | self.app.config['MONGO_DBNAME'] = 'flask_pymongo_test_db' 43 | self.app.config['MONGO_HOST'] = 'localhost' 44 | self.app.config['MONGO_PORT'] = '27017' 45 | 46 | mongo = flask.ext.pymongo.PyMongo(self.app) 47 | assert mongo.db.name == 'flask_pymongo_test_db', 'wrong dbname: %s' % mongo.db.name 48 | assert mongo.cx.host == 'localhost' 49 | assert mongo.cx.port == 27017 50 | 51 | def test_rejects_invalid_string(self): 52 | self.app.config['MONGO_PORT'] = '27017x' 53 | 54 | self.assertRaises(TypeError, flask.ext.pymongo.PyMongo, self.app) 55 | 56 | def test_multiple_pymongos(self): 57 | for prefix in ('ONE', 'TWO'): 58 | self.app.config['%s_DBNAME' % prefix] = prefix 59 | 60 | for prefix in ('ONE', 'TWO'): 61 | flask.ext.pymongo.PyMongo(self.app, config_prefix=prefix) 62 | 63 | # this test passes if it raises no exceptions 64 | 65 | def test_config_with_uri(self): 66 | self.app.config['MONGO_URI'] = 'mongodb://localhost:27017/flask_pymongo_test_db' 67 | 68 | with warnings.catch_warnings(): 69 | # URI connections without a username and password 70 | # work, but warn that auth should be supplied 71 | warnings.simplefilter('ignore') 72 | mongo = flask.ext.pymongo.PyMongo(self.app) 73 | assert mongo.db.name == 'flask_pymongo_test_db', 'wrong dbname: %s' % mongo.db.name 74 | assert mongo.cx.host == 'localhost' 75 | assert mongo.cx.port == 27017 76 | 77 | def test_config_with_uri_no_port(self): 78 | self.app.config['MONGO_URI'] = 'mongodb://localhost/flask_pymongo_test_db' 79 | 80 | with warnings.catch_warnings(): 81 | # URI connections without a username and password 82 | # work, but warn that auth should be supplied 83 | warnings.simplefilter('ignore') 84 | mongo = flask.ext.pymongo.PyMongo(self.app) 85 | assert mongo.db.name == 'flask_pymongo_test_db', 'wrong dbname: %s' % mongo.db.name 86 | assert mongo.cx.host == 'localhost' 87 | assert mongo.cx.port == 27017 88 | 89 | def test_config_with_document_class(self): 90 | self.app.config['MONGO_DOCUMENT_CLASS'] = CustomDict 91 | mongo = flask.ext.pymongo.PyMongo(self.app) 92 | assert mongo.cx.document_class == CustomDict 93 | 94 | def test_config_without_document_class(self): 95 | mongo = flask.ext.pymongo.PyMongo(self.app) 96 | assert mongo.cx.document_class == dict 97 | 98 | 99 | class CustomDocumentClassTest(util.FlaskPyMongoTest): 100 | """ Class that tests reading from DB with custom document_class """ 101 | def test_create_with_document_class(self): 102 | """ This test doesn't use self.mongo, because it has to change config 103 | 104 | It uses second mongo connection, using a CUSTOM prefix to avoid 105 | duplicate config_prefix exception. To make use of tearDown and thus DB 106 | deletion even in case of failure, it uses same DBNAME. 107 | 108 | """ 109 | # copying standard DBNAME, so this DB gets also deleted by tearDown 110 | self.app.config['CUSTOM_DBNAME'] = self.app.config['MONGO_DBNAME'] 111 | self.app.config['CUSTOM_DOCUMENT_CLASS'] = CustomDict 112 | # not using self.mongo, because we want to use updated config 113 | # also using CUSTOM, to avoid duplicate config_prefix exception 114 | mongo = flask.ext.pymongo.PyMongo(self.app, 'CUSTOM') 115 | assert mongo.db.things.find_one() == None 116 | # write document and retrieve, to check if type is really CustomDict 117 | mongo.db.things.insert({'_id': 'thing', 'val': 'foo'}, safe=True) 118 | assert type(mongo.db.things.find_one()) == CustomDict 119 | 120 | def test_create_without_document_class(self): 121 | """ This uses self.mongo, which uses config without document_class """ 122 | assert self.mongo.db.things.find_one() == None 123 | # write document and retrieve, to check if type is dict (default) 124 | self.mongo.db.things.insert({'_id': 'thing', 'val': 'foo'}, safe=True) 125 | assert type(self.mongo.db.things.find_one()) == dict 126 | -------------------------------------------------------------------------------- /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/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 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-PyMongo.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-PyMongo.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-PyMongo" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-PyMongo" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Flask-PyMongo documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Dec 26 10:16:15 2011. 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 sys, os 15 | sys.path.insert(0, os.path.abspath('..')) 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | #sys.path.insert(0, os.path.abspath('.')) 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.autodoc'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'Flask-PyMongo' 45 | copyright = u'2011, Dan Crosta' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | version = '0.3.0' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '0.3.0' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | html_theme = 'flask_small' 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | #html_theme_options = {} 100 | 101 | # Add any paths that contain custom themes here, relative to this directory. 102 | sys.path.append(os.path.abspath('_themes')) 103 | html_theme_path = ['_themes'] 104 | 105 | # custom settings for flask theme 106 | html_theme_options = { 107 | 'index_logo': '', #TODO 108 | 'github_fork': 'dcrosta/flask-pymongo', 109 | } 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 = 'Flask-PyMongodoc' 175 | 176 | 177 | # -- Options for LaTeX output -------------------------------------------------- 178 | 179 | latex_elements = { 180 | # The paper size ('letterpaper' or 'a4paper'). 181 | #'papersize': 'letterpaper', 182 | 183 | # The font size ('10pt', '11pt' or '12pt'). 184 | #'pointsize': '10pt', 185 | 186 | # Additional stuff for the LaTeX preamble. 187 | #'preamble': '', 188 | } 189 | 190 | # Grouping the document tree into LaTeX files. List of tuples 191 | # (source start file, target name, title, author, documentclass [howto/manual]). 192 | latex_documents = [ 193 | ('index', 'Flask-PyMongo.tex', u'Flask-PyMongo Documentation', 194 | u'Dan Crosta', 'manual'), 195 | ] 196 | 197 | # The name of an image file (relative to this directory) to place at the top of 198 | # the title page. 199 | #latex_logo = None 200 | 201 | # For "manual" documents, if this is true, then toplevel headings are parts, 202 | # not chapters. 203 | #latex_use_parts = False 204 | 205 | # If true, show page references after internal links. 206 | #latex_show_pagerefs = False 207 | 208 | # If true, show URL addresses after external links. 209 | #latex_show_urls = False 210 | 211 | # Documents to append as an appendix to all manuals. 212 | #latex_appendices = [] 213 | 214 | # If false, no module index is generated. 215 | #latex_domain_indices = True 216 | 217 | 218 | # -- Options for manual page output -------------------------------------------- 219 | 220 | # One entry per manual page. List of tuples 221 | # (source start file, name, description, authors, manual section). 222 | man_pages = [ 223 | ('index', 'flask-pymongo', u'Flask-PyMongo Documentation', 224 | [u'Dan Crosta'], 1) 225 | ] 226 | 227 | # If true, show URL addresses after external links. 228 | #man_show_urls = False 229 | 230 | 231 | # -- Options for Texinfo output ------------------------------------------------ 232 | 233 | # Grouping the document tree into Texinfo files. List of tuples 234 | # (source start file, target name, title, author, 235 | # dir menu entry, description, category) 236 | texinfo_documents = [ 237 | ('index', 'Flask-PyMongo', u'Flask-PyMongo Documentation', 238 | u'Dan Crosta', 'Flask-PyMongo', 'One line description of project.', 239 | 'Miscellaneous'), 240 | ] 241 | 242 | # Documents to append as an appendix to all manuals. 243 | #texinfo_appendices = [] 244 | 245 | # If false, no module index is generated. 246 | #texinfo_domain_indices = True 247 | 248 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 249 | #texinfo_show_urls = 'footnote' 250 | 251 | 252 | # Example configuration for intersphinx: refer to the Python standard library. 253 | intersphinx_mapping = { 254 | 'python': ('http://docs.python.org/', None), 255 | 'flask': ('http://flask.pocoo.org/docs/', None), 256 | 'pymongo': ('http://api.mongodb.org/python/current/', None), 257 | } 258 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Flask-PyMongo 2 | ============= 3 | 4 | `MongoDB `_ is an open source database that stores 5 | flexible JSON-like "documents," which can have any number, name, or 6 | hierarchy of fields within, instead of rows of data as in a relational 7 | database. Python developers can think of MongoDB as a persistent, searchable 8 | repository of Python dictionaries (and, in fact, this is how `PyMongo 9 | `_ represents MongoDB documents). 10 | 11 | Flask-PyMongo bridges Flask and PyMongo, so that you can use Flask's normal 12 | mechanisms to configure and connect to MongoDB. 13 | 14 | 15 | Quickstart 16 | ---------- 17 | 18 | First, install Flask-PyMongo: 19 | 20 | .. code-block:: bash 21 | 22 | $ pip install Flask-PyMongo 23 | 24 | Flask-PyMongo depends, and will install for you, recent versions of Flask 25 | (0.8 or later) and PyMongo (2.4 or later). Flask-PyMongo is compatible 26 | with and tested on Python 2.6, 2.7, and 3.3. 27 | 28 | Next, add a :class:`~flask_pymongo.PyMongo` to your code: 29 | 30 | .. code-block:: python 31 | 32 | from flask import Flask 33 | from flask.ext.pymongo import PyMongo 34 | 35 | app = Flask(__name__) 36 | mongo = PyMongo(app) 37 | 38 | :class:`~flask_pymongo.PyMongo` connects to the MongoDB server running on 39 | port 27017 on localhost, and assumes a default database name of ``app.name`` 40 | (i.e. whatever name you pass to :class:`~flask.Flask`). This database is 41 | exposed as the :attr:`~flask_pymongo.PyMongo.db` attribute. 42 | 43 | You can use :attr:`~flask_pymongo.PyMongo.db` directly in views: 44 | 45 | .. code-block:: python 46 | 47 | @app.route('/') 48 | def home_page(): 49 | online_users = mongo.db.users.find({'online': True}) 50 | return render_template('index.html', 51 | online_users=online_users) 52 | 53 | 54 | Helpers 55 | ------- 56 | 57 | Flask-PyMongo provides helpers for some common tasks: 58 | 59 | .. automethod:: flask_pymongo.wrappers.Collection.find_one_or_404 60 | 61 | .. automethod:: flask_pymongo.PyMongo.send_file 62 | 63 | .. automethod:: flask_pymongo.PyMongo.save_file 64 | 65 | .. autoclass:: flask_pymongo.BSONObjectIdConverter 66 | 67 | Configuration 68 | ------------- 69 | 70 | :class:`~flask_pymongo.PyMongo` understands the following configuration 71 | directives: 72 | 73 | ============================ =================================================== 74 | ``MONGO_URI`` A `MongoDB URI 75 | `_ 76 | which is used in preference of the other 77 | configuration variables. 78 | ``MONGO_HOST`` The host name or IP address of your MongoDB server. 79 | Default: "localhost". 80 | ``MONGO_PORT`` The port number of your MongoDB server. Default: 81 | 27017. 82 | ``MONGO_AUTO_START_REQUEST`` Set to ``False`` to disable PyMongo 2.2's "auto 83 | start request" behavior (see 84 | :class:`~pymongo.mongo_client.MongoClient`). 85 | Default: ``True``. 86 | ``MONGO_MAX_POOL_SIZE`` (optional): The maximum number of idle connections 87 | maintained in the PyMongo connection pool. 88 | Default: PyMongo default. 89 | ``MONGO_SOCKET_TIMEOUT_MS`` (optional): (integer) How long (in milliseconds) a send 90 | or receive on a socket can take before timing out. 91 | Default: PyMongo default. 92 | ``MONGO_CONNECT_TIMEOUT_MS`` (optional): (integer) How long (in milliseconds) a 93 | connection can take to be opened before timing out. 94 | Default: PyMongo default. 95 | ``MONGO_DBNAME`` The database name to make available as the ``db`` 96 | attribute. Default: ``app.name``. 97 | ``MONGO_USERNAME`` The user name for authentication. Default: ``None`` 98 | ``MONGO_PASSWORD`` The password for authentication. Default: ``None`` 99 | ``MONGO_REPLICA_SET`` The name of a replica set to connect to; this must 100 | match the internal name of the replica set (as 101 | deteremined by the `isMaster 102 | `_ 103 | command). Default: ``None``. 104 | ``MONGO_READ_PREFERENCE`` Determines how read queries are routed to the 105 | replica set members. Must be one of the 106 | constants defined on 107 | :class:`pymongo.read_preferences.ReadPreference` or 108 | the string names thereof 109 | ``MONGO_DOCUMENT_CLASS`` This tells pymongo to return custom objects instead 110 | of dicts, for example ``bson.son.SON``. Default: ``dict`` 111 | ============================ =================================================== 112 | 113 | When :class:`~flask_pymongo.PyMongo` or 114 | :meth:`~flask_pymongo.PyMongo.init_app` are invoked with only one argument 115 | (the :class:`~flask.Flask` instance), a configuration value prefix of 116 | ``MONGO`` is assumed; this can be overridden with the `config_prefix` 117 | argument. 118 | 119 | This technique can be used to connect to multiple databases or database 120 | servers: 121 | 122 | .. code-block:: python 123 | 124 | app = Flask(__name__) 125 | 126 | # connect to MongoDB with the defaults 127 | mongo1 = PyMongo(app) 128 | 129 | # connect to another MongoDB database on the same host 130 | app.config['MONGO2_DBNAME'] = 'dbname_two' 131 | mongo2 = PyMongo(app, config_prefix='MONGO2') 132 | 133 | # connect to another MongoDB server altogether 134 | app.config['MONGO3_HOST'] = 'another.host.example.com' 135 | app.config['MONGO3_PORT'] = 27017 136 | app.config['MONGO3_DBNAME'] = 'dbname_three' 137 | mongo3 = PyMongo(app, config_prefix='MONGO3') 138 | 139 | Some auto-configured settings that you should be aware of are: 140 | 141 | ``tz_aware``: 142 | Flask-PyMongo always uses timezone-aware :class:`~datetime.datetime` 143 | objects. That is, it sets the ``tz_aware`` parameter to ``True`` when 144 | creating a connection. The timezone of :class:`~datetime.datetime` 145 | objects returned from MongoDB will always be UTC. 146 | 147 | ``safe``: 148 | Flask-PyMongo sets "safe" mode by default, which causes 149 | :meth:`~pymongo.collection.Collection.save`, 150 | :meth:`~pymongo.collection.Collection.insert`, 151 | :meth:`~pymongo.collection.Collection.update`, and 152 | :meth:`~pymongo.collection.Collection.remove` to wait for acknowledgement 153 | from the server before returning. You may override this on a per-call 154 | basis by passing the keyword argument ``safe=False`` to any of the 155 | effected methods. 156 | 157 | 158 | API 159 | === 160 | 161 | Constants 162 | --------- 163 | 164 | .. autodata:: flask_pymongo.ASCENDING 165 | 166 | .. autodata:: flask_pymongo.DESCENDING 167 | 168 | 169 | Classes 170 | ------- 171 | 172 | .. autoclass:: flask_pymongo.PyMongo 173 | :members: 174 | 175 | .. autoclass:: flask_pymongo.wrappers.Collection 176 | :members: 177 | 178 | 179 | Wrappers 180 | -------- 181 | 182 | These classes exist solely in order to make expressions such as 183 | ``mongo.db.foo.bar`` evaluate to a 184 | :class:`~flask_pymongo.wrappers.Collection` instance instead of 185 | a :class:`pymongo.collection.Collection` instance. They are documented here 186 | solely for completeness. 187 | 188 | .. autoclass:: flask_pymongo.wrappers.MongoClient 189 | :members: 190 | 191 | .. autoclass:: flask_pymongo.wrappers.MongoReplicaSetClient 192 | :members: 193 | 194 | .. autoclass:: flask_pymongo.wrappers.Database 195 | :members: 196 | 197 | 198 | History and Contributors 199 | ------------------------ 200 | 201 | Changes: 202 | 203 | - 0.3.0: July 4, 2013 204 | 205 | - This is a minor version bump which introduces backwards breaking 206 | changes! Please read these change notes carefully. 207 | - Removed read preference constants from Flask-PyMongo; to set a 208 | read preference, use the string name or import contants directly 209 | from :class:`pymongo.read_preferences.ReadPreference`. 210 | - `#22 (partial) `_ 211 | Add support for ``MONGO_SOCKET_TIMEOUT_MS`` and 212 | ``MONGO_CONNECT_TIMEOUT_MS`` options (ultrabug). 213 | - `#27 (partial) `_ 214 | Make Flask-PyMongo compatible with Python 3 (Vizzy). 215 | 216 | - 0.2.1: December 22, 2012 217 | 218 | - `#19 `_ Added 219 | ``MONGO_DOCUMENT_CLASS`` config option (jeverling). 220 | 221 | - 0.2.0: December 15, 2012 222 | 223 | - This is a minor version bump which may introduce backwards breaking 224 | changes! Please read these change notes carefully. 225 | - `#17 `_ Now using 226 | PyMongo 2.4's ``MongoClient`` and ``MongoReplicaSetClient`` objects 227 | instead of ``Connection`` and ``ReplicaSetConnection`` classes 228 | (tang0th). 229 | - `#17 `_ Now requiring 230 | at least PyMongo version 2.4 (tang0th). 231 | - `#17 `_ The wrapper 232 | class ``flask_pymongo.wrappers.Connection`` is renamed to 233 | ``flask_pymongo.wrappers.MongoClient`` (tang0th). 234 | - `#17 `_ The wrapper 235 | class ``flask_pymongo.wrappers.ReplicaSetConnection`` is renamed to 236 | ``flask_pymongo.wrappers.MongoReplicaSetClient`` (tang0th). 237 | - `#18 `_ 238 | ``MONGO_AUTO_START_REQUEST`` now defaults to ``False`` when 239 | connecting using a URI. 240 | 241 | - 0.1.4: December 15, 2012 242 | 243 | - `#15 `_ Added support 244 | for ``MONGO_MAX_POOL_SIZE`` (Fabrice Aneche) 245 | 246 | - 0.1.3: September 22, 2012 247 | 248 | - Added support for configuration from MongoDB URI. 249 | 250 | - 0.1.2: June 18, 2012 251 | 252 | - Updated wiki example application 253 | - `#14 `_ Added 254 | examples and docs to PyPI package. 255 | 256 | - 0.1.1: May 26, 2012 257 | 258 | - Added support for PyMongo 2.2's "auto start request" feature, by way 259 | of the ``MONGO_AUTO_START_REQUEST`` configuration flag. 260 | - `#13 `_ Added 261 | BSONObjectIdConverter (Christoph Herr) 262 | - `#12 `_ Corrected 263 | documentation typo (Thor Adam) 264 | 265 | - 0.1: December 21, 2011 266 | 267 | - Initial Release 268 | 269 | 270 | Contributors: 271 | 272 | - `jeverling `_ 273 | - `tang0th `_ 274 | - `Fabrice Aneche `_ 275 | - `Thor Adam `_ 276 | - `Christoph Herr `_ 277 | 278 | -------------------------------------------------------------------------------- /flask_pymongo/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Dan Crosta 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 met: 6 | # 7 | # * Redistributions of source code must retain the above copyright notice, 8 | # this list of conditions and the following disclaimer. 9 | # 10 | # * Redistributions in binary form must reproduce the above copyright notice, 11 | # this list of conditions and the following disclaimer in the documentation 12 | # and/or other materials provided with the distribution. 13 | # 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | # POSSIBILITY OF SUCH DAMAGE. 25 | 26 | 27 | __all__ = ('PyMongo', 'ASCENDING', 'DESCENDING') 28 | 29 | from bson.errors import InvalidId 30 | from bson.objectid import ObjectId 31 | from flask import abort, current_app, request 32 | from gridfs import GridFS, NoFile 33 | from mimetypes import guess_type 34 | from pymongo import uri_parser 35 | from werkzeug.wsgi import wrap_file 36 | from werkzeug.routing import BaseConverter 37 | from pymongo.read_preferences import ReadPreference 38 | import pymongo 39 | import warnings 40 | 41 | from flask_pymongo.wrappers import MongoClient 42 | from flask_pymongo.wrappers import MongoReplicaSetClient 43 | 44 | import sys 45 | 46 | PY2 = sys.version_info[0] == 2 47 | 48 | # Python 3 compatibility 49 | if PY2: 50 | text_type = (str, unicode) 51 | num_type = (int, long) 52 | else: 53 | text_type = str 54 | num_type = int 55 | 56 | 57 | DESCENDING = pymongo.DESCENDING 58 | """Descending sort order.""" 59 | 60 | ASCENDING = pymongo.ASCENDING 61 | """Ascending sort order.""" 62 | 63 | 64 | class BSONObjectIdConverter(BaseConverter): 65 | """A simple converter for the RESTful URL routing system of Flask. 66 | 67 | .. code-block:: python 68 | 69 | @app.route('/') 70 | def show_task(task_id): 71 | task = mongo.db.tasks.find_one_or_404(task_id) 72 | return render_template('task.html', task=task) 73 | 74 | Valid object ID strings are converted into 75 | :class:`~bson.objectid.ObjectId` objects; invalid strings result 76 | in a 404 error. The converter is automatically registered by the 77 | initialization of :class:`~flask_pymongo.PyMongo` with keyword 78 | :attr:`ObjectId`. 79 | """ 80 | 81 | def to_python(self, value): 82 | try: 83 | return ObjectId(value) 84 | except InvalidId: 85 | raise abort(400) 86 | 87 | def to_url(self, value): 88 | return str(value) 89 | 90 | 91 | class PyMongo(object): 92 | """Automatically connects to MongoDB using parameters defined in Flask 93 | configuration. 94 | """ 95 | 96 | def __init__(self, app=None, config_prefix='MONGO'): 97 | if app is not None: 98 | self.init_app(app, config_prefix) 99 | 100 | def init_app(self, app, config_prefix='MONGO'): 101 | """Initialize the `app` for use with this :class:`~PyMongo`. This is 102 | called automatically if `app` is passed to :meth:`~PyMongo.__init__`. 103 | 104 | The app is configured according to the configuration variables 105 | ``PREFIX_HOST``, ``PREFIX_PORT``, ``PREFIX_DBNAME``, 106 | ``PREFIX_AUTO_START_REQUEST``, 107 | ``PREFIX_REPLICA_SET``, ``PREFIX_READ_PREFERENCE``, 108 | ``PREFIX_USERNAME``, ``PREFIX_PASSWORD``, and ``PREFIX_URI`` where 109 | "PREFIX" defaults to "MONGO". If ``PREFIX_URL`` is set, it is 110 | assumed to have all appropriate configurations, and the other 111 | keys are overwritten using their values as present in the URI. 112 | 113 | :param flask.Flask app: the application to configure for use with 114 | this :class:`~PyMongo` 115 | :param str config_prefix: determines the set of configuration 116 | variables used to configure this :class:`~PyMongo` 117 | """ 118 | if 'pymongo' not in app.extensions: 119 | app.extensions['pymongo'] = {} 120 | 121 | if config_prefix in app.extensions['pymongo']: 122 | raise Exception('duplicate config_prefix "%s"' % config_prefix) 123 | 124 | self.config_prefix = config_prefix 125 | def key(suffix): 126 | return '%s_%s' % (config_prefix, suffix) 127 | 128 | if key('URI') in app.config: 129 | # bootstrap configuration from the URL 130 | parsed = uri_parser.parse_uri(app.config[key('URI')]) 131 | if 'database' not in parsed or not parsed['database']: 132 | raise ValueError('MongoDB URI does not contain database name') 133 | app.config[key('DBNAME')] = parsed['database'] 134 | app.config[key('READ_PREFERENCE')] = parsed['options'].get('read_preference') 135 | app.config[key('AUTO_START_REQUEST')] = parsed['options'].get('auto_start_request', True) 136 | app.config[key('USERNAME')] = parsed['username'] 137 | app.config[key('PASSWORD')] = parsed['password'] 138 | app.config[key('REPLICA_SET')] = parsed['options'].get('replica_set') 139 | app.config[key('MAX_POOL_SIZE')] = parsed['options'].get('max_pool_size') 140 | app.config[key('SOCKET_TIMEOUT_MS')] = parsed['options'].get('socket_timeout_ms', None) 141 | app.config[key('CONNECT_TIMEOUT_MS')] = parsed['options'].get('connect_timeout_ms', None) 142 | 143 | # we will use the URI for connecting instead of HOST/PORT 144 | app.config.pop(key('HOST'), None) 145 | app.config.pop(key('PORT'), None) 146 | host = app.config[key('URI')] 147 | 148 | else: 149 | app.config.setdefault(key('HOST'), 'localhost') 150 | app.config.setdefault(key('PORT'), 27017) 151 | app.config.setdefault(key('DBNAME'), app.name) 152 | app.config.setdefault(key('READ_PREFERENCE'), None) 153 | app.config.setdefault(key('AUTO_START_REQUEST'), True) 154 | app.config.setdefault(key('SOCKET_TIMEOUT_MS'), None) 155 | app.config.setdefault(key('CONNECT_TIMEOUT_MS'), None) 156 | 157 | # these don't have defaults 158 | app.config.setdefault(key('USERNAME'), None) 159 | app.config.setdefault(key('PASSWORD'), None) 160 | app.config.setdefault(key('REPLICA_SET'), None) 161 | app.config.setdefault(key('MAX_POOL_SIZE'), None) 162 | 163 | try: 164 | port = int(app.config[key('PORT')]) 165 | except ValueError: 166 | raise TypeError('%s_PORT must be an integer' % config_prefix) 167 | 168 | host = '%s:%s' % (app.config[key('HOST')], app.config[key('PORT')]) 169 | 170 | username = app.config[key('USERNAME')] 171 | password = app.config[key('PASSWORD')] 172 | auth = (username, password) 173 | 174 | if any(auth) and not all(auth): 175 | raise Exception('Must set both USERNAME and PASSWORD or neither') 176 | 177 | read_preference = app.config[key('READ_PREFERENCE')] 178 | if isinstance(read_preference, text_type): 179 | # Assume the string to be the name of the read 180 | # preference, and look it up from PyMongo 181 | read_preference = getattr(ReadPreference, read_preference) 182 | if read_preference is None: 183 | raise ValueError( 184 | '%s_READ_PREFERENCE: No such read preference name (%r)' % ( 185 | config_prefix, read_pref)) 186 | app.config[key('READ_PREFERENCE')] = read_preference 187 | # Else assume read_preference is already a valid constant 188 | # from pymongo.read_preferences.ReadPreference or None 189 | 190 | replica_set = app.config[key('REPLICA_SET')] 191 | dbname = app.config[key('DBNAME')] 192 | auto_start_request = app.config[key('AUTO_START_REQUEST')] 193 | max_pool_size = app.config[key('MAX_POOL_SIZE')] 194 | socket_timeout_ms = app.config[key('SOCKET_TIMEOUT_MS')] 195 | connect_timeout_ms = app.config[key('CONNECT_TIMEOUT_MS')] 196 | 197 | # document class is not supported by URI, using setdefault in all cases 198 | document_class = app.config.setdefault(key('DOCUMENT_CLASS'), None) 199 | 200 | if auto_start_request not in (True, False): 201 | raise TypeError('%s_AUTO_START_REQUEST must be a bool' % config_prefix) 202 | 203 | args = [host] 204 | kwargs = { 205 | 'auto_start_request': auto_start_request, 206 | 'tz_aware': True, 207 | } 208 | 209 | if read_preference is not None: 210 | kwargs['read_preference'] = read_preference 211 | 212 | if socket_timeout_ms is not None: 213 | kwargs['socketTimeoutMS'] = socket_timeout_ms 214 | 215 | if connect_timeout_ms is not None: 216 | kwargs['connectTimeoutMS'] = connect_timeout_ms 217 | 218 | if replica_set is not None: 219 | kwargs['replicaSet'] = replica_set 220 | connection_cls = MongoReplicaSetClient 221 | else: 222 | connection_cls = MongoClient 223 | 224 | if max_pool_size is not None: 225 | kwargs['max_pool_size'] = max_pool_size 226 | 227 | if document_class is not None: 228 | kwargs['document_class'] = document_class 229 | 230 | cx = connection_cls(*args, **kwargs) 231 | db = cx[dbname] 232 | 233 | if any(auth): 234 | db.authenticate(username, password) 235 | 236 | app.extensions['pymongo'][config_prefix] = (cx, db) 237 | app.url_map.converters['ObjectId'] = BSONObjectIdConverter 238 | 239 | @property 240 | def cx(self): 241 | """The automatically created 242 | :class:`~flask_pymongo.wrappers.Connection` or 243 | :class:`~flask_pymongo.wrappers.ReplicaSetConnection` 244 | object. 245 | """ 246 | if self.config_prefix not in current_app.extensions['pymongo']: 247 | raise Exception('not initialized. did you forget to call init_app?') 248 | return current_app.extensions['pymongo'][self.config_prefix][0] 249 | 250 | @property 251 | def db(self): 252 | """The automatically created 253 | :class:`~flask_pymongo.wrappers.Database` object 254 | corresponding to the provided ``MONGO_DBNAME`` configuration 255 | parameter. 256 | """ 257 | if self.config_prefix not in current_app.extensions['pymongo']: 258 | raise Exception('not initialized. did you forget to call init_app?') 259 | return current_app.extensions['pymongo'][self.config_prefix][1] 260 | 261 | # view helpers 262 | def send_file(self, filename, base='fs', version=-1, cache_for=31536000): 263 | """Return an instance of the :attr:`~flask.Flask.response_class` 264 | containing the named file, and implement conditional GET semantics 265 | (using :meth:`~werkzeug.wrappers.ETagResponseMixin.make_conditional`). 266 | 267 | .. code-block:: python 268 | 269 | @app.route('/uploads/') 270 | def get_upload(filename): 271 | return mongo.send_file(filename) 272 | 273 | :param str filename: the filename of the file to return 274 | :param str base: the base name of the GridFS collections to use 275 | :param bool version: if positive, return the Nth revision of the file 276 | identified by filename; if negative, return the Nth most recent 277 | revision. If no such version exists, return with HTTP status 404. 278 | :param int cache_for: number of seconds that browsers should be 279 | instructed to cache responses 280 | """ 281 | if not isinstance(base, text_type): 282 | raise TypeError('"base" must be string or unicode') 283 | if not isinstance(version, num_type): 284 | raise TypeError('"version" must be an integer') 285 | if not isinstance(cache_for, num_type): 286 | raise TypeError('"cache_for" must be an integer') 287 | 288 | storage = GridFS(self.db, base) 289 | 290 | try: 291 | fileobj = storage.get_version(filename=filename, version=version) 292 | except NoFile: 293 | abort(404) 294 | 295 | 296 | # mostly copied from flask/helpers.py, with 297 | # modifications for GridFS 298 | data = wrap_file(request.environ, fileobj, buffer_size=1024 * 256) 299 | response = current_app.response_class( 300 | data, 301 | mimetype=fileobj.content_type, 302 | direct_passthrough=True) 303 | response.content_length = fileobj.length 304 | response.last_modified = fileobj.upload_date 305 | response.set_etag(fileobj.md5) 306 | response.cache_control.max_age = cache_for 307 | response.cache_control.s_max_age = cache_for 308 | response.cache_control.public = True 309 | response.make_conditional(request) 310 | return response 311 | 312 | def save_file(self, filename, fileobj, base='fs', content_type=None): 313 | """Save the file-like object to GridFS using the given filename. 314 | Returns ``None``. 315 | 316 | .. code-block:: python 317 | 318 | @app.route('/uploads/', methods=['POST']) 319 | def save_upload(filename): 320 | mongo.save_file(filename, request.files['file']) 321 | return redirect(url_for('get_upload', filename=filename)) 322 | 323 | :param str filename: the filename of the file to return 324 | :param file fileobj: the file-like object to save 325 | :param str base: base the base name of the GridFS collections to use 326 | :param str content_type: the MIME content-type of the file. If 327 | ``None``, the content-type is guessed from the filename using 328 | :func:`~mimetypes.guess_type` 329 | """ 330 | if not isinstance(base, text_type): 331 | raise TypeError('"base" must be string or unicode') 332 | if not (hasattr(fileobj, 'read') and callable(fileobj.read)): 333 | raise TypeError('"fileobj" must have read() method') 334 | 335 | if content_type is None: 336 | content_type, _ = guess_type(filename) 337 | 338 | storage = GridFS(self.db, base) 339 | storage.put(fileobj, filename=filename, content_type=content_type) 340 | --------------------------------------------------------------------------------