# 🧬 Use Structured JSON Field

The StructuredJSONField is a special type of field that allows you to create a structured JSONField. This kind of field allows you to declare a data structure that will be enforced to the json structure.

To declare a data structure you need to create a class that inherits from camomilla.structured.BaseModel and declare the fields that you want to use. The Base model is a pydantic model, so you can use all the pydantic features. If you never used pydantic before, you can find the documentation here (opens new window).

Let's see an example:

from camomilla.structured import BaseModel, StructuredJSONField

class MyStructuredJSONField(BaseModel):
    name: str
    age: int

class MyModel(models.Model):    
    structured_field = StructuredJSONField(schema=MyStructuredJSONField)

In this example we created a model with a StructuredJSONField that will accept only jsons with the following structure:

{
    "name": "string",
    "age": 0
}

If you try to save a json with a different structure, the field will raise a ValidationError.

# Default value

Since the StructuredJSONField is a JSONField, you can use all the JSONField features, like the default parameter:

from camomilla.structured import BaseModel, StructuredJSONField

class MyStructuredJSONField(BaseModel):
    name: str
    age: int

class MyModel(models.Model):
    structured_field = StructuredJSONField(schema=MyStructuredJSONField, default={"name": "John", "age": 30})

In this example we set a default value for the field. If you try to save a json without the name or age fields, the field will be populated with the default value.

You can also use a generator as default value:

from camomilla.structured import BaseModel, StructuredJSONField

class MyStructuredJSONField(BaseModel):
    name: str
    age: int

def default_value():
    return {"name": "John", "age": 30}

class MyModel(models.Model):    
    structured_field = StructuredJSONField(schema=MyStructuredJSONField, default=default_value)

In this example we used a function as default value. The function will be called every time a new instance of the model is created.

# Nesting Models

Structured models can be nested. Let's see an example:

from camomilla.structured import BaseModel

class MyNestedModel(BaseModel):
    name: str
    age: int

class MyOtherNestedModel(BaseModel):
    name: str
    age: int
    children: MyNestedModel
    childrens: list[MyNestedModel]

If you need to nest recursively a model, for example a model that as itself as children, you can declare the type as a string:

from camomilla.structured import BaseModel

class MyNestedModel(BaseModel):
    name: str
    age: int
    children: 'MyNestedModel'
    childrens: list['MyNestedModel']

# List of StructuredJSONField

If you use a list as a default value, the field will adapt the schema to accept a list of the specified type:

from camomilla.structured import BaseModel, StructuredJSONField

class MyStructuredJSONField(BaseModel):
    name: str
    age: int

class MyModel(models.Model):
    structured_field = StructuredJSONField(schema=MyStructuredJSONField, default=list)

# Foreign Key Field

There are some special features that you can use with the StructuredJSONField.

If you declare a field with a django model as type, the field will be populated with the instance of the model, as it was a foreign key:

from camomilla.structured import BaseModel, StructuredJSONField
from django.contrib.auth.models import User

class MyStructuredJSONField(BaseModel):
    name: str
    age: int
    user: User

At database level the json will store only the model primary key, but when you access the field you will get the instance of the model. Hence we have a fully working relation inside a json field.

# QuerySet Field

You can also use an other special type of field: camomilla.structured.QuerySet. This field will store a queryset inside the json field. This is useful when you need to store a list of models. For example:

from camomilla.structured import BaseModel, StructuredJSONField, QuerySet
from django.contrib.auth.models import User

class MyStructuredJSONField(BaseModel):
    name: str
    age: int
    users: QuerySet[User]

In this example we declared a field that will store a list of users. The field will be populated with a queryset, so you can use all the django queryset features. In the json structure the field will store only the primary keys of the models, ordered by the queryset order or insertion order. When you access the django queryset, the order will be preserved. This means that you can manage queryset ordering just saving the json with data in correct order.

# Built-in cache system

Both Foreign Key and QuerySet fields can lead to performance issues. If you have a lot of instances of django models spread all over the json, the field will make several queries to the database to retrieve the related models.

The structured field has a built-in cache system to avoid this problem 🎉.

The cache system will analyze the json and will make only the stricly necessary queries to the database. The cache system is enabled by default, but you can disable it from camomilla settings:

# settings.py

CAMOMILLA = {
    "STRUCTURED_FIELD": {
        "CACHE_ENABLED": False
    }
}