ホームページ > バックエンド開発 > Python チュートリアル > JSONField と Pydantic を使用して Django で柔軟なデータ モデルを構築する方法

JSONField と Pydantic を使用して Django で柔軟なデータ モデルを構築する方法

Mary-Kate Olsen
リリース: 2024-12-31 18:13:09
オリジナル
441 人が閲覧しました

How to Build Flexible Data Models in Django with JSONField and Pydantic

この記事では、Django の JSONField (JSON および JSONB ラッパー) を使用して半構造化データをモデル化する方法と、そのデータにスキーマを適用する方法について説明します。 Pydantic を使用したデータ - Python Web 開発者にとって自然なアプローチです。

柔軟な型定義

支払いを処理するシステム、たとえばトランザクション テーブルを考えてみましょう。次のようになります:

from django.db import models

class Transaction(models.Model):
    # Other relevant fields...
    payment_method = models.JSONField(default=dict, null=True, blank=True)
ログイン後にコピー
ログイン後にコピー

私たちはpayment_methodフィールドに焦点を当てています。実際の状況では、支払いを処理するための既存の方法を使用することになります。

  • クレジットカード

  • ペイパル

  • 今すぐ購入、後で支払い

  • 暗号通貨

当社のシステムは、一貫性のある検証可能な構造を維持しながら、各支払い方法に必要な特定のデータを保存できるように適応できる必要があります。

Pydantic を使用して、さまざまな支払い方法の正確なスキーマを定義します。

from typing import Optional
from pydantic import BaseModel

class CreditCardSchema(BaseModel):
    last_four: str
    expiry_month: int
    expiry_year: int
    cvv: str


class PayPalSchema(BaseModel):
    email: EmailStr
    account_id: str


class CryptoSchema(BaseModel):
    wallet_address: str
    network: Optional[str] = None


class BillingAddressSchema(BaseModel):
    street: str
    city: str
    country: str
    postal_code: str
    state: Optional[str] = None


class PaymentMethodSchema(BaseModel):
    credit_card: Optional[CreditCardSchema] = None
    paypal: Optional[PayPalSchema] = None
    crypto: Optional[CryptoSchema] = None
    billing_address: Optional[BillingAddressSchema] = None
ログイン後にコピー
ログイン後にコピー

このアプローチには、いくつかの大きな利点があります。

  1. 一度に null 以外の値を持つことができる支払い方法は 1 つだけです。

  2. 複雑なデータベースの移行を行わずに、簡単に拡張または変更できます。

  3. モデル レベルでデータの整合性を確保します。

payment_method フィールドにスキーマを適用するには、Pydantic モデルを利用して、フィールドに渡されるデータが定義したスキーマと一致していることを確認します。

from typing import Optional, Mapping, Type, NoReturn
from pydantic import ValidationError as PydanticValidationError
from django.core.exceptions import ValidationError

def payment_method_validator(value: Optional[dict]) -> Optional[Type[BaseModel] | NoReturn]:
    if value is None:
        return

    if not isinstance(value, Mapping):
        raise TypeError("Payment method must be a dictionary")

    try:
        PaymentMethodSchema(**value)
    except (TypeError, PydanticValidationError) as e:
        raise ValidationError(f"Invalid payment method: {str(e)}")
ログイン後にコピー
ログイン後にコピー

ここでは、バリデーターに入力されるデータが正しいタイプであることを確認するためにいくつかのチェックを実行し、Pydantic が検証できるようにします。 Null 許容値に対しては何も行わず、渡された値が Dict や OrderedDict などの Mapping 型のサブクラスでない場合は型エラーを発生させます。

コンストラクターに渡す値を使用して Pydantic モデルのインスタンスを作成するとき。値の構造が PaymentMethodSchema に定義されたスキーマに適合しない場合、Pydantic は検証エラーを発生させます。たとえば、PayPalSchema の電子メール フィールドに無効な電子メール値を渡すと、Pydantic は次のような検証エラーを生成します:

ValidationError: 1 validation error for PaymentMethodSchema
paypal.email
  value is not a valid email address: An email address must have an @-sign. [type=value_error, input_value='Check me out on LinkedIn: https://linkedin.com/in/daniel-c-olah', input_type=str]
ログイン後にコピー

この検証は 2 つの方法で強制できます:

  1. カスタム検証メソッド

    保存プロセス中に、検証関数を呼び出して、支払い方法が予想されるスキーマと一致することを確認します。

    from django.db import models
    
    class Transaction(models.Model):
        # ... other fields ...
        payment_method = models.JSONField(null=True, blank=True)
        def save(self, *args, **kwargs):
            # Override save method to include custom validation
            payment_method_validator(self.payment_method)
            super().save(*args, **kwargs)
    
    ログイン後にコピー

    このアプローチは効果的ではありますが、Django では煩雑で慣用的ではなくなる可能性があります。コードをすっきりさせるために、この関数を同じことを行うクラス メソッドに置き換えることもできます。

  2. フィールドバリデータの使用

    このメソッドは、Django の組み込みフィールド検証メカニズムを活用します。

    from django.db import models
    
    class Transaction(models.Model):
        # Other relevant fields...
        payment_method = models.JSONField(default=dict, null=True, blank=True)
    
    ログイン後にコピー
    ログイン後にコピー

    このアプローチでは、payment_method フィールドに保存される値の柔軟性と制御のバランスが取れています。これにより、その分野の既存データの整合性を損なうことなく、将来の要件の変化に適応できるようになります。たとえば、Paystack スキーマに Paystack ID フィールドを含めることができます。複雑なデータベースの移行に対処する必要がないため、この変更はシームレスになります。

