You are on page 1of 36

Get Started

On This Page
Install
Requirements
Using MongoDB Fields
EmbeddedField
ArrayField
Security and Integrity Checks
Validators
Integrity checks
Database configuration
Enforce schema
Djongo Manager
Using Pymongo commands
GridFS
DjongoNxt
Indexes
Model Query
Model Update
Database Transactions
Schema Validation and Model Creation
Aggregation Operators
Contribute
Note: If you are an enterprise that uses Djongo in your products, contact
us for long term support and licensing options.

InstallPermalink
1. pip install djongo

2. Into settings.py file of your project, add:

3. DATABASES = {
4. 'default': {
5. 'ENGINE': 'djongo',
6. 'NAME': 'your-db-name',
7. }
8. }
9. YOU ARE SET! Have fun!
RequirementsPermalink
1. Python 3.6 or higher.
2. MongoDB 3.4 or higher.
3. If your models use nested queries or sub querysets like:

4. inner_query =
Blog.objects.filter(name__contains='Ch').values('name')
5. entries =
Entry.objects.filter(blog__name__in=inner_query)
MongoDB 3.6 or higher is required.

Using MongoDB FieldsPermalink


EmbeddedFieldPermalink
Nest a dict inside a model with the EmbeddedField. The model_container is used to
describe the structure of the data being stored.

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)

class Meta:
abstract = True

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog
)
headline = models.CharField(max_length=255)

e = Entry()
e.blog = {
'name': 'Djongo'
}
e.headline = 'The Django MongoDB connector'
e.save()
ArrayFieldPermalink
Nest a list of dict inside a model for more complex data.

from djongo import models

class Entry(models.Model):
blog = models.ArrayField(
model_container=Blog
)
headline = models.CharField(max_length=255)

e = Entry()
e.blog = [
{'name': 'Djongo'}, {'name': 'Django'}, {'name': 'MongoDB'}
]
e.headline = 'Djongo is the best Django and MongoDB connector'
e.save()
Security and Integrity ChecksPermalink
Djongo allows for checks on data fields before they are saved to the database.
Running the correct integrity checks and field value validators before writing data
into the database is important.

ValidatorsPermalink
Apply validators to each field before they are saved.

from django.core.exceptions import ValidationError


from django.utils.translation import gettext_lazy as _
from djongo import models
from django.core.validators import URLValidator

def script_injection(value):
if value.find('<script>') != -1:
raise ValidationError(_('Script injection in %(value)s'),
params={'value': value})

class Address(models.Model)
city = models.CharField(max_length=50)
homepage = models.URLField(validators=[URLValidator,
script_injection])
class Meta:
abstract=True

class Entry(models.Model):
_id = models.ObjectIdField()
address = models.EmbeddedField(model_container=Address)
Integrity checksPermalink
class Entry(models.Model):
_id = models.ObjectIdField()
address = models.EmbeddedField(model_container=Address,
null=False,
blank=False)
By setting null=False, blank=False in EmbeddedField, missing values are never
stored.

Database configurationPermalink
The settings.py supports (but is not limited to) the following options:

Attribute Value Description

The MongoDB connection engine for interfacing


ENGINE djongo
with Django.

Ensures that the model schema and database


ENFORCE_SCHEMA True schema are exactly the same. Raises Migration
Error in case of discrepancy.

(Default) Implicitly creates collections. Returns


ENFORCE_SCHEMA False missing fields as None instead of raising an
exception.

your-
Specify your database name. This field cannot be
NAME db-
left empty.
name

A dictConfig for the type of logging to run on


LOGGING dict
djongo.

A set of key-value pairs that will be passed directly


CLIENT dict to MongoClient as kwargs while creating a new
client connection.

All options except ENGINE and ENFORCE_SCHEMA are the same those listed in
the pymongo documentation.

DATABASES = {
'default': {
'ENGINE': 'djongo',
'NAME': 'your-db-name',
'ENFORCE_SCHEMA': False,
'CLIENT': {
'host': 'host-name or ip address',
'port': port_number,
'username': 'db-username',
'password': 'password',
'authSource': 'db-name',
'authMechanism': 'SCRAM-SHA-1'
},
'LOGGING': {
'version': 1,
'loggers': {
'djongo': {
'level': 'DEBUG',
'propagate': False,
}
},
},
}
}
Enforce schemaPermalink
MongoDB is schemaless, which means no schema rules are enforced by the
database. You can add and exclude fields per entry and MongoDB will not
complain. This can make life easier, especially when there are frequent changes to
the data model. Take for example the Blog Model (version 1).

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
You can save several entries into the DB and later modify it to version 2:

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
description = models.TextField()
The modified Model can be saved without running any migrations.

