Class Property didesain untuk dijadikan subclass.
Namun, biasanya lebih mudah untuk membuat subclass dari subclass
Property yang sudah ada.
Semua atribut Property khusus,
bahkan yang dianggap 'publik',
memiliki nama yang diawali dengan garis bawah.
Ini karena StructuredProperty
menggunakan namespace atribut non-garis bawah untuk merujuk ke nama
Property bertingkat; ini penting untuk menentukan
kueri pada sub-properti.
Class Property dan subclass-nya yang telah ditetapkan memungkinkan
subclassing menggunakan API validasi dan
konversi composable (atau stackable). Tindakan ini memerlukan beberapa definisi terminologi:
- Nilai pengguna adalah nilai yang akan ditetapkan dan diakses oleh kode aplikasi menggunakan atribut standar pada entity.
- Nilai dasar adalah nilai yang akan diserialisasi ke dan dideserialisasi dari Datastore.
Subclass Property yang mengimplementasikan transformasi
tertentu antara nilai pengguna dan nilai yang dapat diserialisasi harus
mengimplementasikan dua metode, _to_base_type() dan
_from_base_type().
Metode ini tidak boleh memanggil
metode super().
Inilah yang dimaksud dengan API composable (atau stackable).
API mendukung class stacking dengan konversi basis pengguna
yang lebih canggih: konversi pengguna ke basis
berubah dari lebih canggih menjadi kurang canggih, sedangkan
konversi basis ke pengguna berubah dari kurang canggih menjadi lebih canggih. Misalnya, lihat hubungan antara
BlobProperty, TextProperty,
dan StringProperty.
Misalnya, TextProperty mewarisi dari
BlobProperty; kodenya cukup sederhana karena mewarisi
sebagian besar perilaku yang dibutuhkan.
Selain _to_base_type() dan
_from_base_type(), metode
_validate() juga merupakan API composable.
API validasi membedakan antara nilai pengguna lax dan strict. Kumpulan nilai lax adalah superset dari kumpulan nilai
strict. Metode _validate() mengambil nilai lax dan jika perlu
mengonversinya menjadi nilai strict. Ini berarti bahwa nilai lax diterima ketika menetapkan nilai properti, dan hanya nilai ketat yang akan ditampilkan ketika mendapatkan nilai properti. Jika tidak ada
konversi yang diperlukan, _validate() dapat menampilkan Tidak ada. Jika argumen berada di luar kumpulan nilai lax yang diterima, _validate() harus memunculkan pengecualian, sebaiknya TypeError atau datastore_errors.BadValueError.
_validate(), _to_base_type(),
dan _from_base_type() tidak perlu menangani:
None: Ketiganya tidak akan dipanggil denganNone(dan jika menampilkan Tidak ada, artinya nilai tidak memerlukan konversi).- Nilai berulang: Infrastruktur akan menangani pemanggilan
_from_base_type()atau_to_base_type()untuk setiap item daftar dalam nilai berulang. - Membedakan nilai pengguna dari nilai dasar: Infrastruktur menangani hal ini dengan memanggil API composable.
- Perbandingan: Operasi perbandingan memanggil
_to_base_type()pada operand-nya. - Membedakan antara nilai pengguna dan nilai dasar: Infrastruktur menjamin bahwa
_from_base_type()akan dipanggil dengan nilai dasar (tidak digabungkan), dan_to_base_type()tersebut akan dipanggil dengan nilai pengguna.
Misalnya, Anda perlu menyimpan bilangan bulat yang sangat panjang.
IntegerProperty standar hanya mendukung bilangan bulat 64-bit (ditandatangani).
Properti Anda mungkin menyimpan bilangan bulat yang lebih panjang sebagai string; ada baiknya jika class properti
menangani konversi.
Aplikasi yang menggunakan class properti Anda mungkin terlihat seperti ini
...
...
...
...
Ini terlihat sederhana dan mudah. Contoh ini juga menunjukkan
penggunaan beberapa opsi properti standar (default, berulang). Sebagai
penulis LongIntegerProperty, Anda akan senang
mengetahui bahwa Anda tidak perlu menulis "boilerplate" apa pun agar
berfungsi. Lebih mudah untuk mendefinisikan subclass dari properti lain, misalnya:
Saat Anda menetapkan nilai properti pada entity, misalnya ent.abc = 42, metode _validate() Anda akan dipanggil, dan (jika tidak memunculkan pengecualian) nilai tersebut disimpan di entity. Saat Anda menulis entity ke Datastore, metode _to_base_type() akan dipanggil, dengan mengonversi nilai menjadi string. Kemudian, nilai tersebut akan diserialisasi oleh class dasar,
StringProperty.
Rantai peristiwa terbalik terjadi saat entity dibaca kembali dari Datastore. Class StringProperty dan Property
bersama-sama menangani detail lainnya, seperti melakukan serialisasi
dan deserialisasi string, menetapkan default, serta menangani
nilai properti berulang.
Dalam contoh ini, mendukung ketidaksetaraan (yaitu kueri yang menggunakan <, <=, >, >=) memerlukan upaya lebih. Contoh implementasi berikut menerapkan ukuran maksimum bilangan bulat dan menyimpan nilai sebagai string panjang tetap:
Ini dapat digunakan dengan cara yang sama seperti LongIntegerProperty,
kecuali bahwa Anda harus meneruskan jumlah bit ke konstruktor properti,
misalnya BoundedLongIntegerProperty(1024).
Anda dapat membuat subclass jenis properti lain dengan cara yang sama.
Pendekatan ini juga berfungsi untuk menyimpan data terstruktur.
Misalkan Anda memiliki class Python FuzzyDate yang mewakili
rentang tanggal; metode ini menggunakan kolom first dan last untuk menyimpan awal dan akhir rentang tanggal:
...
Anda dapat membuat FuzzyDateProperty yang berasal dari
StructuredProperty. Sayangnya, kode yang terakhir tidak
berfungsi dengan class Python lama biasa; kode ini memerlukan subclass Model.
Jadi, definisikan subclass Model sebagai representasi perantara;
Selanjutnya, buat subclass StructuredProperty yang meng-hardcode argumen modelclass menjadi FuzzyDateModel, serta menentukan metode _to_base_type() dan _from_base_type() untuk mengonversi antara FuzzyDate dan
FuzzyDateModel:
Aplikasi dapat menggunakan class ini seperti berikut:
...
Misalkan Anda ingin menerima objek date biasa
selain objek FuzzyDate sebagai nilai untuk
FuzzyDateProperty. Untuk melakukannya, ubah metode _validate()
sebagai berikut:
Sebagai gantinya, Anda dapat membuat subclass FuzzyDateProperty sebagai berikut
(dengan asumsi FuzzyDateProperty._validate()
seperti yang ditampilkan di atas).
Saat Anda menetapkan nilai ke kolom MaybeFuzzyDateProperty, MaybeFuzzyDateProperty._validate() dan FuzzyDateProperty._validate() akan dipanggil dalam urutan tersebut.
Hal yang sama berlaku untuk _to_base_type() dan
_from_base_type(): metode di dalam
superclass dan subclass digabungkan secara implisit.
(Jangan gunakan super untuk mengontrol perilaku yang diwariskan.
Untuk ketiga metode ini,
interaksinya halus dan super tidak melakukan apa yang Anda inginkan.)