SQL Server での JSONField

この記事では、サポートされている参照と制限事項など、Django のJSONFieldmssql-django バックエンドを介してSQL Serverと連携する方法について説明します。

Prerequisites

  • SQL Server 2016 以降 (JSON 関数が必要)
  • mssql-django 1.2 以降

JSONField をSQL Serverにマップする方法

Django のJSONFieldは、SQL Serverの JSON チェック制約を使用して nvarchar(max) にマップされます。 バックエンドは、SQL Serverの組み込みの JSON 関数 (JSON_VALUEJSON_QUERYISJSON) を使用して、ルックアップとクエリを実装します。

JSONField を使用してモデルを定義する

次のモデルを myapp/models.pyに追加します。 この記事の例では、ItemProduct モデルと競合しないように、 モデルを使用します。

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)

基になるテーブルがSQL Serverに存在するように、移行を生成して適用します。

python manage.py makemigrations myapp
python manage.py migrate myapp

JSON データの格納と取得

python manage.py shellで Django シェルを開きます。 >>> プロンプトで、モデルをインポートします。

from myapp.models import Item

JSON データを含むレコードを作成します。

item = Item.objects.create(
    name="Widget",
    metadata={"color": "blue", "weight": 1.5, "dimensions": {"height": 10, "width": 5}},
    tags=["sale", "new"],
)

レコードを取得し、JSON 値にアクセスします。

item = Item.objects.get(name="Widget")
print(item.metadata["color"])  # "blue"
print(item.tags)  # ["sale", "new"]

サポートされている参照

mssql-django バックエンドでは、次の JSONField 参照がサポートされています。

キー/インデックスの参照

Django の二重アンダースコア構文を使用して、入れ子になった JSON 値にアクセスします。

# Filter by nested key value
Item.objects.filter(metadata__color="blue").values()

# Access nested objects
Item.objects.filter(metadata__dimensions__height=10).values()

含む

Note

contains バックエンドでは、mssql-django参照はサポートされていません。 別の方法として、キー パス参照で has_key を使用します。

# Instead of: Item.objects.filter(metadata__contains={"color": "blue"})
# Use key-path lookup:
Item.objects.filter(metadata__color="blue").values()

has_key

特定のキーが存在するかどうかを確認します。

Item.objects.filter(metadata__has_key="color").values()

has_keys

指定されたすべてのキーが存在するかどうかを確認します。

Item.objects.filter(metadata__has_keys=["color", "weight"]).values()

has_any_keys

指定したキーのいずれかが存在するかどうかを確認します。

Item.objects.filter(metadata__has_any_keys=["color", "size"]).values()

isnull

isnull参照には、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

mssql-django バックエンドで、キーが存在するが JSON null値を持つ場合、has_keyは空の QuerySet を返します。 これは PostgreSQL とは異なり、 has_key は値に関係なく True を返します。 isnull=True参照は、キーが存在しないオブジェクトと、値がnullオブジェクトを返します。

None を指定した場合と完全に一致する

exact参照では、None値はサポートされていません。 次のクエリでは、空の QuerySet が返されます。

# Returns empty QuerySet - use isnull lookup instead
Item.objects.filter(metadata__color=None).values()

代わりに、 isnull 参照を使用して null 値を検索します。

Limitations

  • JSONField を使用した一括更新: 特に Django 5.2 以降のバージョンで、JSONField 値で bulk_update を使用する場合にエッジ ケースが存在する場合があります。 詳細については、「 mssql-django の制限事項とサポートされていない機能」を参照してください。
  • CASE WHEN 式: Django 5.2 以降のバージョンでは、CASE WHEN 式内の特定の JSONField 操作によって予期しない結果が発生する可能性があります。
  • exact with None: null の JSON 値をフィルタリングするには、exact の代わりに isnull を使用します。
  • null 値を持つhas_key: has_key は、存在するが null 値を持つキーに対して空の QuerySet を返します。
  • JSON 文字列値のリテラル引用符文字: リテラル " 文字 (たとえば、 metadata={"description": '"quoted"'}) を含む JSON 文字列値の等価参照が、格納されている行と一致しない可能性があります。 引用符文字を含む値は正しく格納されますが、フィールド参照では確実に取得できません。