This works fine if you know what you are doing. Consider a query that retrieves
entries belonging to both the ‘older’ model (with just 2 fields) and the current
model. What will the value of description now be? To handle such scenarios
Djongo comes with the ENFORCE_SCHEMA option.

When connecting to Djongo you can set ENFORCE_SCHEMA: True. In this case,
a MigrationError will be raised when field values are missing from the retrieved
documents. You can then check what went wrong.

ENFORCE_SCHEMA: False works by silently setting the missing fields with the
value None. If your app is programmed to expect this (which means it is not a bug)
you can get away by not calling any migrations.
Djongo ManagerPermalink
Djongo Manager extends the functionality of the usual Django Manager. It gives
direct access to the pymongo collection API. To use this manager define your
manager as DjongoManager in the model.

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
)
headline = models.CharField(max_length=255)
objects = models.DjongoManager()
Use it like the usual Django manager:

post = Entry.objects.get(pk=p_key)
Will get a model object having primary key p_key.

Using Pymongo commandsPermalink


MongoDB has powerful query syntax and DjongoManager lets you exploit it fully.
For the above Entry model define a custom query function:

class EntryView(DetailView):

def get_object(self, queryset=None):


index = [i for i in Entry.objects.mongo_aggregate([
{
'$match': {
'headline': self.kwargs['path']
}
},
])]

return index

You can directly access any pymongo command by prefixing mongo_ to the
command name.

For example, to perform aggregate on the BlogPage collection (BlogPage is stored


as a table in SQL or a collection in MongoDB) the function name
becomes mongo_aggregate. To directly insert a document (instead of .save() a
model) use mongo_insert_one()
GridFSPermalink
To save files using GridFS you must create a file storage instance of GridFSStorage:

grid_fs_storage = GridFSStorage(collection='myfiles')
In your model define your field as FileField or ImageField as usual:

avatar = models.ImageField(storage=grid_fs_storage, upload_to='')


Refer to Using GridFSStorage for more details.

DjongoNxtPermalink
Features under development at DjongoNxt are not a part of the standard
Djongo package. Visit the support page for more information.

DjongoNxt brings support to all features of MongoDB features including:

IndexesPermalink
Support for indexes provided by MongoDB, for example 2dSphere Index, Text
Index and Compound Indexes.

Model QueryPermalink
Support for GeoSpatial Queries and Tailable Cursors.

Model UpdatePermalink
Unordered and Ordered Bulk Writes.

Database TransactionsPermalink
Atomic multi document transactions with commit and rollback support.

Schema Validation and Model CreationPermalink


Automatic JSON Schema validation document generation and options to add Read
and Write Concerns for the Models.

Aggregation OperatorsPermalink
Support for various aggregation operators provided by MongoDB.

ContributePermalink
If you think djongo is useful, please share it with the world! Your endorsements
and online reviews will help get more support for this project.
You can contribute to the source code or the documentation by creating a simple
pull request! You may want to refer to the design documentation to get an idea on
how Django MongoDB connector is implemented.
Integrating Django with MongoDB
On This Page
How it works
What you get
Rapid Prototyping
Goodbye Migra ons
Use Django Admin to add documents
Querying Embedded fields
Migra ng an exis ng Django app to MongoDB
Se ng up an exis ng MongoDB database on Django
The internal __schema__ collec on
Zero risk
Medium risk
Note: If you are an enterprise that uses Djongo in your products, contact
us for long term support and licensing options.

Use MongoDB as a backend database for your Django project, without changing
the Django ORM. Use Django Admin to add and modify documents in MongoDB.
Start using Django with MongoDB by adding just one line of code.

How it worksPermalink
Djongo makes zero changes to the existing Django ORM framework, which
means unnecessary bugs and security vulnerabilities do not crop up. It simply
translates a SQL query string into a MongoDB query document. As a result, all
Django features, models, etc., work as is.

Django contrib modules:

'django.contrib.admin',
'django.contrib.auth',
'django.contrib.sessions',
and others… fully supported.

What you getPermalink


Djongo ensures that you:

Reuse Django Models/ORM.


