Using Djongo Array Model Field
ArrayField
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):
Arguments
Argument | Type | Description |
---|---|---|
model_container |
models.Model |
The child model class type (not the instance) that this array field will contain. |
model_form_class |
models.forms.ModelForm |
The child model form class type of the array model. All child models inside the array must be of the same type. Mixing different types of child models inside the embedded array is not supported. |
model_form_kwargs |
dict() |
The kwargs (if any) that must be passed to the forms.ModelForm while instantiating it. |
Example
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 fields
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 fields
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 Admin
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
Retrieving an entry from the database will result in no JOINS and only a single database lookup. It is super fast