Heim > Backend-Entwicklung > Python-Tutorial > So erstellen Sie flexible Datenmodelle in Django mit JSONField und Pydantic

So erstellen Sie flexible Datenmodelle in Django mit JSONField und Pydantic

Mary-Kate Olsen
Freigeben: 2024-12-31 18:13:09
Original
405 Leute haben es durchsucht

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

In diesem Artikel erkläre ich Ihnen, wie Djangos JSONField (ein JSON- und JSONB-Wrapper) zum Modellieren halbstrukturierter Daten verwendet werden kann und wie Sie darauf ein Schema erzwingen können Daten mithilfe von Pydantic – ein Ansatz, der für einen Python-Webentwickler selbstverständlich sein sollte.

Flexible Typdefinitionen

Betrachten wir ein System, das Zahlungen verarbeitet, zum Beispiel die Transaktionstabelle. Es wird so aussehen:

from django.db import models

class Transaction(models.Model):
    # Other relevant fields...
    payment_method = models.JSONField(default=dict, null=True, blank=True)
Nach dem Login kopieren
Nach dem Login kopieren

Unser Fokus liegt auf dem Feld payment_method. In einer realen Situation werden wir über bestehende Methoden zur Zahlungsabwicklung verfügen:

  • Kreditkarte

  • PayPal

  • Jetzt kaufen, später bezahlen

  • Kryptowährung

Unser System muss anpassbar sein, um die für jede Zahlungsmethode erforderlichen spezifischen Daten zu speichern und gleichzeitig eine konsistente und validierbare Struktur beizubehalten.

Wir verwenden Pydantic, um genaue Schemata für verschiedene Zahlungsmethoden zu definieren:

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
Nach dem Login kopieren
Nach dem Login kopieren

Dieser Ansatz bietet mehrere wesentliche Vorteile:

  1. Es kann jeweils nur eine Zahlungsmethode einen Wert ungleich Null haben.

  2. Es ist einfach zu erweitern oder zu ändern, ohne dass komplexe Datenbankmigrationen erforderlich sind.

  3. Gewährleistet die Datenintegrität auf Modellebene.

Um ein Schema für unser payment_method-Feld zu erzwingen, nutzen wir das Pydantic-Modell, um sicherzustellen, dass alle an das Feld übergebenen Daten mit dem von uns definierten Schema übereinstimmen.

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)}")
Nach dem Login kopieren
Nach dem Login kopieren

Hier führen wir einige Überprüfungen durch, um sicherzustellen, dass die in unseren Validator eingegebenen Daten vom richtigen Typ sind, damit Pydantic sie validieren kann. Wir unternehmen nichts für Nullable-Werte und lösen einen Typfehler aus, wenn der übergebene Wert keine Unterklasse eines Mapping-Typs ist, wie etwa ein Dict oder ein OrderedDict.

Wenn wir eine Instanz des Pydantic-Modells mit dem Wert erstellen, den wir an den Konstruktor übergeben. Wenn die Struktur des Werts nicht zum definierten Schema für PaymentMethodSchema passt, löst Pydantic einen Validierungsfehler aus. Wenn wir beispielsweise einen ungültigen E-Mail-Wert für das E-Mail-Feld in PayPalSchema übergeben, löst Pydantic einen Validierungsfehler wie diesen aus:

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]
Nach dem Login kopieren

Wir können diese Validierung auf zwei Arten erzwingen:

  1. Benutzerdefinierte Validierungsmethode

    Während des Speichervorgangs rufen wir die Validierungsfunktion auf, um sicherzustellen, dass die Zahlungsmethode dem erwarteten Schema entspricht.

    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)
    
    Nach dem Login kopieren

    Dieser Ansatz ist zwar effektiv, kann aber in Django umständlich und weniger idiomatisch werden. Wir könnten die Funktion sogar durch eine Klassenmethode ersetzen, die das Gleiche tut, um den Code sauberer zu machen.

  2. Feldvalidatoren verwenden

    Diese Methode nutzt den integrierten Feldvalidierungsmechanismus von Django:

    from django.db import models
    
    class Transaction(models.Model):
        # Other relevant fields...
        payment_method = models.JSONField(default=dict, null=True, blank=True)
    
    Nach dem Login kopieren
    Nach dem Login kopieren

    Dieser Ansatz vereint Flexibilität und Kontrolle über die im Feld payment_method gespeicherten Werte. Es ermöglicht uns, uns an zukünftige Änderungen der Anforderungen anzupassen, ohne die Integrität der vorhandenen Daten in diesem Bereich zu gefährden. Beispielsweise könnten wir ein Paystack-ID-Feld in unser Paystack-Schema aufnehmen. Diese Änderung würde nahtlos erfolgen, da wir uns nicht mit komplexen Datenbankmigrationen befassen müssten.