Work with the original Django variant.
Future proof your code.
Atomic SQL JOIN opera ons.
Get expert support for complex projects.
Rapid PrototypingPermalink
Djongo lets you rapidly develop and evolve your app models. Modifying your
models is much faster with Djongo compared to traditional Django ORM. Since
MongoDB is a schema-less database, every time you redefine a model, MongoDB
does not expect you to redefine the schema.

Goodbye MigrationsPermalink
With Djongo you permanently say goodbye to Django Migrations. To enable
migration free model evolution simply set ENFORCE_SCHEMA: False in your database
configuration. Djongo no longer interprets SQL DDL statements (example
CREATE TABLE) to emit pymongo create_collection commands.
With ENFORCE_SCHEMA: False collections are created implicitly, on the fly.

Use Django Admin to add documentsPermalink


The Django Admin interface can be used to work with MongoDB. Additionally,
several MongoDB specific features are supported
using EmbeddedField, ArrayField and other fields. Let’s say you want to create a
blogging platform using Django with MongoDB as your backend. In your
Blog app/models.py file define the Blog model:

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

class Meta:
abstract = True
Now ‘embed’ your Blog inside a Entry using the EmbeddedField:

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
)

headline = models.CharField(max_length=255)
Register your Entry in admin.py:

from django.contrib import admin


from .models import Entry
admin.site.register(Entry)
That’s it you are set! Fire up Django Admin on localhost:8000/admin/ and this is
what you get:

Querying Embedded fieldsPermalink


In the above example, to query all Entries with Blogs which have names that start
with Beatles, use the following query:

entries = Entry.objects.filter(blog__startswith={'name': 'Beatles'})


Refer to Using Django with MongoDB data fields for more details.

Migrating an existing Django app to MongoDBPermalink


When migrating an existing Django app to MongoDB, it is recommended to start a
new database on MongoDB. For example, use myapp-djongo-db in
your settings.py file.
1. Into settings.py file of your project, add:

2. DATABASES = {
3. 'default': {
4. 'ENGINE': 'djongo',
5. 'NAME': 'myapp-djongo-db',
6. }
7. }
8. Run manage.py makemigrations <myapp> followed by manage.py migrate.
9. Open Django Admin and you should find all Models defined in your app, showing
up in the Admin.
10. While the relevant collec ons have been created in MongoDB, they have have no
data inside.
11. Con nue by inser ng data into the collec ons manually, or use Django Admin for
a GUI.
Setting up an existing MongoDB database on DjangoPermalink
The internal __schema__ collectionPermalink
There is no concept of an AUTOINCREMENT field in MongoDB. Therefore,
Djongo internally creates a __schema__ collection to track such fields.
The __schema__ collection looks like:

{
"_id" : ObjectId("5a5c3c87becdd9fe2fb255a9"),
"name" : "django_migrations",
"auto" : {
"field_names" : [
"id"
],
"seq" : NumberInt(14)
}
}
For every collection in the DB that has an autoincrement field, there is a
corresponding entry in the __schema__ collection. Running manage.py
migrate automatically creates these entries.

Now there are 2 approaches to setting up your existing data onto MongoDB:

Zero riskPermalink
1. Start with a new database name in settings.py.
2. If you have not already done so, define your models in the models.py file. The
model names and model fields have to be exactly the same, as the exis ng data
that you want to setup.
3. Run manage.py makemigrations <app_name> followed by manage.py migrate.
4. Now your empty DB should have a __schema__ collec on, and other collec ons
defined in the model.py file.
5. Copy collec on data (of your custom models defined in model.py) to the new
DB.
6. In __schema__ collec on make sure that the seq number of your
AUTOINCREMENT fields is set to the latest value. This should correspond to the
document count for each model. For example, if your model has 16 entries (16
documents in the DB), then seq should be set as 16. Usually the
AUTOINCREMENT field is called id.
However, if you do not want to create a new database (and copy existing data into
this new database), you can try this approach:

Medium riskPermalink
1. Start with an empty database. You can always delete this later.
2. Same as before.
3. Same as before.
4. Now copy the __schema__ collec on from the new database (from step1) to the
exis ng database.
5. Same as step 6 from before.
6. You can now delete the database created in step 1.
You are now done setting up Django with MongoDB. Start using Django with
MongoDB, like you would with any other database backend.
Using Djongo Model fields
On This Page
EmbeddedField
Arguments
Field data integrity checks
Nes ng Embedded Fields
Embedded Form
Querying Embedded fields
Using EmbeddedField in Django Admin
Data Model
The Embedded Data Model
EmbeddedFieldPermalink
MongoDB allows the creation of an embedded document. By using Djongo as
your connector, you can embed any other ‘model’ into your parent model through
the EmbeddedField.

class EmbeddedField(MongoField):
def __init__(self,
model_container: typing.Type[Model],
model_form_class: typing.Type[forms.ModelForm] =
None,
model_form_kwargs: dict = None,
*args, **kwargs):
ArgumentsPermalink
Argument Type Description

The child model class


model_container models.Model type (not instance) that this
embedded field will contain.

The child model form


model_form_class models.forms.ModelForm class type of the embedded
model.

The kwargs (if any) that


must be passed to
model_form_kwargs dict()
the forms.ModelForm while
instantiating it.

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

class Meta:
abstract = True

class Entry(models.Model):
_id = models.ObjectIdField()
blog = models.EmbeddedField(
model_container=Blog
)

headline = models.CharField(max_length=255)
objects = models.DjongoManager()

e = Entry.objects.create(
headline='h1',
blog={
'name': 'b1',
'tagline': 't1'
})

g = Entry.objects.get(headline='h1')
assert e == g

e = Entry()
e.blog = {
'name': 'b2'
'tagline': 't2'
}
e.headline = 'h2'
e.save()

Field data integrity checksPermalink


Djongo automatically validates the value assigned to an EmbeddedField. Integrity
criteria (null=True or blank=False) can be applied on the ÈmbeddedField or to the
internal fields (CharField)

class Entry(models.Model):
_id = models.ObjectIdField()
blog = models.EmbeddedField(
model_container=Blog,
null=True
)
headline = models.CharField(max_length=255)
objects = models.DjongoManager()

e = Entry(headline='h1', blog=None)
e.clean_fields()

>>>
# No validation error
class Entry(models.Model):
_id = models.ObjectIdField()
blog = models.EmbeddedField(
model_container=Blog,
null=False
)

headline = models.CharField(max_length=255)
objects = models.DjongoManager()

e = Entry(headline='h1', blog=None)
e.clean_fields()

>>>
ValidationError({'blog': ['This field cannot be null.']})
Nesting Embedded FieldsPermalink
An EmbeddedField or ArrayField can be nested inside an EmbeddedField. There is no
limitation on the depth of nesting.

from djongo import models

class Tagline(models.Model)
title = models.CharField(max_length=100)
subtitle = models.CharField(max_length=100)

class Meta:
abstract = True

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.EmbeddedField(model_container=Tagline)

class Meta:
abstract = True
class Entry(models.Model):
_id = models.ObjectIdField()
blog = models.EmbeddedField(
model_container=Blog
)

headline = models.CharField(max_length=255)
objects = models.DjongoManager()

e = Entry.objects.create(
headline='h1',
blog={
'name': 'b1',
'tagline': {
'title': 'Tagline Title'
'subtitle': 'Tagline Subtitle'
}
})

g = Entry.objects.get(headline='h1')
assert e == g

Embedded FormPermalink
While creating a Form for the ModelForm, the embedded forms are
automatically generated. Multiple embedded forms get automatically generated
when the Model contains an array of embedded models. However, you can still
override this by specifying the model_form_class argument in the EmbeddedField.

from djongo import models


from django import forms

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

class Meta:
abstract = True

class BlogForm(forms.ModelForm):
class Meta:
model = Blog
fields = (
'name', 'tagline'
)

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
model_form_class=BlogForm
)

headline = models.CharField(max_length=255)
objects = models.DjongoManager()
Querying Embedded fieldsPermalink
To query all BlogPost with content made by authors whose name
startswith Beatles use the following query:

entries = Entry.objects.filter(blog__startswith={'name': 'Beatles'})


Internally Djongo converts this query (for BlogPost collection) to the form:

filter = {
'blog.name': {
'$regex': '^Beatles.*$'
}
}
For querying nested embedded fields provide the appropriate dictionary value

