├── 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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------