Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article explains how Django's JSONField works with SQL Server through the mssql-django backend, including supported lookups and limitations.
Prerequisites
- SQL Server 2016 or later (JSON functions are required)
mssql-django1.2 or later
How JSONField maps to SQL Server
Django's JSONField maps to nvarchar(max) with a JSON check constraint in SQL Server. The backend uses SQL Server's built-in JSON functions (JSON_VALUE, JSON_QUERY, ISJSON) to implement lookups and queries.
Define a model with JSONField
Add the following model to myapp/models.py. The examples in this article use an Item model so they don't conflict with the Product model from the Django quickstart.
from django.db import models
class Item(models.Model):
name = models.CharField(max_length=100)
metadata = models.JSONField(default=dict)
tags = models.JSONField(null=True, blank=True)
Generate and apply the migration so the underlying table exists in SQL Server:
python manage.py makemigrations myapp
python manage.py migrate myapp
Store and retrieve JSON data
Open the Django shell with python manage.py shell. At the >>> prompt, import the model:
from myapp.models import Item
Create a record with JSON data:
item = Item.objects.create(
name="Widget",
metadata={"color": "blue", "weight": 1.5, "dimensions": {"height": 10, "width": 5}},
tags=["sale", "new"],
)
Retrieve the record and access JSON values:
item = Item.objects.get(name="Widget")
print(item.metadata["color"]) # "blue"
print(item.tags) # ["sale", "new"]
Supported lookups
The mssql-django backend supports the following JSONField lookups:
Key/index lookups
Access nested JSON values using Django's double-underscore syntax:
# Filter by nested key value
Item.objects.filter(metadata__color="blue").values()
# Access nested objects
Item.objects.filter(metadata__dimensions__height=10).values()
contains
Note
The contains lookup isn't supported on the mssql-django backend. Use has_key with key-path lookups as an alternative:
# Instead of: Item.objects.filter(metadata__contains={"color": "blue"})
# Use key-path lookup:
Item.objects.filter(metadata__color="blue").values()
has_key
Check if a specific key exists:
Item.objects.filter(metadata__has_key="color").values()
has_keys
Check if all specified keys exist:
Item.objects.filter(metadata__has_keys=["color", "weight"]).values()
has_any_keys
Check if any of the specified keys exist:
Item.objects.filter(metadata__has_any_keys=["color", "size"]).values()
isnull
The isnull lookup has specific behavior with SQL Server:
# Returns objects where the key doesn't exist AND keys with None value
Item.objects.filter(metadata__color__isnull=True).values()
# Returns objects where the key exists and has a non-null value
Item.objects.filter(metadata__color__isnull=False).values()
Note
On the mssql-django backend, if a key exists but has a JSON null value, has_key returns an empty QuerySet. This differs from PostgreSQL, where has_key returns True regardless of value. The isnull=True lookup returns objects where the key doesn't exist and objects where the value is null.
exact with None
The exact lookup doesn't support None values. The following query returns an empty QuerySet:
# Returns empty QuerySet - use isnull lookup instead
Item.objects.filter(metadata__color=None).values()
Use the isnull lookup instead to find null values.
Limitations
- Bulk updates with JSONField: Some edge cases exist when using
bulk_updatewith JSONField values, particularly on Django 5.2 and later versions. For more information, see Limitations and unsupported features in mssql-django. - CASE WHEN expressions: On Django 5.2 and later versions, certain JSONField operations inside CASE WHEN expressions might produce unexpected results.
- exact with None: Use
isnullinstead ofexactto filter for null JSON values. - has_key with null values:
has_keyreturns an empty QuerySet for keys that exist but have anullvalue. - Literal quote characters in JSON string values: Equality lookups on JSON string values that contain literal
"characters (for example,metadata={"description": '"quoted"'}) might not match the stored row. Values containing quote characters store correctly but aren't reliably retrievable through field lookups.