entries = Entry.objects.filter(blog__startswith={'tagline':
{'subtitle': 'Artist'})
Internally Djongo converts this query (for BlogPost collection) to the form:

filter = {
'blog.tagline.subtitle': {
'$regex': '^Artist.*$'
}
}
Using EmbeddedField in Django AdminPermalink
Django Admin is a powerful tool for managing data used in an app. When the
models use Djongo relational fields, NoSQL “embedded models” can be created
directly from Django Admin. These fields provide better performance when
compared with traditional Django relational fields.
Django admin can use models to automatically build a site area that can be used to
create, view, update, and delete records. This can save a lot of time during
development, making it very easy to test the models and get a feel for the right
data. Django Admin is already quite well known, but to demonstrate how to use it
with Djongo, here is a simple example.

First define our basic models. In these tutorials, the same example used in the
official Django documentation is used. The documentation talks about 3 models
that interact with each other: Blog, Author and Entry. To make the example
clearer, few fields from the original models are omitted.

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

def __str__(self):
return self.name

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

def __str__(self):
return self.name

class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()

def __str__(self):
return self.headline
Start with the admin development by registering a model. Register the models
defined above in the admin.py file.
from django.contrib import admin
from .models import Blog, Author, Entry

admin.site.register([Blog, Author, Entry])


Data ModelPermalink
The Entry model defined in the documentation consists of 3 parts:

1-to-Many Rela onship: A Blog is made up of mul ple Entrys’ and each Entry is
associated with just one Blog. The same entry cannot appear in two Blogs’ and
this defines the 1-to-Many rela onship.
Many-to-Many Rela onship: An Entry can have mul ple Authors’ and
an Author can make mul ple Entrys’. This defines the many-to-many
rela onship for our data model.
Normal data columns.
An interesting point of note is that the Blog model consists of just 2 fields. Most
of the data is stored in the Entry model.

So what happens when a user enters a blog? The user wants to view the ‘Beatles
blog’. In the project you could probably do:

blog = Blog.objects.get(name='Beatles Blog')


Next, to retrieve all entries related to the Beatles blog, follow it up with:

entries = Entry.objects.filter(blog_id=blog.id)
While it is fine to obtain entries in this fashion, you end up making 2 trips to the
database. For SQL based backend this is not the most efficient way. The number
of trips can be reduced to one. Django makes the query more efficient:

entries = Entry.objects.filter(blog__name='Beatles Blog')


This query will hit the database just once. All entries associated with a Blog having
the name ‘Beatles Blog’ will be retrieved. However, this query generates a SQL
JOIN. JOINs are much slower when compared to single table lookups.

Since a Blog model shares a 1-to-many relationship with Entry the Entry model can
be written as:

class Entry(models.Model):
blog_name = models.CharField(max_length=100)
blog_tagline = models.TextField()
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()

def __str__(self):
return self.headline
The Blog fields have been inserted into the Entry model. With this new data model
the query changes to:

entries = Entry.objects.filter(blog_name='Beatles Blog')


There are no JOINs generated with this and queries will be much faster. There is
data duplication, but only if the backend database does not use data compression.

Using compression to mitigate data duplication is fine but take a look at the Entry
model, it has 10 columns and is getting unmanageable.

The Embedded Data ModelPermalink


A Blog contains a name and a tagline. An Entry contains details of the Blog,
the Authors, body_text and some Meta data. To make the Entry model manageable it
can be redefined with an EmbeddedField.

Embedded data models should be used when it does not make sense to store a data
set as another table in the database and refer to it every time with a foreign key
lookup. However, you still want to group the data set in a hierarchical fashion, to
isolate its functionality.

In case you don’t plan on using your embedded model as a standalone model
(which means it will always be embedded inside a parent model) you should add
the class Meta and abstract = True This way Djongo will never register this model
as an actual model.

It is a good practice to define embedded models as abstract models and this


is strongly recommended.

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

class Meta:
abstract = True

class MetaData(models.Model):
pub_date = models.DateField()
mod_date = models.DateField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()

class Meta:
abstract = True

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

def __str__(self):
return self.name

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
)
meta_data = models.EmbeddedField(
model_container=MetaData,
)

headline = models.CharField(max_length=255)
body_text = models.TextField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()

def __str__(self):
return self.headline
To display the embedded models in Django Admin, a Form for the embedded fields
is required. Since the embedded field is an abstract model, the form is easily
created by using a ModelForm. The BlogForm defines Blog as the model
with name and tagline as the form fields.
If you do not specify a ModelForm for your embedded models, and pass it using
the model_form_class argument, Djongo will automatically generate a ModelForm for
you.

Register the new models in admin.py.

from django.contrib import admin


from .embedded_models import Author, Entry

