Dalam artikel ini, saya akan membimbing anda melalui cara JSONField Django (pembungkus JSON & JSONB) boleh digunakan untuk memodelkan data separa berstruktur dan cara anda boleh menguatkuasakan skema pada itu data menggunakan Pydantic—pendekatan yang sepatutnya dirasakan semula jadi untuk pembangun web Python.
Mari kita pertimbangkan sistem yang memproses pembayaran, contohnya jadual Transaksi. Ia akan kelihatan seperti ini:
from django.db import models class Transaction(models.Model): # Other relevant fields... payment_method = models.JSONField(default=dict, null=True, blank=True)
tumpuan kami adalah pada medan kaedah_pembayaran. Dalam situasi dunia sebenar, kami akan mempunyai kaedah sedia ada untuk memproses pembayaran:
Kad kredit
PayPal
Beli Sekarang, Bayar Kemudian
Cryptocurrency
Sistem kami mesti boleh disesuaikan untuk menyimpan data khusus yang diperlukan oleh setiap kaedah pembayaran sambil mengekalkan struktur yang konsisten dan boleh disahkan.
Kami akan menggunakan Pydantic untuk mentakrifkan skema yang tepat untuk kaedah pembayaran yang berbeza:
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
Pendekatan ini menawarkan beberapa faedah penting:
Hanya satu kaedah pembayaran boleh mempunyai nilai bukan nol pada satu masa.
Mudah untuk melanjutkan atau mengubah suai tanpa pemindahan pangkalan data yang rumit.
Memastikan integriti data pada peringkat model.
Untuk menguatkuasakan skema pada medan kaedah_bayaran kami, kami memanfaatkan model Pydantic untuk memastikan bahawa sebarang data yang dihantar ke medan itu sejajar dengan skema yang telah kami tetapkan.
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)}")
Di sini, kami melakukan beberapa semakan untuk memastikan data yang memasuki pengesah kami adalah jenis yang betul supaya Pydantic boleh mengesahkannya. Kami tidak melakukan apa-apa untuk nilai yang boleh dibatalkan dan kami menimbulkan ralat jenis jika nilai yang dihantar bukan subkelas jenis Pemetaan, seperti Dict atau OrderedDict.
Apabila kita mencipta contoh model Pydantic menggunakan nilai yang kita masukkan ke dalam pembina. Jika struktur nilai tidak sesuai dengan skema yang ditentukan untuk PaymentMethodSchema, Pydantic akan menimbulkan ralat pengesahan. Contohnya, jika kami menghantar nilai e-mel yang tidak sah untuk medan e-mel dalam PayPalSchema, Pydantic akan menimbulkan ralat pengesahan seperti ini:
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]
Kami boleh menguatkuasakan pengesahan ini dalam dua cara:
Kaedah Pengesahan Tersuai
Semasa proses simpan, kami memanggil fungsi pengesahan untuk memastikan kaedah pembayaran sepadan dengan skema yang dijangkakan.
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)
Walaupun berkesan, pendekatan ini boleh menjadi rumit dan kurang idiomatik dalam Django. Kita juga boleh menggantikan fungsi dengan kaedah kelas yang melakukan perkara yang sama untuk menjadikan kod lebih bersih.
Menggunakan Pengesah Medan
Kaedah ini memanfaatkan mekanisme pengesahan medan terbina dalam Django:
from django.db import models class Transaction(models.Model): # Other relevant fields... payment_method = models.JSONField(default=dict, null=True, blank=True)
Pendekatan ini mengimbangi fleksibiliti dan kawalan ke atas nilai yang disimpan dalam medan kaedah_pembayaran. Ia membolehkan kami menyesuaikan diri dengan perubahan masa hadapan dalam keperluan tanpa menjejaskan integriti data sedia ada dalam bidang itu. Sebagai contoh, kami boleh memasukkan medan ID Paystack dalam skema Paystack kami. Perubahan ini akan menjadi lancar, kerana kami tidak perlu berurusan dengan migrasi pangkalan data yang rumit.
Kami juga boleh menambah kaedah pay_later pada masa hadapan tanpa sebarang kerumitan. Jenis medan juga boleh berubah dan kami tidak akan menghadapi kekangan migrasi medan pangkalan data, seperti yang dihadapi semasa berhijrah daripada kunci utama integer kepada kunci utama UUID. Anda boleh menyemak kod lengkap di sini untuk memahami konsep sepenuhnya.
Denormalisasi melibatkan penduaan data yang disengajakan merentas berbilang dokumen atau koleksi untuk mengoptimumkan prestasi dan kebolehskalaan. Pendekatan ini berbeza dengan normalisasi ketat yang digunakan dalam pangkalan data hubungan tradisional, dan pangkalan data NoSQL telah memainkan peranan penting dalam mempopularkan penyahnormalan dengan memperkenalkan paradigma storan berorientasikan dokumen yang fleksibel.
Pertimbangkan senario e-dagang dengan jadual berasingan untuk produk dan pesanan. Apabila pelanggan membuat pesanan, adalah penting untuk menangkap gambar butiran produk yang disertakan dalam troli. Daripada merujuk rekod produk semasa, yang boleh berubah dari semasa ke semasa disebabkan kemas kini atau pemadaman, kami menyimpan maklumat produk terus dalam pesanan. Ini memastikan pesanan mengekalkan konteks dan integriti asalnya, mencerminkan keadaan sebenar produk pada masa pembelian. Denormalisasi memainkan peranan penting dalam mencapai konsistensi ini.
Satu pendekatan yang mungkin mungkin melibatkan penduaan beberapa medan produk dalam jadual pesanan. Walau bagaimanapun, kaedah ini boleh memperkenalkan cabaran kebolehskalaan dan menjejaskan perpaduan skema pesanan. Penyelesaian yang lebih berkesan ialah mensirikan medan produk yang berkaitan ke dalam struktur JSON, membenarkan pesanan mengekalkan rekod produk yang lengkap tanpa bergantung pada pertanyaan luaran. Kod berikut menggambarkan teknik ini:
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
Memandangkan kami telah membincangkan kebanyakan konsep dalam bahagian sebelumnya, anda harus mula menghargai peranan Pydantic dalam semua ini. Dalam contoh di atas, kami menggunakan Pydantic untuk mengesahkan senarai produk yang dipautkan kepada pesanan. Dengan mentakrifkan skema untuk struktur produk, Pydantic memastikan setiap produk yang ditambahkan pada pesanan memenuhi keperluan yang dijangkakan. Jika data yang diberikan tidak mematuhi skema, Pydantic menimbulkan ralat pengesahan.
Kami boleh menanyakan kekunci JSONField dengan cara yang sama kami melakukan penampilan dalam medan Django. Berikut ialah beberapa contoh berdasarkan kes penggunaan kami.
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)}")
Anda boleh menyemak dokumentasi untuk mengetahui lebih lanjut tentang menapis medan JSON.
Menggunakan JSON dan JSONB dalam PostgreSQL memberikan fleksibiliti yang hebat untuk bekerja dengan data separa berstruktur dalam pangkalan data hubungan. Alat seperti Pydantic dan JSONField Django membantu menguatkuasakan peraturan untuk struktur data, menjadikannya lebih mudah untuk mengekalkan ketepatan dan menyesuaikan diri dengan perubahan. Walau bagaimanapun, fleksibiliti ini perlu digunakan dengan berhati-hati. Tanpa perancangan yang betul, ia boleh membawa kepada prestasi yang lebih perlahan atau kerumitan yang tidak perlu apabila data anda berubah dari semasa ke semasa.
Dalam Django, pengesah medan hanya dicetuskan apabila full_clean() dipanggil secara eksplisit—ini biasanya berlaku apabila menggunakan Borang Django atau memanggil is_valid() pada penyeri DRF. Untuk butiran lanjut, anda boleh merujuk kepada dokumentasi pengesah Django.
Pendekatan yang lebih maju untuk menangani perkara ini ialah melaksanakan medan Django tersuai yang menyepadukan Pydantic untuk mengendalikan kedua-dua siri dan pengesahan data JSON secara dalaman. Walaupun ini memerlukan artikel khusus, buat masa ini, anda boleh meneroka perpustakaan yang menawarkan penyelesaian siap sedia untuk masalah ini contohnya: django-pydantic-jsonfield
Atas ialah kandungan terperinci Cara Membina Model Data Fleksibel dalam Django dengan JSONField dan Pydantic. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!