Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Moar docs

  • Loading branch information...
commit 1e37f60825e8725617c2a640ca8e6461cda711ed 1 parent 6ebf0b4
@jonashaag jonashaag authored
View
1  .gitignore
@@ -7,3 +7,4 @@
.project
django_mongodb_engine.egg-info
build
+.build
View
4 django_mongodb_engine/creation.py
@@ -47,8 +47,8 @@ def sql_indexes_for_field(self, model, field, **kwargs):
if field.db_index:
kwargs = {}
opts = model._meta
- col = getattr(self.connection.db_connection, opts.db_table)
- descending = getattr(model._meta, "descending_indexes", [])
+ col = getattr(self.connection.database, opts.db_table)
+ descending = getattr(opts, "descending_indexes", [])
direction = (field.attname in descending and -1) or 1
kwargs["unique"] = field.unique
col.ensure_index([(field.name, direction)], **kwargs)
View
2  docs/Makefile
@@ -3,7 +3,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
-SPHINXBUILD = DJANGO_SETTINGS_MODULE="testproj.settings" sphinx-build
+SPHINXBUILD = DJANGO_SETTINGS_MODULE="tests.settings" sphinx-build
PAPER =
# Internal variables.
View
4 docs/_ext/django_mongodb_engine_docs.py
@@ -1,4 +1,8 @@
def setup(app):
+ from docutils.parsers.rst.directives.admonitions import Note
+
+ app.add_directive('forthelazy', Note) # for now
+
app.add_crossref_type(
directivename="setting",
rolename="setting",
View
5 docs/conf.py
@@ -18,6 +18,8 @@
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.coverage',
#'sphinxcontrib.issuetracker',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.intersphinx',
'django_mongodb_engine_docs',
]
@@ -84,3 +86,6 @@
issuetracker_user = "django-mongodb-engine"
issuetracker_project = "mongodb-engine"
issuetracker_issue_pattern = r'[Ii]ssue #(\d+)'
+
+intersphinx_mapping = {'python' : ('http://docs.python.org', None),
+ 'django' : ('http://docs.djangoproject.com/en/dev/', 'django.inv')}
View
22 docs/contrib.rst
@@ -1,22 +0,0 @@
-======================================
- :mod:`django_mongodb_engine.contrib`
-======================================
-
-The :mod:`django_mongodb_engine.contrib` module contains the :class:`MongoDBManager`
-class, which provides support for performing MapReduce queries on Django models.
-(TODO: + execjs?).
-
-.. autoclass:: django_mongodb_engine.contrib.MongoDBManager
-
- .. method:: map_reduce
-
- See :meth:`django_mongodb_engine.contrib.mapreduce.MapReduceMixin.map_reduce`
-
-
-MapReduce Support for Django Models
------------------------------------
-.. autoclass:: django_mongodb_engine.contrib.mapreduce.MapReduceMixin
- :members:
-
-.. autoclass:: django_mongodb_engine.contrib.mapreduce.MapReduceResult
- :members:
View
81 docs/cool-stuff.rst
@@ -0,0 +1,81 @@
+Other Cool Stuff
+================
+
+.. _mapreduce:
+
+Map/Reduce Support
+------------------
+For usage examples, see :file:`tests/contrib/tests.py`.
+
+.. autoclass:: django_mongodb_engine.contrib.MongoDBManager
+.. automodule:: django_mongodb_engine.contrib.mapreduce
+ :members:
+
+.. _mongodb-fields:
+
+Fields
+------
+.. automodule:: django_mongodb_engine.fields
+ :members:
+
+.. _collection-options:
+
+Collection Options
+------------------
+Collection flags can be set in a :class:`MongoMeta` attribute in a model,
+similar to Django's :class:`Meta` class:
+
+.. code-block:: python
+
+ class MyModel(models.Model):
+ # your fields here
+
+ class MongoMeta:
+ # your flags here
+
+Those flags can be:
+
+``index_together`` (default: ``[]``)
+ A list of dictionaries containing an item with the key *field* and a list of
+ field names or :samp:`({field name}, {index direction})` tuples to
+ index together, optionally containing keyword arguments to pass to
+ :meth:`pymongo.Collection.ensure_index`. For example, ::
+
+ index_together = [{'fields' : ['name', ('last_name', -1)],
+ 'name' : 'name-lastname-index'}]
+
+ results in this call::
+
+ target_collection.ensure_index([('name', 1), ('last_name', -1)], name='name-lastname-index')
+
+``descending_indexes`` (default: ``[]``)
+ A list of fields whose index shall be descending rather than ascending.
+ For example, ::
+
+ class MyModel(models.Model):
+ a = models.CharField(db_index=True)
+ b = models.CharField(db_index=True)
+
+ class MongoMeta:
+ descending_indexes = ['b']
+
+ would create an ascending index on field ``a`` and a descending one on ``b``.
+
+``capped`` (default: :const:`False`)
+ A boolean specifying whether the collection for the model shall be capped.
+
+``collection_size`` and ``collection_max`` (default: unset)
+ If using a ``capped`` collection, those two options specify the size (in bytes)
+ and the maximum number of objects of the capped collection, respectively.
+
+
+Automatic Model Instance (De)Referencing
+----------------------------------------
+If you set the ``MONGODB_AUTOMATIC_REFERENCING`` option to :class:`True` in
+your :file:`settings.py`, django-mongodb-engine automatically references
+model instances in :class:`lists <ListField>` or :class:`dicts <DictField>` on
+saves and dereferences them when you access an attribute.
+
+.. todo::
+
+ Example
View
261 docs/embedded-objects.rst
@@ -0,0 +1,261 @@
+Embedded Objects
+================
+
+django-mongodb-engine supports `MongoDB's embedded objects`_.
+
+.. toctree::
+ :maxdepth: 1
+
+.. _simple-embedding:
+
+Simple Embedding
+~~~~~~~~~~~~~~~~
+.. forthelazy::
+
+ Using :class:`~djangotoolbox-fields.EmbeddedModelField`, you can embed
+ any Django model in other models::
+
+ from djangotoobox.fields import EmbeddedModelField
+
+ class ModelA(models.Model):
+ b = EmbeddedModelField(ModelB) # or 'B' (in quotes)
+
+With :class:`~djangotoolbox-fields.EmbeddedModelField`, which was originally
+written for *django-mongodb-engine* and then merged into `djangotoolbox`,
+you can embed any Django model into another model. Example::
+
+ from django.db import models
+ from djangotoolbox import fields
+
+ class Customer(models.Model):
+ name = models.CharField()
+ address = fields.EmbeddedModelField('Address')
+
+ class Address(models.Model):
+ city = models.CharField()
+ street = models.CharField()
+
+Similar to relation definitions, the model passed to
+:class:`~djangotoolbox-fields.EmbeddedModelField` can either be the class name
+(``Address``) or a string containing the class name (``'Address'``).
+
+Let's add a customer to the database. ::
+
+ Customer.objects.create(
+ name='John',
+ address=Address(city='London', street='Some street')
+ )
+
+The resulting data record can be inspected with the `MongoDB console`_:
+
+.. code-block:: js
+
+ /* db.docs_customer.findOne() */
+ {
+ "_id" : ObjectId("4ce..."),
+ "name" : "John",
+ "address" : {
+ "city" : "London",
+ "street" : "Some street",
+ }
+ }
+
+As you see, the *address* is stored as a nested JSON/BSON object.
+
+
+.. _embedded-objects-list:
+
+Embedded Objects in a List
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. forthelazy::
+
+ It's cool to use Embedded Objects in a list instead of relations::
+
+ from djangotoobox.fields import EmbeddedModelField, ListField
+
+ class ModelA(models.Model):
+ b = ListField(EmbeddedModelField(ModelB))
+
+If used in a list or dict or similar, Embedded Objects can be
+a good or even better alternative to relations.
+
+Imagine you're a music store and you have those models::
+
+ from djangotoolbox.fields import EmbeddedModelField, ListField
+
+ class Musician(models.Model):
+ name = models.CharField()
+ albums = ListField(EmbeddedModelField('Album'))
+
+ class Album(models.Model):
+ title = models.CharField()
+ year = models.IntegerField()
+ tracks = ListField(EmbeddedModelField('Track'))
+
+ class Track(models.Model):
+ title = models.CharField()
+ length = models.IntegerField()
+
+Now let's add a data record ::
+
+ dimmu= Musician.objects.create(name='Dimmu Borgir')
+ in_sorte_diaboli = Album(
+ title='In Sorte Diaboli', year=2007,
+ tracks=[Track(title='The Serpentine Offering', length=309),
+ Track(title='The Chosen Legacy', length=257),
+ Track(title='The Conspiracy Unfolds', length=324)]
+ )
+ dimmu.albums.append(in_sorte_diaboli)
+ dimmu.save()
+
+and have a look at what BSON was generated by *django-mongodb-engine*:
+
+.. code-block:: js
+
+ /* > db.docs_musician.findOne() */
+ {
+ "_id" : ObjectId("4cee2549e4721c3b2c000001"),
+ "name" : "Dimmu Borgir",
+ "albums" : [
+ {
+ "title" : "In Sorte Diaboli",
+ "year" : 2007,
+ "tracks" : [
+ {
+ "length" : 309,
+ "title" : "The Serpentine Offering"
+ },
+ {
+ "length" : 257,
+ "title" : "The Chosen Legacy"
+ },
+ {
+ "length" : 324,
+ "title" : "The Conspiracy Unfolds"
+ }
+ ]
+ }
+ ]
+ }
+
+Neat, isn't it?
+
+Keep in Mind: ``auto_now_add`` behaves like ``auto_now``
+--------------------------------------------------------
+because there's `no difference between an insert and update`_ in MongoDB.
+
+
+Keep in Mind: No Reverse-Relations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The model structure used above makes queries down the data tree easy, e.g.
+*get me all albums by musician xy* or *get me all albums whose title starts with
+an 'a'*. You could even emulate queries like *get me all tracks of musician xy*::
+
+ sum((album.tracks for album in Musician.objects.get(name=xy).album), [])
+
+However, queries that would normally require reverse-JOINs are not possible.
+For example, *get me all albums from 2005 with the related musicians* simply
+is impossible on non-relational databases as such a query would require JOINs.
+Emulation of such queries is possible but **very expensive** because you have
+to crawl through the whole collection::
+
+ # VERY expensive and slow query. Don't do this!
+ result = []
+ for musician in Musician.objects.all():
+ musician.albums = filter(lambda album:album.year == 2005, musician.albums)
+ if musician.albums:
+ result.append(musician)
+
+.. rubric:: What's the point?
+
+Consider your queries when designing your models.
+
+
+.. _embedded-objects-queries:
+
+Querying and Updating Embedded Objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Querying
+--------
+.. forthelazy::
+
+ .. code-block:: python
+
+ objects.filter(field=A('subfield', 'value'))
+
+ translates to the Mongo query
+
+ .. code-block:: js
+
+ .find( {'field.subfield' : 'value'} )
+
+django-mongodb-engine has full support for
+`queries and updates that reach into subobjects <MongoDB's embedded objects>`_
+using the :class:`~django_mongodb_engine.query.A` query helper.
+
+For example, *find any customer whose habitat is London*::
+
+ >>> from django_mongodb_engine.query import A
+ >>> Customer.objects.create(name='Bob', address=Address(city='NY'))
+ >>> Customer.objects.create(name='Ann', address=Address(city='London'))
+ >>> Customer.objects.filter(address=A('city', 'London'))
+ [<Customer 'Ann'>]
+
+If you :ref:`enable query debugging <query-debugging>`,
+you can see what this query translates to::
+
+ DEBUG [...] .find {'address.city': 'London'} [...]
+
+If you need to match multiple filters on the same embedded field, chain
+:meth:`~django.db.query.QuerySet.filter` calls::
+
+ Customer.objects.filter(address=A('city', 'London')) \
+ .filter(address=A('street', 'Some street'))
+
+This translates to:
+
+.. code-block:: js
+
+ .find( {'address.city': 'London', 'address.street': 'Some street'} )
+
+
+Updating
+--------
+.. forthelazy::
+
+ .. code-block:: python
+
+ objects.filter(foo='bar').update(field=A('subfield', 'new-value'))
+
+ translates to the Mongo query
+
+ .. code-block:: js
+
+ .update( {foo: 'bar'}, {'$set' : {'field.subfield' : 'new-value'}} )
+
+Updating works similar to this, e.g. John moved to Houston::
+
+ Customer.objects.filter(name='John').update(addresss=A('city', 'Houston'))
+
+Translates to:
+
+.. code-block:: js
+
+ .update( {'name': 'John'}, {'$set': {'address.city': 'Houston'}} )
+
+Of course, :class:`~django_mongodb_engine.query.A` can be used for both filtering
+and updating at the same time; for example, for moving all customers from
+New York to Canasas::
+
+ Customer.objects.filter(address=A('city', 'NY')) \
+ .update(address=A('city', 'Cansas'))
+
+Which translates to:
+
+.. code-block:: js
+
+ .update( {'address.city': 'NY'}, {'$set': {'address.city': 'Cansas'}} )
+
+
+.. _MongoDB's embedded objects: http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29
+.. _MongoDB console: http://www.mongodb.org/display/DOCS/mongo+-+The+Interactive+Shell
View
41 docs/faq.rst
@@ -0,0 +1,41 @@
+Frequently Asked Questions
+==========================
+
+Does it support JOINs?
+~~~~~~~~~~~~~~~~~~~~~~
+No, because MongoDB doesn't, but have look at
+:doc:`embedded-objects` and :doc:`lists-and-dicts`.
+
+Does it support transcations?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+No, because MongoDB doesn't.
+
+How can I use Capped Collections?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+See :ref:`collection-options`.
+
+.. _query-debugging:
+
+Does it support query debugging?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Yep! Set ``DEBUG`` to :const:`True` in your :file:`settings.py` and
+`configure <http://docs.djangoproject.com/en/dev/topics/logging/#configuring-logging>`_
+the ``django.db.backends`` logger, for example like this::
+
+ LOGGING = {
+ 'version' : 1,
+ 'formatters' : {'simple' : {'format': '%(levelname)s %(message)s'}},
+ 'handlers' : {
+ 'console' : {
+ 'level' : 'DEBUG',
+ 'class' : 'logging.StreamHandler',
+ 'formatter' : 'simple'
+ }
+ },
+ 'loggers' : {
+ 'django.db.backends' : {
+ 'level' : 'DEBUG',
+ 'handlers' : ['console']
+ }
+ }
+ }
View
6 docs/fields.rst
@@ -1,6 +0,0 @@
-===============
- MongoDB Fields
-===============
-
-.. automodule:: django_mongodb_engine.fields
- :members: EmbeddedModelField, GridFSField
View
62 docs/index.rst
@@ -4,50 +4,48 @@
.. _MongoDB: http://mongodb.org
-Setup
------
+*django-mongodb-engine* is a full-featured MongoDB backend for Django
+including support for :doc:`Embedded Objects </embedded-objects>`,
+:doc:`lists and dicts </lists-and-dicts>`, aggregations and
+:ref:`Map/Reduce <mapreduce>`.
-No special setup needed, just set ``django_mongodb_engine`` as your database
-``ENGINE``, pick a ``NAME`` and, if needed, change ``HOST`` and ``PORT`` to match
-your needs. Additionally, you should provide the ``SUPPORRTS_TRANSACTIONS``
-option in your ``DATABASES`` settings::
+
+Quickstart
+~~~~~~~~~~
+Get the *latest* versions of `djangotoolbox`_, `Django-nonrel`_ and
+django-mongodb-engine from GitHub::
+
+ git clone git://github.com/django-mongodb-engine/mongodb-engine
+ cd mongodb-engine && python setup.py install
+
+Database setup is easy (see also: `Django database setup docs`_)::
DATABASES = {
'default' : {
'ENGINE' : 'django_mongodb_engine',
- 'NAME' : 'myproject_db',
+ 'NAME' : 'my_database',
+
+ # optional:
+ 'HOST' : 'localhost',
+ 'PORT' : 27017,
'SUPPORTS_TRANSACTIONS' : False
}
}
+**That's it!** You can now go straight ahead developing your Django application
+as you would do with any other database.
-Questions?
-----------
-Does it support transcations?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-No, because MongoDB doesn't.
-
-Does it support JOINs?
-~~~~~~~~~~~~~~~~~~~~~~
-No, because MongoDB doesn't, but
-
-* you can use djangotoolbox' ``DictField``, ``ListField`` and ``SetField`` to
- store Python lists, sets and dictionaries
-* you can embed model instances using :class:`django_mongodb_engine.fields.EmbeddedModelField`
-* you can combine those two
-
-
-User Docs
----------
-.. toctree::
- :maxdepth: 2
+.. _Django database setup docs: http://docs.djangoproject.com/en/dev/ref/settings/#databases
+.. _djangotoolbox: http://allbuttonspressed.com/#TODO
+.. _Django-nonrel: http://allbuttonspressed.com/#TODO
- fields
- contrib
-Internals
----------
+This might be interesting, too:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. toctree::
:maxdepth: 2
- internals/index
+ Embedded Objects Instead of JOINs <embedded-objects>
+ Lists and Dicts Instead of JOINS <lists-and-dicts>
+ cool-stuff
+ faq
View
10 docs/internals/base.rst
@@ -1,10 +0,0 @@
-=======================================================
- Base Engine Module - :mod:`django_mongodb_engine.base`
-=======================================================
-
-.. contents::
- :local:
-.. currentmodule:: django_mongodb_engine.base
-
-.. automodule:: django_mongodb_engine.base
- :members: DatabaseFeatures, DatabaseValidation, DatabaseIntrospection, DatabaseWrapper
View
10 docs/internals/creation.rst
@@ -1,10 +0,0 @@
-========================================================
- Creation Module - :mod:`django_mongodb_engine.creation`
-========================================================
-
-.. contents::
- :local:
-.. currentmodule:: django_mongodb_engine.creation
-
-.. automodule:: django_mongodb_engine.creation
- :members: DatabaseCreation
View
12 docs/internals/index.rst
@@ -1,12 +0,0 @@
-===============
- API Reference
-===============
-
-:Release: |version|
-:Date: |today|
-
-.. toctree::
- :maxdepth: 2
-
- base
- creation
View
80 docs/lists-and-dicts.rst
@@ -0,0 +1,80 @@
+Storing Python Lists and Dicts
+==============================
+
+Using :class:`ListField` and :class:`DictField`, you can store *arbitrary* Python
+lists, dicts and sets in MongoDB.
+
+.. class:: ListField
+.. class:: SetField
+
+ Example::
+
+ from djangotoolbox.fields import ListField, SetField
+
+ def optional_ordering(n):
+ """ Optional ordering function (sorts reversed) """
+ return -n
+
+ class Post(models.Model):
+ title = models.CharField()
+ tags = SetField(default=set())
+ ratings = ListField(models.IntegerField(), ordering=optional_ordering)
+
+ A ``Post``'s ``tags`` attribute now is a standard Python list::
+
+ >>> post_about_turtles = Post.objects.create(title='I Like Turtles',
+ ... tags=['animals', 'food'])
+ >>> post_about_turtles.tags.remove('food') # nobody would eat animals, right?
+ >>> post_about_turtles.save()
+ >>> Post.objects.get().tags
+ set(['animals']) # see how the list got a set
+
+ If you pass a field instance as argument, items of the list or dict will be
+ converted into a data type compatible to this field::
+
+ >>> post_about_turtles.ratings.extend(['1', 3.14])
+ >>> post_about_turtles.save()
+ >>> Post.objects.get().ratings
+ [3, 1] # reverse-ordering function, remember?
+
+ Let's have a look at the MongoDB data set.
+
+ .. code-block:: js
+
+ /* > db.docs_post.findOne() */
+ {
+ "_id" : ObjectId("4cee6e62e4721c6508000000"),
+ "ratings" : [
+ 1,
+ 3
+ ],
+ "tags" : [
+ "animal"
+ ],
+ "title" : "I Like Turtles"
+ }
+
+.. class:: DictField
+
+ behaves similar::
+
+ from djangotoolbox.fields import DictField
+
+ class MyMotherWentShopping(models.Model):
+ and_she_bought = DictField()
+
+ ::
+
+ >>> my_mother_went_shopping = MyMotherWentShopping()
+ >>> my_mother_went_shopping.and_she_bought = {
+ ... 'eggs' : 3,
+ ... 'bread' : 'two loafs',
+ ... 'tofu' : (None, ['because I hate tofu'])
+ ... }
+ >>> my_mother_went_shopping.save()
+ >>> MyMotherWentShopping.objects.get().and_she_bought
+ {u'bread': u'two loafs', u'eggs': 3, u'tofu': [None, [u'because I hate tofu']]}
+
+ As you can see, the tuple of the *tofu* item ends up being a list when fetched
+ from the database. That's because MongoDB has no tuples and nobody told Django
+ to convert it to a tuple, so it's still a list.
Please sign in to comment.
Something went wrong with that request. Please try again.