将来的には、手間をかけずに pay_later メソッドを追加することもできます。フィールドのタイプも変更される可能性があり、整数の主キーから UUID 主キーに移行するときに発生するような、データベース フィールドの移行制約に直面することはありません。概念を完全に理解するには、ここで完全なコードをチェックしてください。

非正規化

非正規化には、パフォーマンスとスケーラビリティを最適化するために、複数のドキュメントまたはコレクション間でデータを意図的に複製することが含まれます。このアプローチは、従来のリレーショナル データベースで使用される厳密な正規化とは対照的であり、NoSQL データベースは、柔軟なドキュメント指向のストレージ パラダイムを導入することで、非正規化の普及に貢献してきました。

商品と注文に別々のテーブルを使用する e コマース シナリオを考えてみましょう。顧客が注文するときは、カートに含まれる製品の詳細のスナップショットをキャプチャすることが重要です。更新や削除により時間の経過とともに変化する可能性がある現在の製品レコードを参照するのではなく、製品情報を注文内に直接保存します。これにより、注文が元のコンテキストと完全性を保持し、購入時の製品の正確な状態が反映されることが保証されます。非正規化は、この一貫性を実現する上で重要な役割を果たします。

考えられるアプローチの 1 つは、注文テーブルの一部の製品フィールドを複製することです。ただし、この方法ではスケーラビリティの問題が発生し、順序スキーマの結合性が損なわれる可能性があります。より効果的な解決策は、関連する製品フィールドを JSON 構造にシリアル化し、注文が外部クエリに依存せずに製品の自己完結型レコードを維持できるようにすることです。次のコードは、この手法を示しています。

from typing import Optional
from pydantic import BaseModel

class CreditCardSchema(BaseModel):
    last_four: str
    expiry_month: int
    expiry_year: int
    cvv: str


class PayPalSchema(BaseModel):
    email: EmailStr
    account_id: str


class CryptoSchema(BaseModel):
    wallet_address: str
    network: Optional[str] = None


class BillingAddressSchema(BaseModel):
    street: str
    city: str
    country: str
    postal_code: str
    state: Optional[str] = None


class PaymentMethodSchema(BaseModel):
    credit_card: Optional[CreditCardSchema] = None
    paypal: Optional[PayPalSchema] = None
    crypto: Optional[CryptoSchema] = None
    billing_address: Optional[BillingAddressSchema] = None
ログイン後にコピー
ログイン後にコピー

前のセクションでほとんどの概念を説明したので、これらすべてにおける Pydantic の役割を理解し始めるはずです。上の例では、Pydantic を使用して、注文にリンクされた製品のリストを検証します。製品構造のスキーマを定義することにより、Pydantic は注文に追加されたすべての製品が期待される要件を満たしていることを保証します。提供されたデータがスキーマに準拠していない場合、Pydantic は検証エラーを生成します。

Django で JSONField をクエリする

Django フィールドで検索を実行するのと同じ方法で、JSONField キーをクエリできます。ここでは、ユースケースに基づいたいくつかの例を示します。

from typing import Optional, Mapping, Type, NoReturn
from pydantic import ValidationError as PydanticValidationError
from django.core.exceptions import ValidationError

def payment_method_validator(value: Optional[dict]) -> Optional[Type[BaseModel] | NoReturn]:
    if value is None:
        return

    if not isinstance(value, Mapping):
        raise TypeError("Payment method must be a dictionary")

    try:
        PaymentMethodSchema(**value)
    except (TypeError, PydanticValidationError) as e:
        raise ValidationError(f"Invalid payment method: {str(e)}")
ログイン後にコピー
ログイン後にコピー

JSON フィールドのフィルタリングの詳細については、ドキュメントを参照してください。

結論

PostgreSQL で JSON と JSONB を使用すると、リレーショナル データベースで半構造化データを操作するための優れた柔軟性が得られます。 Pydantic や Django の JSONField などのツールは、データ構造のルールを強制するのに役立ち、精度の維持と変更への適応を容易にします。ただし、この柔軟性は慎重に使用する必要があります。適切な計画を立てないと、時間の経過とともにデータが変化するため、パフォーマンスが低下したり、不必要に複雑になったりする可能性があります。

Django では、フィールド バリデータは full_clean() が明示的に呼び出された場合にのみトリガーされます。これは通常、Django フォームを使用するか、DRF シリアライザーで is_valid() を呼び出すときに発生します。詳細については、Django バリデーターのドキュメントを参照してください。

これに対処するためのより高度なアプローチは、JSON データのシリアル化と検証の両方を内部で処理するために Pydantic を統合するカスタム Django フィールドを実装することです。これには専用の記事が必要ですが、現時点では、この問題に対する既製のソリューションを提供するライブラリを調べることができます (例: django-pydantic-jsonfield

)

以上がJSONField と Pydantic を使用して Django で柔軟なデータ モデルを構築する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート