Skip to content

Commit 0c80b20

Browse files
author
Len Buckens
committed
Merge remote-tracking branch 'upstream/master'
Conflicts: flask_mongoengine/__init__.py
2 parents a320b13 + 568292d commit 0c80b20

File tree

13 files changed

+215
-121
lines changed

13 files changed

+215
-121
lines changed

README

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ You can find the documentation at http://readthedocs.org/docs/flask-mongoengine/
1717

1818
You can install this package using pypi, $ pip install flask-mongoengine
1919

20+
~ What about the license?
21+
22+
Flask-MongoEngine is distributed under MIT license, see LICENSE for more details

docs/index.rst

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,45 @@ Or, if you are setting up your database before your app is initialized, as is th
3535
app = Flask(__name__)
3636
app.config.from_pyfile('the-config.cfg')
3737
db.init_app(app)
38-
39-
To configure the MongoDB connection settings set a key (DB, USERNAME, PASSWORD, HOST, PORT) in the
40-
`'MONGODB_SETTINGS'` dictionary wih `app.config`. For example :
4138

42-
app.config["MONGODB_SETTINGS"] = {"DB": "my_tumble_log", "USERNAME" : "my_user_name", "PASSWORD" : "my_secret_password", "HOST" : "127.0.0.1", "PORT": 27017 }
4339

40+
By default, Flask-MongoEngine assumes that the :program:`mongod` instance is running
41+
on **localhost** on port **27017**, and you wish to connect to the database named **test**.
42+
43+
If MongoDB is running elsewhere, you should provide the :attr:`host` and :attr:`port` settings
44+
in the `'MONGODB_SETTINGS'` dictionary wih `app.config`.::
45+
46+
app.config['MONGODB_SETTINGS'] = {
47+
'db': 'project1',
48+
'host': '192.168.1.35',
49+
'port': 12345
50+
}
51+
52+
If the database requires authentication, the :attr:`username` and :attr:`password`
53+
arguments should be provided `'MONGODB_SETTINGS'` dictionary wih `app.config`.::
54+
55+
app.config['MONGODB_SETTINGS'] = {
56+
'db': 'project1',
57+
'username':'webapp',
58+
'password':'pwd123'
59+
}
60+
61+
Uri style connections are also supported, just supply the uri as the :attr:`host`
62+
in the `'MONGODB_SETTINGS'` dictionary with `app.config`. **Note that database name from uri has priority over name.** ::
63+
64+
app.config['MONGODB_SETTINGS'] = {
65+
'db': 'project1',
66+
'host': 'mongodb://localhost/database_name'
67+
}
68+
69+
70+
Connection settings may also be provided individually by prefixing the setting with `'MONGODB_'` in the `app.config`.::
71+
72+
app.config['MONGODB_DB'] = 'project1'
73+
app.config['MONGODB_HOST'] = '192.168.1.35'
74+
app.config['MONGODB_PORT'] = 12345
75+
app.config['MONGODB_USERNAME'] = 'webapp'
76+
app.config['MONGODB_PASSWORD'] = 'pwd123'
4477

4578

4679
Custom Queryset
@@ -121,7 +154,7 @@ You can use MongoEngine and WTForms like so::
121154
if request.method == 'POST' and form.validate():
122155
# do something
123156
redirect('done')
124-
return render_response('add_post.html', form=form)
157+
return render_template('add_post.html', form=form)
125158

126159

127160
Supported fields

examples/simpleapp/app.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ def index():
4848
Todo(title="Simple todo A ПЫЩЬ!", text="12345678910").save() # Insert
4949
Todo(title="Simple todo B", text="12345678910").save() # Insert
5050
Todo.objects(title__contains="B").update(set__text="Hello world") # Update
51-
todos = list(Todo.objects[:10])
5251
todos = Todo.objects.all()
5352
return flask.render_template('index.html', todos=todos)
5453

flask_mongoengine/__init__.py

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import
33

4-
from flask import abort
4+
from flask import abort, current_app
55

66
import mongoengine
77

88
from mongoengine.queryset import MultipleObjectsReturned, DoesNotExist, QuerySet
99
from mongoengine.base import ValidationError
1010

11+
from pymongo import uri_parser
12+
1113
from .sessions import *
1214
from .pagination import *
1315
from .json import overide_json_encoder
@@ -20,55 +22,81 @@ def _include_mongoengine(obj):
2022
setattr(obj, key, getattr(module, key))
2123

2224

25+
def _create_connection(conn_settings):
26+
27+
# Handle multiple connections recursively
28+
if isinstance(conn_settings, list):
29+
connections = {}
30+
for conn in conn_settings:
31+
connections[conn.get('alias')] = _create_connection(conn)
32+
return connections
33+
34+
conn = dict([(k.lower(), v) for k, v in conn_settings.items() if v])
35+
36+
if 'replicaset' in conn:
37+
conn['replicaSet'] = conn.pop('replicaset')
38+
39+
# Handle uri style connections
40+
if "://" in conn.get('host', ''):
41+
uri_dict = uri_parser.parse_uri(conn_settings['host'])
42+
conn['db'] = uri_dict['database']
43+
44+
return mongoengine.connect(conn.pop('db', 'test'), **conn)
45+
46+
2347
class MongoEngine(object):
2448

25-
def __init__(self, app=None):
49+
def __init__(self, app=None, config=None):
2650

2751
_include_mongoengine(self)
2852

2953
self.Document = Document
3054
self.DynamicDocument = DynamicDocument
3155

3256
if app is not None:
33-
self.init_app(app)
57+
self.init_app(app, config)
3458

35-
def init_app(self, app):
59+
def init_app(self, app, config=None):
3660

37-
conn_settings = app.config.get('MONGODB_SETTINGS', None)
61+
app.extensions = getattr(app, 'extensions', {})
3862

39-
if not conn_settings:
40-
conn_settings = {
41-
'db': app.config.get('MONGODB_DB', None),
42-
'username': app.config.get('MONGODB_USERNAME', None),
43-
'password': app.config.get('MONGODB_PASSWORD', None),
44-
'host': app.config.get('MONGODB_HOST', None),
45-
'port': int(app.config.get('MONGODB_PORT', 0)) or None
46-
}
63+
# Make documents JSON serializable
64+
overide_json_encoder(app)
4765

48-
if isinstance(conn_settings, list):
49-
self.connection = {}
50-
for conn in conn_settings:
51-
conn = dict((k.lower(), v) for k, v in conn.items() if v is not None)
66+
if not 'mongoengine' in app.extensions:
67+
app.extensions['mongoengine'] = {}
5268

53-
if 'replicaset' in conn:
54-
conn['replicaSet'] = conn['replicaset']
55-
del conn['replicaset']
69+
if self in app.extensions['mongoengine']:
70+
# Raise an exception if extension already initialized as
71+
# potentially new configuration would not be loaded.
72+
raise Exception('Extension already initialized')
5673

57-
self.connection[conn.get('alias')] = mongoengine.connect(**conn)
74+
if not config:
75+
# If not passed a config then we read the connection settings
76+
# from the app config.
77+
config = app.config
5878

79+
if 'MONGODB_SETTINGS' in config:
80+
# Connection settings provided as a dictionary.
81+
connection = _create_connection(config['MONGODB_SETTINGS'])
5982
else:
60-
conn_settings = dict([(k.lower(), v) for k, v in conn_settings.items() if v])
61-
62-
if 'replicaset' in conn_settings:
63-
conn_settings['replicaSet'] = conn_settings['replicaset']
64-
del conn_settings['replicaset']
65-
66-
self.connection = mongoengine.connect(**conn_settings)
67-
68-
app.extensions = getattr(app, 'extensions', {})
69-
app.extensions['mongoengine'] = self
70-
self.app = app
71-
overide_json_encoder(app)
83+
# Connection settings provided in standard format.
84+
settings = {'alias': config.get('MONGODB_ALIAS', None),
85+
'db': config.get('MONGODB_DB', None),
86+
'host': config.get('MONGODB_HOST', None),
87+
'password': config.get('MONGODB_PASSWORD', None),
88+
'port': config.get('MONGODB_PORT', None),
89+
'username': config.get('MONGODB_USERNAME', None)}
90+
connection = _create_connection(settings)
91+
92+
# Store objects in application instance so that multiple apps do
93+
# not end up accessing the same objects.
94+
app.extensions['mongoengine'][self] = {'app': app,
95+
'conn': connection}
96+
97+
@property
98+
def connection(self):
99+
return current_app.extensions['mongoengine'][self]['conn']
72100

73101

74102
class BaseQuerySet(QuerySet):

flask_mongoengine/sessions.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from flask.sessions import SessionInterface, SessionMixin
66
from werkzeug.datastructures import CallbackDict
7+
from bson.tz_util import utc
78

89
__all__ = ("MongoEngineSession", "MongoEngineSessionInterface")
910

@@ -57,21 +58,31 @@ def open_session(self, app, request):
5758
sid = request.cookies.get(app.session_cookie_name)
5859
if sid:
5960
stored_session = self.cls.objects(sid=sid).first()
60-
if stored_session and stored_session.expiration > datetime.datetime.utcnow():
61-
return MongoEngineSession(initial=stored_session.data, sid=stored_session.sid)
61+
62+
if stored_session:
63+
expiration = stored_session.expiration
64+
65+
if not expiration.tzinfo:
66+
expiration = expiration.replace(tzinfo=utc)
67+
68+
if expiration > datetime.datetime.utcnow().replace(tzinfo=utc):
69+
return MongoEngineSession(initial=stored_session.data, sid=stored_session.sid)
70+
6271
return MongoEngineSession(sid=str(uuid.uuid4()))
6372

6473
def save_session(self, app, session, response):
6574
domain = self.get_cookie_domain(app)
75+
httponly = self.get_cookie_httponly(app)
76+
6677
if not session:
6778
if session.modified:
6879
response.delete_cookie(app.session_cookie_name, domain=domain)
6980
return
7081

71-
expiration = datetime.datetime.now() + self.get_expiration_time(app, session)
82+
expiration = datetime.datetime.utcnow().replace(tzinfo=utc) + self.get_expiration_time(app, session)
7283

7384
if session.modified:
7485
self.cls(sid=session.sid, data=session, expiration=expiration).save()
7586

7687
response.set_cookie(app.session_cookie_name, session.sid,
77-
expires=expiration, httponly=True, domain=domain)
88+
expires=expiration, httponly=httponly, domain=domain)

flask_mongoengine/wtf/fields.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def process_formdata(self, valuelist):
7070

7171
try:
7272
# clone() because of https://github.com/MongoEngine/mongoengine/issues/56
73-
obj = self.queryset.clone().get(id=valuelist[0])
73+
obj = self.queryset.clone().get(pk=valuelist[0])
7474
self.data = obj
7575
except DoesNotExist:
7676
self.data = None

tests/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import flask
2+
import unittest
3+
4+
class FlaskMongoEngineTestCase(unittest.TestCase):
5+
"""Parent class of all test cases"""
6+
7+
def setUp(self):
8+
self.app = flask.Flask(__name__)
9+
self.app.config['MONGODB_DB'] = 'testing'
10+
self.app.config['TESTING'] = True
11+
self.ctx = self.app.app_context()
12+
self.ctx.push()
13+
14+
def tearDown(self):
15+
self.ctx.pop()

tests/test_basic_app.py

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import sys
2-
sys.path[0:0] = [""]
32

43
import unittest
54
import datetime
65
import flask
76

87
from flask.ext.mongoengine import MongoEngine
8+
from . import FlaskMongoEngineTestCase
99

1010

11-
class BasicAppTestCase(unittest.TestCase):
11+
class BasicAppTestCase(FlaskMongoEngineTestCase):
1212

1313
def setUp(self):
14-
app = flask.Flask(__name__)
15-
app.config['MONGODB_DB'] = 'testing'
16-
app.config['TESTING'] = True
14+
super(BasicAppTestCase, self).setUp()
1715
db = MongoEngine()
1816

1917
class Todo(db.Document):
@@ -22,67 +20,79 @@ class Todo(db.Document):
2220
done = db.BooleanField(default=False)
2321
pub_date = db.DateTimeField(default=datetime.datetime.now)
2422

25-
db.init_app(app)
23+
db.init_app(self.app)
2624

2725
Todo.drop_collection()
2826
self.Todo = Todo
2927

30-
@app.route('/')
28+
@self.app.route('/')
3129
def index():
3230
return '\n'.join(x.title for x in self.Todo.objects)
3331

34-
@app.route('/add', methods=['POST'])
32+
@self.app.route('/add', methods=['POST'])
3533
def add():
3634
form = flask.request.form
3735
todo = self.Todo(title=form['title'],
3836
text=form['text'])
3937
todo.save()
4038
return 'added'
4139

42-
@app.route('/show/<id>/')
40+
@self.app.route('/show/<id>/')
4341
def show(id):
4442
todo = self.Todo.objects.get_or_404(id=id)
4543
return '\n'.join([todo.title, todo.text])
4644

47-
self.app = app
4845
self.db = db
4946

5047
def test_connection_kwargs(self):
51-
app = flask.Flask(__name__)
52-
app.config['MONGODB_SETTINGS'] = {
48+
self.app.config['MONGODB_SETTINGS'] = {
5349
'DB': 'testing_tz_aware',
54-
'alias': 'tz_aware_true',
50+
'ALIAS': 'tz_aware_true',
5551
'TZ_AWARE': True
5652
}
57-
app.config['TESTING'] = True
53+
self.app.config['TESTING'] = True
5854
db = MongoEngine()
59-
db.init_app(app)
55+
db.init_app(self.app)
6056
self.assertTrue(db.connection.tz_aware)
6157

62-
app.config['MONGODB_SETTINGS'] = {
58+
# PyMongo defaults to tz_aware = True so we have to explicitly turn
59+
# it off.
60+
self.app.config['MONGODB_SETTINGS'] = {
6361
'DB': 'testing',
64-
'alias': 'tz_aware_false',
62+
'ALIAS': 'tz_aware_false',
63+
'TZ_AWARE': False
6564
}
66-
db.init_app(app)
65+
db = MongoEngine()
66+
db.init_app(self.app)
6767
self.assertFalse(db.connection.tz_aware)
6868

6969
def test_connection_kwargs_as_list(self):
70-
app = flask.Flask(__name__)
71-
app.config['MONGODB_SETTINGS'] = [{
70+
self.app.config['MONGODB_SETTINGS'] = [{
7271
'DB': 'testing_tz_aware',
7372
'alias': 'tz_aware_true',
7473
'TZ_AWARE': True
7574
}, {
7675
'DB': 'testing_tz_aware_off',
77-
'alias' : 'tz_aware_false',
78-
'TZ_AWARE' : False
76+
'alias': 'tz_aware_false',
77+
'TZ_AWARE': False
7978
}]
80-
app.config['TESTING'] = True
79+
self.app.config['TESTING'] = True
8180
db = MongoEngine()
82-
db.init_app(app)
81+
db.init_app(self.app)
8382
self.assertTrue(db.connection['tz_aware_true'].tz_aware)
8483
self.assertFalse(db.connection['tz_aware_false'].tz_aware)
8584

85+
def test_connection_default(self):
86+
self.app.config['MONGODB_SETTINGS'] = {}
87+
self.app.config['TESTING'] = True
88+
89+
db = MongoEngine()
90+
db.init_app(self.app)
91+
92+
self.app.config['TESTING'] = True
93+
db = MongoEngine()
94+
db.init_app(self.app)
95+
8696
def test_with_id(self):
8797
c = self.app.test_client()
8898
resp = c.get('/show/38783728378090/')

0 commit comments

Comments
 (0)