Menulis bidang model penyesuaian¶
Kata Pengantar¶
Dokumentasi model reference menjelaskan bagaimana menggunakan kelas-kelas bidang standar Django -- CharField
, DateField
, dll. Untuk banyak tujuan, kelas-kelas tersebut adalah yang anda akan butuhkan. Terkadang, meskipun, versi Django tidak akan bertemu persyaratan tepat anda, atau anda akan menggunakan sebuah bidang yang seluruhnya berbeda dari yang dikemas dengan Django.
Jenis bidang siap pakai Django tidak mencangkup setiap jenis kolom basisdata yang mungkin -- hanya jenis umum, seperti VARCHAR
dan INTEGER
. Untuk lebih mengaburkan jenis kolom, seperti poligon geografis atau bahkan jenis dibuat-pengguna seperti PostgreSQL custom types, anda dapat menentukan subkelas-subkelas Field
Django and sendiri.
Jalan lain, anda mungkin mempunyai obyek Python rumit yang dapat entah bagaimana disambungkan untuk cocok kedalam jenis kolom basisdata standar. Ini adalah kasus lain dimana sebuah subkelas Field
akan membantu anda menggunakan obyek anda dengan model anda.
Obyek contoh kami¶
Membuat penyesuaian bidang membuatuhkan sedikit perhatian ke rincian. Untuk membuat hal-hal lebih mudah untuk diikuti, kami akan menggunakan contoh tetap melalui dokumen ini: membungkus sebuah obyek Python mewakili persetujuan dari kartu-kartu dalam sebuah tangan dari Bridge. Jangan khawatir, anda tidak perlu mengetahui bagaimana bermain Bridge untuk mengikuti contoh ini. Anda hanya perlu mengetahui bahwa 52 kartu dibagikan sama ke empat pemain, yang secara tradisional dipanggil utara, timur, selatan dan barat. Kelas kami kelihatan seperti ini:
class Hand:
"""A hand of cards (bridge style)"""
def __init__(self, north, east, south, west):
# Input parameters are lists of cards ('Ah', '9s', etc.)
self.north = north
self.east = east
self.south = south
self.west = west
# ... (other possibly useful methods omitted) ...
Ini hanyalah kelas Python asli, dengan tidak ada spesifik-Django tentangnya. Kami akan dapat melakukan hal-hal seperti ini dalam model kami (kami menganggap atribut hand
pada model adalah sebuah contoh dari Hand
):
example = MyModel.objects.get(pk=1)
print(example.hand.north)
new_hand = Hand(north, east, south, west)
example.hand = new_hand
example.save()
Kami memberikan dan mengambil dari atribut hand
dalam model kami seperti kelas Python lainnya. Tipuannya adalah mengatakan Django bagaimana menangani penyimpanan dan memuat sebuah obyek seperti itu.
Agar menggunakan kelas Hand
di model kami, kami tidak harus merubah kelas ini sama sekali. Ini adalah cocok, karena dia berarti anda dapat dengan mudah menulis model mendukung kelas-kelas yang ada dimana anda tidak dapat merubah sumber kode.
Catatan
Anda mungkin hanya menginginkan untuk mengambil keuntungan dari penyesuaian jenis kolom basisdata dan berhubungan dengan data sebagai jenis Python standar dalam model anda; string, atau float, sebagai contoh. Kasus ini mirip pada contoh hand
kami dan kami akan mencatat perbedaan ketika kita pergi bersama.
Latar belakang teori¶
Penyimpanan basisdata¶
Jalan paling sederhana memikirkan sebuah bidang model adalah dia menyediakan sebuah jalan untuk mengambil sebuah obyek Pyton biasa -- string, boolean, datetime
, atau sesuatu lebih rumit seperti Hand
-- dan merubahnya ke dan dari sebuah bentuk yang berguna ketika berhubungan dengan basisdata (dan penyambungan, tetapi, seperti yang akan kita lihat nanti, yang jatuh keluar secara alami sekali anda mempunyai sisi basisdata dibawah kendali).
Bidang dalam sebuah model harus bagaimanapun dipindahkan untuk cocok kedalam jenis kolom basisdata yang ada. Basisdata yang berbeda menyediakan kumpulan berbeda dari jenis kolom sah, tetapi aturan masih sama: yaitu hanya jenis anda harus bekerja dengannya. Apapun anda ingin simpan dalam basisdata harus cocok kedalam satu dari jenis itu.
Biasanya, anda antara menulis sebuah bidang Django untuk mencocokkan jenis kolom basisdata tertentu, atau ada jalan cukup mudah untuk merubah data anda menjadi, katakan, sebuah deretan karakter.
Untuk contoh Hand
kami, kami dapat merubah kartu data menjadi deretan karakter dari 104 karakter dengan menambah semua kartu bersama-sama dalam yang telah ditetapkan sebelumnya -- katakan, semua kartu utara dahulu, kemudian kartu timur, selatan dan barat. Hadi obyek Hand
dapt disimpan ke kolom teks atau karakter dalam basisdata.
Apakah yang kelas bidang lakukan?¶
Semua bidang Django (dan ketika kami katakan bidang dalam dokumen ini, kami selalu berarti bidang model dan bukan form fields) adalah subkelas dari django.db.models.Field
. Kebanyakan informasi yang Django rekam mengenai sebuah bidang adalah umum bagi semua teman -- nama, bantuan, teks, keunikan dan sebagainya. Menyimpan semua informasi itu yang ditangani oleh Field
. Kami akan memasuki rincian tepat dari Field
apa yang dapat dilakukan kemudian; untuk sekarang, cukuplah untuk mengatakan bahwa segala sesuatu turun dari Field
dan kemudian menyesuaian potongan kunci dari kebiasaan kelas.
Adalah penting untuk menyadari bahwa sebuah kelas bidang Django bukan apa yang disimpan dalam atribut model anda. Pengaturan model mengandung obyek Python biasa. Kelas-kelas bidang yang anda tentukan dalam sebuah model sebenarnya disimpan dalam kelas Meta
ketika kelas model dibuat (rincian tepatnya bagaimana ini dilakukan adalah tidak penting disini). Ini karena kelas-kelas bidang tidak perlu ketika anda hanya membuat dan merubah atribut. Sebagai gantinya, mereka menyediakan mesin-mesin untuk merubah diantara nilai atribut dan apa yang disimpan dalam basisdata atau dikirim ke serializer.
Ingatlah ini ketika membuat penyesuaian bidang sendiri. Subkelas Field
Django anda tulis menyediakan mesin-mesin untuk merubah diantara contoh Python anda dan nilai database/serializer dalam beragam cara (ada perbedaan diantara menyimpan sebuah nilai dan menggunakan sebuah nilai untuk pencarian, sebagai contoh). Jika ini terdengar sedikit rumit, jangan khawatir -- dia akan menjadi lebih jelas dalam contoh dibawah ini. Ingatlah bahwa anda akan sering membuat dua kelas ketika anda ingin penyesuaian bidang:
- Kelas pertama adalah obyek Python dimana pengguna anda akan merubah. Mereka akan menetapkannya ke atribut model, mereka akan membaca darinya untuk menampilkan tujuan, hal-hal seperti itu. ini adalah kelas
hand
dalam contoh kami. - Kelas kedua adalah subkelas
Field
. Ini adalah kelas yang diketahui bagaimana merubah kelas pertama anda ke belakang dan seterusnya diantara bentuk penyimpanan tetap dan bentuk Python.
Menulis sebuah subkelas bidang¶
Ketika merencanakan subkelas Field
anda, pertama berikan beberapa diduga yang mana ada kelas Field
bidang baru anda yang paling mirip. Dapatkah anda mensubkelaskan bidang Django yang ada dan menyimpan diri anda beberapa pekerjaan? Jika tidak, anda harus mensubkelaskan kelas Field
, yang semuanya turun.
Inisialisasi bidang baru anda adalah sebuah masalah dari memisahkan argumen apapun yang khusus pada kasus anda dari argumen umum dan melewatkannya ke cara __init__()` dari Field
(atau kelas induk anda).
Dalam contoh kami, kami akan memanggil bidang HandField
. (Adalah ide bagus untuk memanggil subkelas Field
anda <Something>Field
, sehingga sangat mudah dicirikan sebagai sebuah subkelas Field
.) Dia tidak berperilaku seperti bidang lainnya yang ada, jadi kami akan mensubkelaskan secara langsung dari Field
:
from django.db import models
class HandField(models.Field):
description = "A hand of cards (bridge style)"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 104
super().__init__(*args, **kwargs)
HandField
kami menerima kebanyakan pilihan bidang standar (lihat daftar dibawah ini), tetapi kami memastikan dia mempunyai panjang tetap, sejak dia hanya butuh menampung 52 nilai kartu ditambah pasangan mereka; 104 karakter jumlahnya.
Catatan
Banyak bidang model Django menerima pilihan dimana mereka tidak melakukan apapun. Sebagai contoh, anda dapat melewatkan kedua editable
dan auto_now
ke a django.db.models.DateField
dan dia dengan mudah mengabaikan parameter editable
(auto_now
menjadi disiratkan editable=False
). Tidak ada kesalahan dimunculkan dalam kasus ini.
Kebiasaan ini menyederhanakan kelas-kelas bidang, karena mereka tidak butuh memeriksa untuk pilihan yang tidak dibutuhkan. Mereka hanya melewatkan semua pilihan ke kelas induk dan kemudian tidak menggunakan mereka kemudian. Itu terserah anda apakah anda ingin bidang anda lebih tegas tentang pilihan mereka pilih, atau menggunakan lebih sederhana, kebiasaan yang tidak wajib dari bidang saat ini.
Cara Field.__init__()
mengambil parameter berikut:
verbose_name
nama
primary_key
max_length
unique
blank
null
db_index
rel
: Digunakan untuk bidang terkait (sepertiForeignKey
). Hanya untuk penggunaan lanjut.- attr:~django.db.models.Field.default
editable
serialize
: JikaFalse
, bidang tidak akan didisambungkan ketika model dilewatkan ke serializers Django. Nilai awal keTrue
.unique_for_date
unique_for_month
unique_for_year
choices
help_text
db_column
db_tablespace
: hanya untuk pembuatan indeks, jika dukungan backend tablespaces. Anda dapat biasanya mengabaikan pilihan ini.auto_created
:True
jika bidang otomatis telah dibuat, adapunOneToOneField
digunakan oleh warisan model. Untuk penggunaan hanya tingkat lanjut.
Semua pilihan tanpa sebuah penjelasan dalam daftar diatas mempunyai arti yang sama mereka lakukan untuk bidang Django biasa. Lihat field documentation untuk contoh dan rincian.
Membongkar bidang¶
The counterpoint to writing your __init__()
method is writing the
deconstruct()
method. It's used during model migrations to tell Django how to take an instance of your new field
and reduce it to a serialized form - in particular, what arguments to pass to
__init__()
to re-create it.
Jika anda belum menambahkan pilihan tambahan pada bagian atas bidang anda warisi, lalu tidak perlu untuk menulis cara deconstruct()
baru. Jika, bagaimanapun, anda sedang merubah argumen dilewati dalam __init__()
(seperti kami dalam HandField
), anda akan butuh tambahan nilai yang sedang dilewati.
Kontrak dari deconstruct()
adalah sederhana; dia mengembalikan sebuah tuple dari empat barang: nama atribut bidang, jalur impor penuh dari kelas bidang, penempatan argumen (sebagai sebuah daftar), dan argumen kata kunci (sebagai pendiktean). Catat bahwa ini berbeda dari cara deconstruct()
for custom classes yang mengembalikan sebuah tuple dari tiga hal.
Sebagai sebuah penyesuaian bidang penulis, anda tidak perlu memperhatikan tentang dua nilai pertama; kelas Field
dasar mempunyai semua kode untuk bekerja nama atribut bidang dan mengimpor jalur. Anda, bagaimanapun, harus memperhatikan tentang penempatan dan katakunci argumen, seperti ini mirip hal-hal anda sedang rubah.
Sebagai contoh, dalam kelas HandField
kami selalu memaksa menyetel max_length dalam __init__()
. Cara deconstruct()
dalam basis kelas Field
akan melihat ini dan mencoba mengembalikannya dalam argumen katakunci; demikian, kami dapat membuangnya dari argumen katakunci untuk kesiapan:
from django.db import models
class HandField(models.Field):
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 104
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
del kwargs["max_length"]
return name, path, args, kwargs
If you add a new keyword argument, you need to write code in deconstruct()
that puts its value into kwargs
yourself. You should also omit the value
from kwargs
when it isn't necessary to reconstruct the state of the field,
such as when the default value is being used:
from django.db import models
class CommaSepField(models.Field):
"Implements comma-separated storage of lists"
def __init__(self, separator=",", *args, **kwargs):
self.separator = separator
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
# Only include kwarg if it's not the default
if self.separator != ",":
kwargs['separator'] = self.separator
return name, path, args, kwargs
Contoh lebih rumit diluar cakupan dari dokumen ini, tetapi ingat - untuk konfigurasi apapun dari instance Field anda, deconstruct()
harus mengembalikan argumen yang anda dapat lewati ke __init__
untuk membangun kembali keadaan tersebut.
Beri perhatian lebih jika anda menyetel nilai baru awal untuk argumen dalam super kelas Field
; anda ingin memastikan mereka selalu disertakan, daripada menghilang jika mereka mengambil nilai awal lama.
Dalam tambahan, coba untuk menghindari nilai kembalian sebagai argumen penempatan; jika memungkinkan, kembalikan nilai sebagai argumen katakunci untuk kesesuaian masa depan maksimal. Tentu saja, jika anda merubah nama dari hal-hal sering daripada penempatan mereka dalam daftar argumen pembangun, anda mungkin memilih penempatan, tetapi ingatlah bahwa orang akan membangun kembali bidang ada dari versi bersambung untuk sejenak (mungkin tahunan), tergantung berapa lama perpindahan langsung anda.
Anda dapat melihat hasil dari membongkar dengan mencari dalam perpindahan yang menyertakan bidang, dan anda dapat menguji pembongkaran dalam satuan percobaan dengan hanya membongkar dan membangun kembali bidang.
name, path, args, kwargs = my_field_instance.deconstruct()
new_instance = MyField(*args, **kwargs)
self.assertEqual(my_field_instance.some_attribute, new_instance.some_attribute)
Merubah penyesuaian kelas dasar bidang¶
Anda tidak dapat merubah kelas dasar dari penyesuaian bidang karena Django tidak akan mengenali perubahan dan buat perpindahan untuknya. Sebagai contoh, jika anda mulai dengan:
class CustomCharField(models.CharField):
...
dan kemudian ditentukan bahwa anda ingin menggunakan TextField
sebagai gantinya, anda tidak dapat merubah subkelas seperti ini:
class CustomCharField(models.TextField):
...
Sebagai gantinya, anda harus membuat kelas bidang penyesuaian baru dan perbaharui model anda untuk mengacunya:
class CustomCharField(models.CharField):
...
class CustomTextField(models.TextField):
...
Seperti diobrolkan di removing fields, anda harus memelihara kelas CustomCharField
asli selama anda mempunyai perpindahan yang mengacunya.
Mendokumentasikan bidang penyesuaian anda¶
Seperti biasa, anda harus mendokumentasikan jenis bidang anda, sehingga pengguna akan mengetahui apa itu. Dalam tambahan untuk menyediakan docstring untuknya, mana yang berguna untuk pengembang, anda dapat juga mengizinkan aplikasi admin untuk melihat gambaran pendek dari jenis bidang melalui aplikasi django.contrib.admindocs. Untuk melakukan ini cukup menyediakan gambaran teks dalam sebuah atribut kelas description
dari bidang penyesuaian anda. Dalam contoh diatas, gambaran ditampilkan oleh aplikasi admindocs
untuk sebuah HandField
akan menjadi 'Sebuah tangan dari kartu (gaya bridge)'.
Dalam tampilan django.contrib.admindocs
, gambaran bidang ditambahkan dengan field.__dict__
yang mengizinkan gambaran pada argumen yang digabungkan dari bidang. Sebagai contoh, gambaran untuk CharField
adalah:
description = _("String (up to %(max_length)s)")
Cara berguna¶
Sekali anda telah membuat subkelas Field
anda, anda boleh mempertimbangkan mengutamakan sedikit metode standar, tergantung pada perilaku bidang anda. Daftar dari cara dibawah ini adalah dalam lebih kurang menurun urutan dari kepentingan, jadi mulai dari atas.
Jenis basisdata penyesuaian¶
Katakan anda telah membuat jenis penyesuaian PostgreSQL dipanggil mtype
. Anda dapat mensubkelaskan Field
dan menerapkan cara db_type()
, seperti begitu:
from django.db import models
class MytypeField(models.Field):
def db_type(self, connection):
return 'mytype'
Sekali anda mempunyai MytypeField
, anda dapat menggunakannya di model apapun, seperti jenis Field
apapun lainnya
class Person(models.Model):
name = models.CharField(max_length=80)
something_else = MytypeField()
Jika anda menuju membangun aplikasi agnostik-basisdata, anda harus melaporkan untuk perbedaan dalam jenis kolom basisdata. Sebagai contoh, jenis kolom date/time dalam PostgreSQL dipanggil timestamp
, sementara kolom yang sama dalam MySQL dipanggil datetime
. Jalan termudah untuk menangani ini dalam cara db_type()
adalah untuk memeriksa atribut connection.settings_dict['ENGINE']
.
Sebagai contoh:
class MyDateField(models.Field):
def db_type(self, connection):
if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
return 'datetime'
else:
return 'timestamp'
Cara db_type()
dan rel_db_type()
dipanggil oleh Django ketika kerangka kerja membangun pernyataan CREATE TABLE
untuk aplikasi anda -- yaitu, ketika anda pertama membuat tabel anda. Cara-cara juga dipanggil ketika membangun ketentuan WHERE
yang menyertakan bidang model -- yaitu, ketika anda mengambil data menggunakan cara QuerySet seperti get()
, filter()
, dan exclude()
dan mempunyai bidang model sebagai sebuah argumen. Mereka tidak dipanggil pada waktu lain, jadi dia dapat mampu menjalankan sedikit kode rumit, seperti pemeriksaan connection.settings_dict
di contoh diatas.
Beberapa jenis kolom basisdata menerima parameter, seperti CHAR(25)
, dimana parameter 25
mewakili panjang kolom maksimum. Dalam kasus seperti ini, dia lebih supel jika parameter ditentukan dalam model daripada menjadi kode-keras dalam cara db_type()
. Sebagai contoh, dia tidak akan masuk akan untuk mempunyai CharMaxlength25Field
, ditampilkan disini:
# This is a silly example of hard-coded parameters.
class CharMaxlength25Field(models.Field):
def db_type(self, connection):
return 'char(25)'
# In the model:
class MyModel(models.Model):
# ...
my_field = CharMaxlength25Field()
Jalan lebih baik dari mengerjakan ini adalah membuat parameter dapat lebih khusus pada wktu berjalan -- yaitu, ketika kelas dipakai. Untuk melakukan itu, cukup terapkan Field.__init__()
, seperti begitu:
# This is a much more flexible example.
class BetterCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super().__init__(*args, **kwargs)
def db_type(self, connection):
return 'char(%s)' % self.max_length
# In the model:
class MyModel(models.Model):
# ...
my_field = BetterCharField(25)
Akhirnya, jika kolom anda membutuhkan pengaturan SQL yang rumit, kembalikan None
dari db_type()
. Ini akan menyebabkan kode pembuatan SQL Django melewati terhadap bidang ini. Anda kemudian bertanggung jawab untuk membuat kolom dalam tabel yang benar dalam cara lain, tentu saja, tetapi ini memberikan anda sebuah jalan untuk memberitahu Django untuk keluar dari jalan.
Cara rel_db_type()
dipanggil oleh bidang seperti ForeignKey
and OneToOneField
yang menunjuk ke bidang lain untuk menentukan jenis data kolom basisdata mereka. Sebagai contoh, jika anda mempunyai sebuah UnsignedAutoField
, anda juga butuh foreign key yang menunjuk ke bidang itu untuk menggunakan jenis data yang sama:
# MySQL unsigned integer (range 0 to 4294967295).
class UnsignedAutoField(models.AutoField):
def db_type(self, connection):
return 'integer UNSIGNED AUTO_INCREMENT'
def rel_db_type(self, connection):
return 'integer UNSIGNED'
Mengubah nilai menjadi obyek Python¶
Jika penyesuaian kelas Field
anda berhubungan dengan struktur dara yang lebih rumit daripada string, date, integer, atau float, kemudian anda butuh untuk menimpa from_db_value()
dan to_python()
.
Jika ada untuk bidang subkelas, from_db_value()
akan dipanggil dalam semua keadaan ketika data dimuat dari basisdata, termasuk dalam keseluruhan dan panggilan values()
.
to_python()
dipanggil dengan memutuskan dan selama cara clean()
digunakan dari formulir.
Sebagai aturan umum, to_python()
harus berurusan secara anggun dengan salah satu dari argumen berikut:
- Sebuah contoh dari jenis yang benar (sebagai contoh,
Hand
dalam contoh kami yang sedang berjalan). - Sebuah string
None
(jika bidang mengizinkannull=True
)
Dalam kelas HandField
, kami menyimpan data sebagai bidang VARCHAR dalam basisdata, jadi kami butuh dapat mengolah deretan karakter dan None
dalam from_db_value()
. Di to_python()
, kami butuh juga menangani instance Hand
:
import re
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
def parse_hand(hand_string):
"""Takes a string of cards and splits into a full hand."""
p1 = re.compile('.{26}')
p2 = re.compile('..')
args = [p2.findall(x) for x in p1.findall(hand_string)]
if len(args) != 4:
raise ValidationError(_("Invalid input for a Hand instance"))
return Hand(*args)
class HandField(models.Field):
# ...
def from_db_value(self, value, expression, connection):
if value is None:
return value
return parse_hand(value)
def to_python(self, value):
if isinstance(value, Hand):
return value
if value is None:
return value
return parse_hand(value)
Perhatikan bahwa kami selalu mengembalikan sebuah instance hand
dari cara ini. Itu adalah jenis obyek Python kami ingin simpan dalam atribut model.
Untuk to_python()
, jika apapun berjalan salah selama perubahan nilai, anda harus memunculkan pengecualian ValidationError
.
Mengubah obyek Python ke nilai pencarian¶
Sejak menggunakan sebuah basisdata membutuhkan perubahan dalam kedua jalan, jika anda menimpa to_python()
anda juga harus menimpa get_prep_value()
untuk merubah obyek Python kembali ke nilai permintaan.
Sebagai contoh:
class HandField(models.Field):
# ...
def get_prep_value(self, value):
return ''.join([''.join(l) for l in (value.north,
value.east, value.south, value.west)])
Peringatan
Jika bidang penyesuaian anda menggunakan jenis CHAR
, VARCHAR
atau TEXT
untuk MySQL, anda harus memastikan bahwa get_prep_value()
selalu mengembalikan jenis string. MySQL melakukan luwas dan pencocokan tidak diharapkan ketika sebuah permintaan dilakukan pada jenis ini dan nilai yang disediakan adalah sebuah integer, yang dapat menyebabkan permintaan untuk menyertakan obyek tidak diharapkan dalam pencarian mereka. Masalah ini tidak dapat timbul jika anda selalu mengembalikan jenis string dari get_prep_value()
.
Mengubah nilai pencarian ke nilai basisdata¶
Beberapa jenis data (sebagai contoh, tanggal) butuh dalam bentuk yang khusus sebelum mereka dapat digunakan oleh backend basisdata. get_db_prep_value()
adalah cara dimana perubahan tersebut harus dibuat. Hubungan khusus yang akan digunakan untuk permintaan dilewati sebagai parameter connection
. Ini mengizinkan anda menggunakan logika perubahan khusus-backend jika dia diwajibkan.
Sebagai contoh, Django menggunakan cara berikut untuk BinaryField
nya:
def get_db_prep_value(self, value, connection, prepared=False):
value = super().get_db_prep_value(value, connection, prepared)
if value is not None:
return connection.Database.Binary(value)
return value
Dalam kasus bidang penyesuaian anda butuh perubahan khusus ketika sedang disimpan yang tidak sama seperti perubahan digunakan untuk parameter permintaan biasa, anda dapat menimpa get_db_prep_save()
.
Mengolah nilai sebelum menyimpan¶
Jika anda ingin prapengolahan nilai sebelum menyimpan, anda dapat menggunakan pre_save()
. Sebagai contoh, DateTimeField
Django menggunakan cara ini untuk menyetel atribut dengan benar dalam kasus dari auto_now
atau auto_now_add
.
Jika anda melakukan menimpa metode ini, anda harus mengembalikan nilai dari atribut di akhirnya. Anda harus juga memperbaharui atribut model jika anda membuat perubahan apapun ke nilai sehingga kode menahan acuan ke model akan selalu melihat nilai benar.
Menentukan bidang formulir untuk sebuah bidang model¶
Untuk menyesuaikan bidang formulir digunakan oleh ModelForm
, anda dapat menimpa formfield()
.
Kelas bidang formulir dapat ditentukan melalui argumen form_class
dan choices_form_class
; yang terakhir digunakan jika bidang mempunyai pilihan yang ditentukan, yang depan kebalikannya. Jika argumen ini tidak disediakan, CharField
atau TypedChoiceField
akan digunakan.
Semua kamus kwargs
dilewatkan secara langsung ke formulir cara __init__()
bidang. Biasanya, semua anda butuh lakukan adalah menyetel nilai awal bagus untuk form_class
argumen (dan mungkin choices_form_class
) dan kemudian mengutus penanganan lebih lanjut untuk kelas induk. Ini mungkin membutuhkan anda menulis bidang formulir penyesuaian (dan bahkan sebuah widget formulir). Lihat forms documentation untuk informasi tentang ini.
Melanjutkan contoh berlangsung kita, kami dapat menulis cara formfield()
sebagai:
class HandField(models.Field):
# ...
def formfield(self, **kwargs):
# This is a fairly standard way to set up some defaults
# while letting the caller override them.
defaults = {'form_class': MyFormField}
defaults.update(kwargs)
return super().formfield(**defaults)
Ini menganggap kami telah mengimpor kelas bidang MyFormField
(yang mempunyai widget awal sendiri). Dokumen ini tidak mencakup rincian dari penulisan bidang formulir penyesuaian.
Menyamai jenis bidang siap pakai¶
Jika anda telah membuat cara db_type()
, anda tidak butuh khawatir tentang get_internal_type()
-- dia tidak akan digunakan. Terkadang, meskipun, penyimpanan basisdata anda mirip dalam jenis pada beberapa bidang lain, jadi anda dapat menggunakan logika bidang lain itu untuk membuat kolum yang benar.
Sebagai contoh:
class HandField(models.Field):
# ...
def get_internal_type(self):
return 'CharField'
Tidak penting backend basisdata anda kami gunakan, ini akan berarti bahwa migrate
dan perintah SQL lainnya membuat jenis kolom benar untuk menyimpan sebuah deretan karakter.
Jika get_internal_type()
megembalikan sebuah deretan karakter yang tidak diketahui ke Django untuk backend basisdata anda sedang gunakan -- yaitu, dia tidak muncul dalam django.db.backends.<db_name>.base.DatabaseWrapper.data_types
-- deretan karakter akan masih digunakan oleh penyambung, tetapi cara db_type()
awal akan mengembalikan None
. Lihat dokumentasi dari db_type()
untuk alasan mengapa ini mungkin berguna. menaruh sebuah gambaran deretan karakter kedalam sebagai jenis dari bidang untuk penyambung adalah ide berguna jika anda sedang akan menggunakan keluaran penyambung dalam beberapa tempat lainnya, diluar Django.
Mengubah data field untuk serialisasi¶
Untuk menyesuaikan bagaimana nilai diserialkan oleh penserial, anda dapat menimpa value_to_string()
. menggunakan value_from_object()
adalah jalan terbaik untuk mendapatkan nilai bidang sebelum serialisasi. Sebagai contoh, sejak HandField
menggunakan string untuk penyimpanan data, kami dapat menggunakan kembali beberapa kode perubahan yang ada:
class HandField(models.Field):
# ...
def value_to_string(self, obj):
value = self.value_from_object(obj)
return self.get_prep_value(value)
Beberapa saran umum¶
Menulis sebuah bidang penyesuaian dapat menjadi pengolahan yang rumit, khususnya jika anda sedang melakukan perubahan rumit diantara jenis Python anda dan basisdata anda dan bentuk penyambungan. Ini adalah sepasang tip untuk membuat hal-hal berjalan lebih halus:
- Lihat bidang Django yang ada (dalam
django/db/models/fields/__init__.py
) untuk inspirasi. Coba temukan bidang yang mirip pada apa anda inginkan dan perpanjang dia sedikit, daripada membuat sebuah keseluruhan bidang baru dari awal. - Taruh sebuah metode
__str__()
pada kelas anda sedang bungkus sebagai sebuah bidang. Ada banyak tempat-tempat dimana perilaku awalan dari kode bidang adalah memanggilstr()
pada nilai. (Dalam contoh kami dalam dokumen ini,value
akan berupa instancehanda
, bukanhandField
). Jadi jika metode__str__()
anda secara otomatis merubah ke bentuk string dari obyek Python anda, anda dapat menyimpan anda sendiri banyak pekerjaan.
Menulis subkelas FileField
¶
Dalam tambahan pada cara diatas, bidang-bidang yang berurusan dengan berkas-berkas mempunyai sedikit persyaratan khusus lainnya yang harus diambil kedalam akun. Kebanyakan dari mekanisme disediakan oleh FileField
, seperti mengendalikan penyimpanan basisdata dan pengambilan, dapat tetap tidak berubah, meninggalkan subkelas-subkelas untuk berurusan dengan tantangan dari mendukung jenis khusus dari berkas.
Django menyediakan sebuah kelas File
, yang digunakan sebagai sebuah proxy pada isi berkas dan tindakan. Ini dapat disubkelaskan ke menyesuaikan bagaimana berkas diakses, dan cara apa yang tersedia. Dia tinggal di django.db.models.fields.files
, dan kebiasaan awalnya adalah dijelaskan dalam file documentation.
Sekali sebuah subkelas dari File
dibuat, subkelas FileField
harus diberitahu untuk menggunakannya. Untuk melakukannya, cukup tetapkan subkelas File
baru ke atribut attr_class
khusus dari subkelas FileField
.
Sedikit saran¶
Sebagai tambahan pada rincian diatas, terdapat sedikit panduan yang dapat meningkatkan efisiensi dan kesiapan dari kode bidang.
- Sumber untuk
ImageField
sendiri Django (dalamdjango/db/models/fields/files.py
) adalah contoh hebat dari bagaimana untuk mensubkelaskanFileField
untuk mendukung jenis khusus dari berkas, karena menggabungkan semua teknik digambarkan diatas. - Menembolok atribut berkas dimanapaun memungkinkan. Sejak berkas-berkas mungkin disimpan dalam sistem penyimpanan jauh, mengambil mereka mungkin memakan waktu tambahan, atau bahkan uang, yang tidak selalu dibutuhkan. Sekali sebuah berkas diambil untuk mendapatkan beberapa data tentang isinya, simpan sebanyak mungkin data tersebut untuk mengurangi jumlah kali berkas harus diambil pada pemanggilan berikut untuk informasi itu.