admin.site.register([Author, Entry])
The number of fields in the Entry model is reduce to 6. Fire up Django Admin to
check what is up!

Only the Entry and Author model are registered. I click on Entrys Add and get:
The Name and Tagline fields are neatly nested within Blog. Pub date Mod date N
pingbanks and Rating are neatly nested within Meta data.

When a user queries for a blog named ‘Beatles Blog’, the query for filtering an
embedded model changes to:

entries = Entry.objects.filter(blog={'name': 'Beatles Blog'})


This query will return all entries having an embedded blog with the name ‘Beatles
Blog’. The query will hit the database just once and there are no JOINs
involved.
Using Djongo Array Model Field
On This Page
ArrayField
Arguments
Example
Crea ng Array fields
Querying Array fields
Using ArrayField in Django Admin
ArrayFieldPermalink
With Djongo there can be an array of embedded documents inside the parent
document. You can create an embed array/list of models inside the parent
model and store it directly into MongoDB.

class ArrayField(MongoField):
def __init__(self,
model_container: typing.Type[Model],
model_form_class: typing.Type[forms.ModelForm] =
None,
model_form_kwargs: dict = None,
*args, **kwargs):
ArgumentsPermalink
Argument Type Description

The child model class


model_container models.Model type (not the instance) that
this array field will contain.

The child model form


class type of the array
model. All child models
inside the array must be of
model_form_class models.forms.ModelForm the same type. Mixing
different types of child
models inside the
embedded array is not
supported.

The kwargs (if any) that


must be passed to
model_form_kwargs dict()
the forms.ModelForm while
instantiating it.
ExamplePermalink
from djongo import models
from django import forms

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

class Meta:
abstract = True

class BlogForm(forms.ModelForm):
class Meta:
model = Blog
fields = (
'name', 'tagline'
)

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

class Meta:
abstract = True

class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = (
'name', 'email'
)

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
model_form_class=BlogForm
)

headline = models.CharField(max_length=255)
authors = models.ArrayField(
model_container=Author,
model_form_class=AuthorForm
)
objects = models.DjongoManager()
Creating Array fieldsPermalink
A Model with an Array field can be created as follows:

entry = Entry()
entry.authors = [{'name': 'John', 'email': 'john@mail.com'},
{'name': 'Paul', 'email': 'paul@mail.com'}]
entry.save()
Querying Array fieldsPermalink
Djongo uses a mixture of Django query syntax and MongoDB query syntax.
Consider a query to retrieve all entries made by the author Paul.
Using ManyToManyField this requires 2 SQL queries. First selects the id for author
Paul from the author table. Next, a JOIN with entry_authors and entry gives the
corresponding entries.

With ArrayField the query reduces to a single simple query:

entries = Entry.objects.filter(authors={'name': 'Paul'})


Djongo lets you get even more specific with your queries. To query all entries
where the third author is Paul:

entries = Entry.objects.filter(authors={'2.name': 'Paul'})


Note: In MongoDB the first element in the array starts at index 0.

Using ArrayField in Django AdminPermalink


The official Django documentation exemplifies 3 models that interact with each
other: Blog, Author and Entry. This tutorial considers the same 3 models.
The blog; ForeignKey of the Entry model was optimized in the other tutorial, here
we optimize the authors; ManyToManyField.

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

def __str__(self):
return self.name

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

def __str__(self):
return self.name

class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()

def __str__(self):
return self.headline
A ManyToManyField defines a relation wherein an entry is made by several authors.
It also defines a relation wherein an author could have made several entries.
Django handles this internally by creating another table, the entry_authors table
which contains different mappings between entry_id and author_id.

Fetching an entry will require 2 SQL queries. The second query will be an
expensive JOIN query across entry_authors and authors. The Model described
above will work perfectly well on MongoDB as well, when you use Djongo as the
connector. MongoDB however offers much more powerful ways to make such
queries. These queries come at the cost of higher disk space utilization.

As a designer using Djongo, you have the freedom to continue with the above
schema. Alternatively, you can define a schema having a trade off on disk space
for higher performance.

Let us redefine the authors in the Entry models using the ArrayField:

from djongo import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()

class Meta:
abstract = True

class MetaData(models.Model):
pub_date = models.DateField()
mod_date = models.DateField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()

class Meta:
abstract = True

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

class Meta:
abstract = True

def __str__(self):
return self.name

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
)
meta_data = models.EmbeddedField(
model_container=MetaData,
)

headline = models.CharField(max_length=255)
body_text = models.TextField()

authors = models.ArrayField(
model_container=Author,
)
n_comments = models.IntegerField()

def __str__(self):
return self.headline

Notice how the ManyToManyField is now replaced by the ArrayField. To display the
Array field in Django Admin, a Form for the field must be present. Since the array
is made up of abstract Author models, the form can be easily created by using
a ModelForm. If you do not specify a ModelForm for your array models in
the model_form_class argument, Djongo will automatically generate a ModelForm for
you.

Django Admin reveals multiple neatly nested Name and Email fields under a single
Author label.

Retrieving an entry from the database will result in no JOINS and only a single
database lookup. It is super fast
Using Djongo Array Reference Field
On This Page
Array Reference field
ArrayReferenceField
Arguments
Array Reference fieldPermalink
The ArrayField stores embedded models within a MongoDB array as embedded
documents for each entry. However, if entries contain duplicate embedded
documents, using the ArrayField would result in unnecessary duplication and
increased disk space usage. On the other hand, the Django ManyToManyField only
refers to a different table of entries. In addition however, it creates an intermediate
“through/join” table which records all the mappings.

The ArrayReferenceField is one of the most powerful features of Djongo.


The ArrayReferenceField is a bargain between the ArrayField and ManyToManyField.
Similar to the ManyToManyField a separate collection is used for storing duplicate
entries (instead of embedding them as an array). This means there is no data
duplication. However, the intermediate “through/join” mapping table is
completely skipped! This is achieved by storing only a reference to the entries in
the embedded array.

While the ManyToManyField required two queries to fetch data,


the ArrayReferenceField requires just one query and is much faster. If you have
used the ManyToManyField, then you know how to use the ArrayReferenceField. In
fact, it implements the exact same API as the ManyToManyField. You can replace
all existing ManyToManyField with ArrayReferenceField and everything will continue
to work as is.

In the example the Entry Model can be rewritten as follows:

class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()

def __str__(self):
return self.name

class Entry(models.Model):
blog = models.EmbeddedField(
model_container=Blog,
model_form_class=BlogForm
)
meta_data = models.EmbeddedField(
model_container=MetaData,
model_form_class=MetaDataForm
)

headline = models.CharField(max_length=255)
body_text = models.TextField()

authors = models.ArrayReferenceField(
to=Author,
on_delete=models.CASCADE,
)
n_comments = models.IntegerField()

def __str__(self):
return self.headline

Notice how the Author model is no longer set as abstract. This means a
separate author collection will be created in the DB. Simply set the authors to a list
containing several author instances. When the entry gets saved, only a reference to
the primary_key of the author model is saved in the array. Upon retrieving an
entry from the DB the corresponding authors are automatically looked up and the
author list is populated.

The ArrayReferenceField behaves exactly like the ManyToManyField. However,


underneath only references to the entries are being stored in the array.

ArrayReferenceFieldPermalink
class ArrayReferenceField(ForeignKey):
def __init__(self, *args, **kwargs):
ArgumentsPermalink
Same as the ForeignKey Base class
Other fields
On This Page
The ObjectId Field
ObjectIdField
Arguments
The List field
ListField
Arguments
The ObjectId FieldPermalink
For every document inserted into a collection MongoDB internally creates
an ObjectID field with the name _id. Reference this field from within the Model:

class Entry(models.Model):
_id = models.ObjectIdField()
blog = models.EmbeddedField(
model_container=Blog,
)
By default the ObjectIdField internally sets primary_key as True. This means the
implicitly created id AUTOINCREMENT field will not be created. The Field
inherits from the AutoField. An ObjectID will be automatically generated by
MongoDB for every document inserted.

Consider using the ObjectIdField in your models if you want to avoid calling
Django migrations every time you create a new model.

ObjectIdFieldPermalink
class ObjectIdField(Field):
def __init__(self, *args, **kwargs):
ArgumentsPermalink
Same as the Field Base class

The List fieldPermalink


Note: To be depreciated soon and replaced with a JSONField

ArrayFieldand ArrayReferenceField require all Models in the list to be of the same


type. MongoDB allows the saving of arbitrary data inside it is embedded array.
The ListField is useful in such cases. The list field cannot be represented in
Django Admin though and can only be used in the python script.

ListFieldPermalink
class ListField(Field):
def __init__(self, *args, **kwargs):
ArgumentsPermalink
Same as the Field Base class

You might also like