Wir könnten in Zukunft sogar problemlos eine pay_later-Methode hinzufügen. Auch die Feldtypen könnten sich ändern, und es gäbe keine Einschränkungen bei der Migration von Datenbankfeldern, wie sie bei der Migration von ganzzahligen Primärschlüsseln zu UUID-Primärschlüsseln auftreten. Sie können sich den vollständigen Code hier ansehen, um das Konzept vollständig zu verstehen.

Denormalisierung

Denormalisierung beinhaltet die bewusste Duplizierung von Daten über mehrere Dokumente oder Sammlungen hinweg, um die Leistung und Skalierbarkeit zu optimieren. Dieser Ansatz steht im Gegensatz zur strikten Normalisierung, die in herkömmlichen relationalen Datenbanken verwendet wird, und NoSQL-Datenbanken haben durch die Einführung flexibler, dokumentorientierter Speicherparadigmen maßgeblich zur Popularisierung der Denormalisierung beigetragen.

Stellen Sie sich ein E-Commerce-Szenario mit separaten Tabellen für Produkte und Bestellungen vor. Wenn ein Kunde eine Bestellung aufgibt, ist es wichtig, eine Momentaufnahme der im Warenkorb enthaltenen Produktdetails zu erfassen. Anstatt auf die aktuellen Produktdatensätze zu verweisen, die sich im Laufe der Zeit aufgrund von Aktualisierungen oder Löschungen ändern können, speichern wir die Produktinformationen direkt in der Bestellung. Dadurch wird sichergestellt, dass die Bestellung ihren ursprünglichen Kontext und ihre Integrität behält und den genauen Zustand der Produkte zum Zeitpunkt des Kaufs widerspiegelt. Die Denormalisierung spielt eine entscheidende Rolle beim Erreichen dieser Konsistenz.

Ein möglicher Ansatz könnte darin bestehen, einige Produktfelder in der Bestelltabelle zu duplizieren. Allerdings kann diese Methode zu Skalierbarkeitsproblemen führen und die Kohärenz des Bestellschemas gefährden. Eine effektivere Lösung besteht darin, die relevanten Produktfelder in einer JSON-Struktur zu serialisieren, sodass der Auftrag eine eigenständige Aufzeichnung der Produkte führen kann, ohne auf externe Abfragen angewiesen zu sein. Der folgende Code veranschaulicht diese Technik:

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
Nach dem Login kopieren
Nach dem Login kopieren

Da wir die meisten Konzepte im vorherigen Abschnitt behandelt haben, sollten Sie beginnen, die Rolle von Pydantic bei all dem zu schätzen. Im obigen Beispiel verwenden wir Pydantic, um eine Liste von Produkten zu validieren, die mit einer Bestellung verknüpft sind. Durch die Definition eines Schemas für die Produktstruktur stellt Pydantic sicher, dass jedes zur Bestellung hinzugefügte Produkt die erwarteten Anforderungen erfüllt. Wenn die bereitgestellten Daten nicht dem Schema entsprechen, löst Pydantic einen Validierungsfehler aus.

JSONField in Django abfragen

Wir können JSONField-Schlüssel auf die gleiche Weise abfragen, wie wir Suchen in Django-Feldern durchführen. Hier sind ein paar Beispiele basierend auf unserem Anwendungsfall.

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)}")
Nach dem Login kopieren
Nach dem Login kopieren

Sie können sich die Dokumentation ansehen, um mehr über das Filtern von JSON-Feldern zu erfahren.

Abschluss

Die Verwendung von JSON und JSONB in ​​PostgreSQL bietet große Flexibilität für die Arbeit mit halbstrukturierten Daten in relationalen Datenbanken. Tools wie Pydantic und JSONField von Django helfen dabei, Regeln für die Datenstruktur durchzusetzen, wodurch es einfacher wird, die Genauigkeit aufrechtzuerhalten und sich an Änderungen anzupassen. Diese Flexibilität muss jedoch mit Bedacht genutzt werden. Ohne ordnungsgemäße Planung kann es zu einer langsameren Leistung oder unnötiger Komplexität kommen, wenn sich Ihre Daten im Laufe der Zeit ändern.

In Django werden Feldvalidatoren nur ausgelöst, wenn full_clean() explizit aufgerufen wird – dies geschieht normalerweise, wenn Django Forms verwendet oder is_valid() auf DRF-Serialisierern aufgerufen wird. Weitere Einzelheiten finden Sie in der Dokumentation zum Django-Validator.

Ein fortgeschrittenerer Ansatz zur Lösung dieses Problems wäre die Implementierung eines benutzerdefinierten Django-Felds, das Pydantic integriert, um sowohl die Serialisierung als auch die Validierung von JSON-Daten intern zu verwalten. Obwohl dies einen eigenen Artikel erfordert, können Sie vorerst Bibliotheken erkunden, die vorgefertigte Lösungen für dieses Problem bieten, zum Beispiel: django-pydantic-jsonfield

Das obige ist der detaillierte Inhalt vonSo erstellen Sie flexible Datenmodelle in Django mit JSONField und Pydantic. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage