Replace MongoKit by MongoEngine on docs
Docs chapter "Patterns for Flask" has a section dedicated to MongoKit, but MongoKit lib seems unmaintained and is not compatible with recent versions of PyMongo. This commit replaces MongoKit guide with a new one using MongoEngine. Closes #2907
This commit is contained in:
parent
b2765893e6
commit
4e280b4142
3 changed files with 154 additions and 145 deletions
|
|
@ -34,7 +34,7 @@ Snippet Archives <http://flask.pocoo.org/snippets/>`_.
|
||||||
jquery
|
jquery
|
||||||
errorpages
|
errorpages
|
||||||
lazyloading
|
lazyloading
|
||||||
mongokit
|
mongoengine
|
||||||
favicon
|
favicon
|
||||||
streaming
|
streaming
|
||||||
deferredcallbacks
|
deferredcallbacks
|
||||||
|
|
|
||||||
153
docs/patterns/mongoengine.rst
Normal file
153
docs/patterns/mongoengine.rst
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
.. mongoengine-pattern:
|
||||||
|
|
||||||
|
MongoEngine in Flask
|
||||||
|
====================
|
||||||
|
|
||||||
|
Using a document database rather than a full DBMS gets more common these days.
|
||||||
|
This pattern shows how to use MongoEngine, a document mapper library, to
|
||||||
|
integrate with MongoDB.
|
||||||
|
|
||||||
|
This pattern requires a running MongoDB server, MongoEngine_ and Flask-MongoEngine_
|
||||||
|
libraries installed::
|
||||||
|
|
||||||
|
pip install flask-mongoengine
|
||||||
|
|
||||||
|
.. _MongoEngine: http://mongoengine.org
|
||||||
|
.. _Flask-MongoEngine: http://docs.mongoengine.org/projects/flask-mongoengine/en/latest/>`_
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Basic setup can be done by defining ``MONGODB_SETTINGS`` on App config and then
|
||||||
|
creating a ``MongoEngine`` instance::
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask_mongoengine import MongoEngine
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['MONGODB_SETTINGS'] = {
|
||||||
|
'host': "mongodb://localhost:27017/mydb"
|
||||||
|
}
|
||||||
|
db = MongoEngine(app)
|
||||||
|
|
||||||
|
|
||||||
|
Mapping Documents
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
To declare models that will represent your Mongo documents, just create a class that
|
||||||
|
inherits from ``Document`` and declare each of the fields::
|
||||||
|
|
||||||
|
from mongoengine import *
|
||||||
|
|
||||||
|
|
||||||
|
class Movie(Document):
|
||||||
|
|
||||||
|
title = StringField(required=True)
|
||||||
|
year = IntField()
|
||||||
|
rated = StringField()
|
||||||
|
director = StringField()
|
||||||
|
actors = ListField()
|
||||||
|
|
||||||
|
If the model has embedded documents, use ``EmbeddedDocument`` to defined the fields of
|
||||||
|
the embedded document and ``EmbeddedDocumentField`` to declare it on the parent document::
|
||||||
|
|
||||||
|
class Imdb(EmbeddedDocument):
|
||||||
|
|
||||||
|
imdb_id = StringField()
|
||||||
|
rating = DecimalField()
|
||||||
|
votes = IntField()
|
||||||
|
|
||||||
|
|
||||||
|
class Movie(Document):
|
||||||
|
|
||||||
|
...
|
||||||
|
imdb = EmbeddedDocumentField(Imdb)
|
||||||
|
|
||||||
|
|
||||||
|
Creating Data
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Just create the objects and call ``save()``::
|
||||||
|
|
||||||
|
bttf = Movie(title="Back To The Future", year=1985)
|
||||||
|
bttf.actors = [
|
||||||
|
"Michael J. Fox",
|
||||||
|
"Christopher Lloyd"
|
||||||
|
]
|
||||||
|
bttf.imdb = Imdb(imdb_id="tt0088763", rating=8.5)
|
||||||
|
bttf.save()
|
||||||
|
|
||||||
|
|
||||||
|
Queries
|
||||||
|
-------
|
||||||
|
|
||||||
|
Use the class ``objects`` attribute to make queries::
|
||||||
|
|
||||||
|
bttf = Movies.objects(title="Back To The Future").get() # Throw error if not unique
|
||||||
|
|
||||||
|
``objects`` is an iterable. Query operators may be user by concatenating it with the document
|
||||||
|
key using a double-underscore::
|
||||||
|
|
||||||
|
some_theron_movie = Movie.objects(actors__in=["Charlize Theron"]).first()
|
||||||
|
|
||||||
|
for recents in Movie.objects(year__gte=2017):
|
||||||
|
print(recents.title)
|
||||||
|
|
||||||
|
Available operators are as follows:
|
||||||
|
|
||||||
|
* ``ne`` -- not equal to
|
||||||
|
* ``lt`` -- less than
|
||||||
|
* ``lte`` -- less than or equal to
|
||||||
|
* ``gt`` -- greater than
|
||||||
|
* ``gte`` -- greater than or equal to
|
||||||
|
* ``not`` -- negate a standard check, may be used before other operators (e.g.
|
||||||
|
``Q(age__not__mod=5)``)
|
||||||
|
* ``in`` -- value is in list (a list of values should be provided)
|
||||||
|
* ``nin`` -- value is not in list (a list of values should be provided)
|
||||||
|
* ``mod`` -- ``value % x == y``, where ``x`` and ``y`` are two provided values
|
||||||
|
* ``all`` -- every item in list of values provided is in array
|
||||||
|
* ``size`` -- the size of the array is
|
||||||
|
* ``exists`` -- value for field exists
|
||||||
|
|
||||||
|
String queries
|
||||||
|
::::::::::::::
|
||||||
|
|
||||||
|
The following operators are available as shortcuts to querying with regular
|
||||||
|
expressions:
|
||||||
|
|
||||||
|
* ``exact`` -- string field exactly matches value
|
||||||
|
* ``iexact`` -- string field exactly matches value (case insensitive)
|
||||||
|
* ``contains`` -- string field contains value
|
||||||
|
* ``icontains`` -- string field contains value (case insensitive)
|
||||||
|
* ``startswith`` -- string field starts with value
|
||||||
|
* ``istartswith`` -- string field starts with value (case insensitive)
|
||||||
|
* ``endswith`` -- string field ends with value
|
||||||
|
* ``iendswith`` -- string field ends with value (case insensitive)
|
||||||
|
* ``match`` -- performs an $elemMatch so you can match an entire document within an array
|
||||||
|
|
||||||
|
Some Tips
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Attributes can be set as ``unique``
|
||||||
|
* ``MongoEngine`` creates the ``_id`` attribute automatically to acess ``ObjectIds``
|
||||||
|
* You can add choices to string fields: ``StringField(choices=['Apple', 'Banana'])``
|
||||||
|
* If you don't want your class name to be the same name as the collection, you can define
|
||||||
|
a ``meta`` class member and use the ``collection`` parameter::
|
||||||
|
|
||||||
|
class Movie(Document):
|
||||||
|
|
||||||
|
meta ={'collection': 'movie_documents'}
|
||||||
|
|
||||||
|
Accessing PyMongo MongoClient
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
If, for some reason, you want to access PyMongo instance, use ``get_connection`` function::
|
||||||
|
|
||||||
|
from mongoengine.connection import get_connection
|
||||||
|
|
||||||
|
conn = get_connection()
|
||||||
|
collection = conn.mydb.movie
|
||||||
|
collection({'title': u'Days of Thunder'})
|
||||||
|
|
||||||
|
For more information about MongoEngine, head over to the
|
||||||
|
`website <http://docs.mongoengine.org/>`_.
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
.. mongokit-pattern:
|
|
||||||
|
|
||||||
MongoKit in Flask
|
|
||||||
=================
|
|
||||||
|
|
||||||
Using a document database rather than a full DBMS gets more common these days.
|
|
||||||
This pattern shows how to use MongoKit, a document mapper library, to
|
|
||||||
integrate with MongoDB.
|
|
||||||
|
|
||||||
This pattern requires a running MongoDB server and the MongoKit library
|
|
||||||
installed.
|
|
||||||
|
|
||||||
There are two very common ways to use MongoKit. I will outline each of them
|
|
||||||
here:
|
|
||||||
|
|
||||||
|
|
||||||
Declarative
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The default behavior of MongoKit is the declarative one that is based on
|
|
||||||
common ideas from Django or the SQLAlchemy declarative extension.
|
|
||||||
|
|
||||||
Here an example :file:`app.py` module for your application::
|
|
||||||
|
|
||||||
from flask import Flask
|
|
||||||
from mongokit import Connection, Document
|
|
||||||
|
|
||||||
# configuration
|
|
||||||
MONGODB_HOST = 'localhost'
|
|
||||||
MONGODB_PORT = 27017
|
|
||||||
|
|
||||||
# create the little application object
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config.from_object(__name__)
|
|
||||||
|
|
||||||
# connect to the database
|
|
||||||
connection = Connection(app.config['MONGODB_HOST'],
|
|
||||||
app.config['MONGODB_PORT'])
|
|
||||||
|
|
||||||
|
|
||||||
To define your models, just subclass the `Document` class that is imported
|
|
||||||
from MongoKit. If you've seen the SQLAlchemy pattern you may wonder why we do
|
|
||||||
not have a session and even do not define a `init_db` function here. On the
|
|
||||||
one hand, MongoKit does not have something like a session. This sometimes
|
|
||||||
makes it more to type but also makes it blazingly fast. On the other hand,
|
|
||||||
MongoDB is schemaless. This means you can modify the data structure from one
|
|
||||||
insert query to the next without any problem. MongoKit is just schemaless
|
|
||||||
too, but implements some validation to ensure data integrity.
|
|
||||||
|
|
||||||
Here is an example document (put this also into :file:`app.py`, e.g.)::
|
|
||||||
|
|
||||||
from mongokit import ValidationError
|
|
||||||
|
|
||||||
def max_length(length):
|
|
||||||
def validate(value):
|
|
||||||
if len(value) <= length:
|
|
||||||
return True
|
|
||||||
# must have %s in error format string to have mongokit place key in there
|
|
||||||
raise ValidationError('%s must be at most {} characters long'.format(length))
|
|
||||||
return validate
|
|
||||||
|
|
||||||
class User(Document):
|
|
||||||
structure = {
|
|
||||||
'name': unicode,
|
|
||||||
'email': unicode,
|
|
||||||
}
|
|
||||||
validators = {
|
|
||||||
'name': max_length(50),
|
|
||||||
'email': max_length(120)
|
|
||||||
}
|
|
||||||
use_dot_notation = True
|
|
||||||
def __repr__(self):
|
|
||||||
return '<User %r>' % (self.name)
|
|
||||||
|
|
||||||
# register the User document with our current connection
|
|
||||||
connection.register([User])
|
|
||||||
|
|
||||||
|
|
||||||
This example shows you how to define your schema (named structure), a
|
|
||||||
validator for the maximum character length and uses a special MongoKit feature
|
|
||||||
called `use_dot_notation`. Per default MongoKit behaves like a python
|
|
||||||
dictionary but with `use_dot_notation` set to ``True`` you can use your
|
|
||||||
documents like you use models in nearly any other ORM by using dots to
|
|
||||||
separate between attributes.
|
|
||||||
|
|
||||||
You can insert entries into the database like this:
|
|
||||||
|
|
||||||
>>> from yourapplication.database import connection
|
|
||||||
>>> from yourapplication.models import User
|
|
||||||
>>> collection = connection['test'].users
|
|
||||||
>>> user = collection.User()
|
|
||||||
>>> user['name'] = u'admin'
|
|
||||||
>>> user['email'] = u'admin@localhost'
|
|
||||||
>>> user.save()
|
|
||||||
|
|
||||||
Note that MongoKit is kinda strict with used column types, you must not use a
|
|
||||||
common `str` type for either `name` or `email` but unicode.
|
|
||||||
|
|
||||||
Querying is simple as well:
|
|
||||||
|
|
||||||
>>> list(collection.User.find())
|
|
||||||
[<User u'admin'>]
|
|
||||||
>>> collection.User.find_one({'name': u'admin'})
|
|
||||||
<User u'admin'>
|
|
||||||
|
|
||||||
.. _MongoKit: http://bytebucket.org/namlook/mongokit/
|
|
||||||
|
|
||||||
|
|
||||||
PyMongo Compatibility Layer
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
If you just want to use PyMongo, you can do that with MongoKit as well. You
|
|
||||||
may use this process if you need the best performance to get. Note that this
|
|
||||||
example does not show how to couple it with Flask, see the above MongoKit code
|
|
||||||
for examples::
|
|
||||||
|
|
||||||
from MongoKit import Connection
|
|
||||||
|
|
||||||
connection = Connection()
|
|
||||||
|
|
||||||
To insert data you can use the `insert` method. We have to get a
|
|
||||||
collection first, this is somewhat the same as a table in the SQL world.
|
|
||||||
|
|
||||||
>>> collection = connection['test'].users
|
|
||||||
>>> user = {'name': u'admin', 'email': u'admin@localhost'}
|
|
||||||
>>> collection.insert(user)
|
|
||||||
|
|
||||||
MongoKit will automatically commit for us.
|
|
||||||
|
|
||||||
To query your database, you use the collection directly:
|
|
||||||
|
|
||||||
>>> list(collection.find())
|
|
||||||
[{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'}]
|
|
||||||
>>> collection.find_one({'name': u'admin'})
|
|
||||||
{u'_id': ObjectId('4c271729e13823182f000000'), u'name': u'admin', u'email': u'admin@localhost'}
|
|
||||||
|
|
||||||
These results are also dict-like objects:
|
|
||||||
|
|
||||||
>>> r = collection.find_one({'name': u'admin'})
|
|
||||||
>>> r['email']
|
|
||||||
u'admin@localhost'
|
|
||||||
|
|
||||||
For more information about MongoKit, head over to the
|
|
||||||
`website <https://github.com/namlook/mongokit>`_.
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue