Cetakan bahasa Django datang dengan beragam luas dari built-in tags and filters dirancang untuk megnalamatkan kebutuhan pembawaan logis dari aplikasi anda. Namun, anda mungkin menemukan diri anda sendiri membutuhkan fungsi yang tidak dicakupi oleh inti kumpulan dari cetakan primitif. Anda dapat memperpanjang cetakan mesin dengan menentukan penyesuaian etiket dan penyaring menggunakan Python, dan membuat mereka tersedua pada cetakan anda menggunakan etiket {% load %}.
Tempat paling umum untuk menentukan cetakan etiket dan penyaring adalah didalam aplikasi Django. Jika mereka terhubung pada aplikasi yang ada, dia masuk akal untuk menggabungkan mereka; kalau tidak, mereka dapat ditambahkan ke aplikasi baru. Ketika aplikasi Django ditambahkan ke INSTALLED_APPS, etiket apapun dia tentukan dalam tempat biasa dibawah otomatis dibuat tersedia untuk dimuat dengan cetakan.
Aplikasi harus mengandung sebuah direktori templatetags, pada tingkatan sama seperti models.py, views.py, dll. Jika ini tidak ada, buat dia - jangan lupakan berkas __init__.py untuk memastikan direktori diperlakukan sebagai paket Python.
Penyesuaian etiket dan penyaring anda akan berada dalam sebuah modul didalam direktori templatetags. Nama dari berkas modul adalah nama anda akan gunakan untuk memuat etiket kemudian, jadi berhati-hatilah untuk mengambil sebuah nama yang tidak akan bentrok dengan penyesuaian etiket dan penyaring di aplikasi lain.
Sebagai contoh, jika penyesuaian etiket/penyaring anda berada dalam sebuah berkas yang dipanggil poll_extras.py, tata letak eplikasi anda mungkin terlihat seperti ini:
Dan di cetakan anda, anda akan menggunakan berikut:
Django template
{%loadpoll_extras%}
Aplikasi yang mengandung penyesuaian etiket harus berada dalam INSTALLED_APPS agar etiket {% load %} bekerja. Ini adalah fitur keamanan: Dia mengizinkan anda untuk menempatkan kode Python untuk banyak cetakan pustaka pada sebuah mesin rumah tunggal tanpa mengadakan akses ke semua dari mereka untuk setiap pemasangan Django.
Tidak ada batasan pada bagaimana banyak modul anda taruh dalam paket templatetags. Hanya perlu diingat bahwa sebuah pernyataan {% load %} akan memuatetiket/penyaring untuk nama modul Python yang diberikan, bukan nama dari aplikasi.
Untuk menjadi pustaka sah, modul harus mengandung sebuah variabel tingkat-modul yang dinamai register yaitu sebuah instance template.Library, dimana semua etiket dan penyaring didaftarkan. Jadi dekat diatas modul anda, taruh berikut:
Kalau tidak, cetakan modul etiket dapat didaftarkan melalui argumen 'libraries' ke DjangoTemplates. Ini berguna jika anda ingin menggunakan label berbeda dari nama cetakan modul etiket ketika memuat cetakan etiket. Dia juga mengadakan anda untuk mendaftar etiket tanpa memasang sebuah aplikasi.
Saringan penyesuaian hanyalah fungsi fungsi yang mengambil satu atau dua argumen:
Nilai dari variabel (masukan) -- tidak perlu deretan karakter.
Nilai dari argumen -- ini dapat mempunyai nilai awal, atau ditinggalkan sama sekali.
Sebagai contoh, dalam saringan {{ var|foo:"bar" }}, the filter foo akan dilewatkan variabel var dan argumen ''bar''.
Sejak cetakan bahasa tidak menyediakan penanganan pengecualian, setiap pengecualian yang muncul dari sebuah cetakan saringan akan tidak terlindungi sebagai sebuah kesalahan peladen. Dengan demikian, fungsi penyaring harus menghindari memunculkan pengecualian jika ada alasan nilai mundur untuk kembali. Dalam kasus dari masukan yang mewakili kesalahan jelas dalam sebuah cetakan, menampilkan pencegualian mungkin masih lebih baik daripada kegagalan diam yang menyembunyikan kesalahan.
Ini adalah sebuah contoh pengertian saringan
Code
defcut(value, arg):"""Removes all values of arg from the given string"""return value.replace(arg,'')
Dan ini adalah sebuah contoh bagaimana saringan itu akan digunakan:
Django template
{{somevariable|cut:"0"}}
Kebanyakan saringan tidak mengambil argumen. Dalam kasus ini, tinggalkan argumen dari fungsi anda. Contoh:
Code
deflower(value):# Only one argument."""Converts a string into all lowercase"""return value.lower()
Sekali anda telah menulis arti saringan anda, anda butuh mendaftarkannya dengan instance Library anda, untuk membuatnya tersedia pada bahasa cetakan Django:
Jika anda sedang menulis sebuah cetakan penyaring yang hanya menerima sebuah deretan karakter sebagai argumen pertama, anda harus menggunakan decorator stringfilter. Ini akan merubah sebuah obyek ke nilai deretan karakternya sebelum dilewatkan ke fungsi anda:
Jalan ini, anda akan dapat melewatkan, katakan, sebuah integer ke penyaring ini, dan dia tidak menyebabkan sebuah AttributeError (karena integer tidak mempunyai cara lower()).
Ketika menulis sebuah penyesuaian penyaring, berikan beberapa pemikiran tentang bagaimana penyaring akan berinteraksi dengan kebiasaan pelolosan otomatis Django. Catat bahwa tiga jenis deretan karakter dapat dilewatkan disekeliling dalam cetakan kode:
Raw strings adalah jenis str atau unicode Python asli. Pada keluaran, mereka lolos jika pelolosan otomatis ini berlaku dan disajikan tidak berubah, jika tidak.
Safe strings adalah deretan karakter yang telah ditandai aman dari pelolosan lanjut pada waktu keluar. Apa saja yang diperlukan pelolosan telah dilakukan. Mereka umumnya digunakan untuk keluaran yang mengandung HTML mentah yang akan di ditafsirkan apa adanya pada sisi klien.
Secara internal, deretan karakter ini adalah dari jenis SafeBytes atau SafeText. Mereka berbagi kelas dasar umum dari SafeData, sehingga anda dapat mencoba untuk mereka menggunakan kode seperti:
Code
ifisinstance(value, SafeData):# Do something with the "safe" string....
Kode cetakan penyaring gagal kedalam satu dari dua keadaan:
Penyaring anda tidak memperkenalkan salah satu karakter tidak aman (<, >, ', " atau &) kedalam hasil yang tidak hadir. Dalam kasus ini, anda dapat membiarkan Django mengambil semua penanganan pelolosan otomatis untuk anda. Semua anda butuh untuk dilakukan adalah menyetel bendera is_safe ke True ketika anda mendaftar fungsi penyaringan, seperti begitu:
Code
@register.filter(is_safe=True)defmyfilter(value):return value
Bendera ini memberitahu Django bahwa jika sebuah deretan karakter "safe" dilewatkan kedalam penyaring anda, hasil akan masih menjadi "safe" dan jika sebuah deretan karakter tidak aman dilewatkan, Django akan otomatis meloloskannya, jika diperlukan.
Anda dapat berpikir dari ini sebagai berarti "penyaring ini aman -- dia tidak memperkenalkan kemungkinan apapun dari HTML tidak aman."
Alasan is_safe adalah diperlukan bahwa karena ada banyak tindakan deretan karakter biasa yang akan mengembalikan obyek SafeData kembali ke dalam obyek str atau unicode normal, daripada mencoba menangkap semuanya, yang akan sangat sulit, Django memperbaiki kerusakan setelah penyaring telah lengkap.
Sebagi contoh, andaikan anda mempunyai sebuah penyaring yang menambahkan deretan karakter xx ke akhir dari masukan. Sejak ini diperkenalkan karakter HTML tidak berbahaya ke hasil (selain dari apapun itu sudah hadir), anda harus menandai penyaring anda dengan is_safe:
Code
@register.filter(is_safe=True)defadd_xx(value):return'%sxx'% value
Ketika penyaring ini digunakan dalam sebuah cetakan dimana pelolosan otomatis diadakan, Django akan meloloskan keluaran ketika masukan belum ditandai sebagai "safe".
Secara awalan, is_safe adalah False, dan anda dapat menghilangkannya dari saringan dimana dia tidak wajib.
Hati-hati ketika memutuskan jika penyaring anda sangat membiarkan deretan karakter aman sebagai safe. Jika anda sedang memidnahkan karakter, anda mungkin secara tidak sengaja membiarkan etiket HTML tidak seimbang atau kesatuan dalam hasil. Sebagai contoh, memindahkan sebuah > dari masukan mungkin mengubah <a> menjadi <a, yang akan butuh diloloskan pada keluaran untuk menghindari masalah. Demikian pula, memindahkan titik koma (;) dapat merubah & into &, yang tidak lagi kesatuan sah dan demikian butuh pelolosan. Kebanyakan kasus tidak dekat dengan trik ini, tetapi jaga mata anda untuk masalah apapun seperti itu ketika meninjau kode anda.
Menandai sebuah penyaring is_safe akan memaksa nilai kembalian penyaring menjadi deretan karakter. Jika penyaring anda harus mengembalikan sebuah boolean atau nilai bukan deretan karakter, tandai dia is_safe akan mungkin mempunyai konsekuensi yang tidak diinginkan (seperti merubah boolean False menjadi deretan karakter 'False').
Kalau tidak, kode penyaring anda dapat secara manual merawat pelolosan apapun yang dibutuhkan. Ini adalah penting ketika anda sedang memperkenalkan markah HTML baru kedalam hasil. Anda ingin menandai keluaran sebagai aman dari pelolosan lebih lanjut sehingga markah HTML anda tidak diloloskan lebih lanjut, jadi anda akan butuh menangani masukan diri anda sendiri.
Berhati-hatilah, meskipun. Anda butuh melakukan lagi daripada hanya menandai keluaran sebagai aman. Anda butuh memastikan dia sangat adalah aman, dan apa yang anda lakukan tergantung pada apakah pelolosan otomatis berpengaruh. Idenya adalah menulis penyaring yang dapat berjalan dalam cetakan dimana pelolosan otomatis salah satunya nyala atau mati agar membuat hal-hal semakin mudah untuk cetakan penulis anda.
Agar penyaring anda mengetahui keadaan pelolosan otomatis saat ini, setel bendera needs_autoescape ke True ketika anda mendaftar fungsi penyaring anda. (Jika anda tidak menentukan bendera ini, dia awalnya adalah False). Bendera ini mengatakan Django bahwa fungsi penyaring anda ingin dilewatkan sebuah argumen katakunci tambahan, dipanggil autoescape, yaitu True jika pelolosan otomatis dalam efek dan False sebaliknya. Sangat dianjurkan untuk menyetel parameter autoescape ke True, sehingga jika anda memanggil fungsi dari kode Python dia akan mempunyai pelolosan diadakan secara awal.
Sebagai contoh, mari kita menulis penyaring yang menekankan karakter pertama dari sebuah deretan karakter:
Code
fromdjangoimport template
fromdjango.utils.htmlimport conditional_escape
fromdjango.utils.safestringimport mark_safe
register = template.Library()@register.filter(needs_autoescape=True)definitial_letter_filter(text, autoescape=True):
first, other = text[0], text[1:]if autoescape:
esc = conditional_escape
else:
esc =lambda x: x
result ='<strong>%s</strong>%s'%(esc(first), esc(other))return mark_safe(result)
Bendera needs_autoescape dan argumen katakunci autoescape berarti bahwa fungsi kami akan mengetahui apakah pelolosan otomatis berada dalam efek ketika penyaring dipanggil. Kami menggunakan autoescape untuk memutuskan apakah data masukan butuh dilewatkan melalui django.utils.html.conditional_escape atau tidak. (Dalam kasus terakhir, kami hanya menggunakan fungsi identitas sebagai fungsi "escape".) Fungsi conditional_escape() seperti escape() kecuali dia hanya meloloskan masukan yang bukan instance SafeData. Jika sebuah instance SafeData dilewatkan ke conditional_escape(), data dikembalikan tidak berubah.
Akhirnya, dalam contoh diatas, kami ingat untuk menandai hasil sebagai aman sehingga HTML kami dimasukkan langsung kedalam cetakan tanpa pelolosan lanjut.
Tidak perlu khawatir tentang bendera is_safe dalam kasus ini (meskipun termasuknya tidak akaan melukai apapun). Kapanpun anda secara manual menangani masalah pelolosan otomatis dan mengembalikan sebuah deretan karakter aman, bendera is_safe tidak merubah apapun.
Jika anda menulis sebuah penyesuaian penyaring yang dijalankan pada obyek datetime, anda akan biasanya mendaftarkannya dengan bendera expects_localtime disetel ke True:
Ketika bendera ini disetel, jika argumen pertama pada penyaring anda adalah zona waktu perhatikan datetime, Django akan merubahnya ke zona waktu saat ini sebelum melewatkannya ke penyaring anda ketika sesuai, menurut pada aturan untuk perubahan zona waktu dalam cetakan.
Etiket lebih rumit daripada penyaring, karena etiket dapat melakukan apapun. Django menyediakan sejumlah jalan pintas yang membuat penulisan kebanyakan jenis etiket lebih mudah. Pertama kami akan menjelajahi jalan pintas tersebut, kemudian menjelaskan bagaimana menulis sebuah etiket dari goresan untuk kasus-kasus tersebut ketika jalan pintas tidak cukup kuat.
Banyak cetakan etiket mengambil sejumlah argumen -- deretan karakter atau cetakan variabel -- dan mengembalikan sebuah hasil setelah melakukan beberapa pengolahan berdasarkan hanya pada masukan argumen dan beberapa informasi luar. Sebagai contoh, sebuah etiket current_time mungkin menerima sebuah bentuk deretan karakter dan mengembalikan waktu sebagai bentuk deretan karakter.
Untuk meringankan pembuatan dari jenis ini dari etiket, Django menyediakan fungsi pembantu, simple_tag. Fungsi ini, yaitu sebuah cara dari django.template.Library, mengambil sebuah fungsi yang menerima angka apapun dari argumen, membungkusnya dalam fungsi render dan bit-bit yang dibutuhkan lainnya disebutkan diatas dan mendaftarkannya dengan cetakan sistem.
Fungsi current_time kami dapat jadi ditulis seperti ini:
Beberapa hal untuk dicatat tentang fungsi pembantu simple_tag:
Memeriksa angka wajib dari argumen, dll., telah selesai dilakukan oleh waktu fungsi kami dipanggil, jadi kami tidak butuh melakukan itu.
Kutipan disekeliling argumen (jika ada) telah dilucuti, jadi kami hanya menerima deretan karakter kosong.
Jika argumen adalah cetakan variabel, fungsi kami dilewatkan nilai sast ini dari variabel, bukan variabel itu sendiri.
Tidak seperti peralatan etiket lain, simple_tag melewatkan keluarannya melalui conditional_escape() jika konteks cetakan dalam suasana pelolosan otomatis, untuk memastikan HTML benar dan melindungi anda dari kerentanan XSS.
Jika tambahan pelolosan tidak diharapkan, anda akan butuh menggunakan mark_safe() jika anda sepenuhnya yakin bahwa kode anda tidak mengandung kerentanan XSS. Untuk membangun potongan kecil HTML, gunakan format_html() daripada mark_safe() sangat kuat dianjurkan.
Jika etiket cetakan anda butuh untuk mengakses konteks saat ini, anda dapat menggunakan argumen takes_context ketika mendaftarkan etiket anda:
Kemudian dalam cetakan angka apapun dari argumen, dipisahkan oleh spasi, mungkin dilewatkan ke cetakan etiket. Seperti dalam Python, nilai untuk argumen katakunci disetel menggunakan tanda sama dengan ("=") dan harus disediakan setelah penempatan argumen. Sebagai contoh:
Itu dimungkinkan untuk menyimpan hasil etiket dalam sebuah cetakan variabel daripada secara langsung mengeluarkannya. Ini dilakukan dengan menggunakan argumen as diikuti oleh nama variabel. Melakukannya mengadakan anda untuk mengeluarkan isi diri anda dimana anda melihatnya cocok:
Django template
{%current_time"%Y-%m-%d %I:%M %p"asthe_time%}<p>The time is {{the_time}}.</p>
Jenis umum lainnya dari cetakan etiket adalah jenis yang menampilkan beberapa data dengan membangun cetakan lain. Sebagai contoh, antarmuka admin Django menggunakan penyesuaian etiket cetakan untuk menampilkan tombol dibawah dari halaman formulir "add/change". Tombol-tombol tersebut selalu terlihat sama, tetapi ssaran tautan berubah tergantung pada obyek yang sedang disunting -- sehingga mereka kasus sempurna untuk menggunakan cetakan kecil yang diisi dengan rincian dari obyek saat ini. (Dalam kasus admin, ini adalah etiket submit_row.)
Urutan etiket ini dipanggil "penyertaan etiket".
Menulis pencatuman etiket adalah mungkin pertunjukan terbaik dengan contoh. Mari kita menulis sebuah etiket yang mengeluarkan daftar pilihan untuk obyek Poll yang diberikan, seperti dibuat dalam tutorial. Kami akan menggunakan etiket seperti ini:
Pertama, tentukan fungsi yang mengambil argumen dan menghasulkan kamus dari data untuk hasil. Titik terpenting disini adalah kami hanya butuh mengembalikan sebuah kamus, bukan apapun lebih rumit. Ini akan digunakan sebagai konteks cetakan untuk fragmen cetakan. Contoh:
Selanjutnya, buat cetakan digunakan untuk membangun keluaran etiket. Cetakan ini adalah fitur tetap dari etiket: penulis etiket menentukannya, bukan perancang etiket. Mengikuti contoh kami, cetakan sangat mudah:
Sekarang, buat dan daftar penyertaan etiket dengan memanggil cara inclusion_tag() pada sebuah obyek Library. Mengikuti contoh kami, jika cetakan diatas dalam sebuah berkas dipanggil results.html dalam sebuah direktori yang dicari oleh pemuat cetakan, kami akan mendaftar etiket seperti ini:
Code
# Here, register is a django.template.Library instance, as before@register.inclusion_tag('results.html')defshow_results(poll):...
Kemungkinan lain dia memungkinkan mendaftarkan pemasukan etiket menggunakan instace django.template.Template:
Code
fromdjango.template.loaderimport get_template
t = get_template('results.html')
register.inclusion_tag(t)(show_results)
...ketika pertama membuat fungsi.
Terkadang, penyertaan etiket anda mungkin membutuhkan sejumlah besar argumen, membuatnya sakit untuk cetakan penulis melewatkan semua argumen dan mengingat urutan mereka. Untuk mengatasi ini, Django menyediakan sebuah pilihan takes_context untuk penyertaan etiket. Jika anda menentukan takes_context dalam sebuah cetakan etiket, etiket tidak akan mempunyai argumen wajib, dan pokok fungsi Python akan mempunyai satu argumen -- konteks cetakan mulai ketika etiket dipanggil.
Sebagai contoh, katakan anda sedang menulis sebuah penyertaan etiket yang akan selalu digunakan dalam sebuah konteks yang mengandung variabel home_link dan home_title yang menunjuk kembali ke halaman utama. Disini fungsi Python akan kelihatan seperti:
Catat bahwa parameter pertama pada fungsi harus dipanggil context.
Dalam baris register.inclusion_tag() itu, kami mencirikan takes_context=True dan nama dari cetakan. Ini adalah cetakan link.html mungkin kelihatan seperti:
Django template
Jump directly to <ahref="{{link}}">{{title}}</a>.
Lalu, kapanpun anda ingin menggunakan tag tersuai, muat pustakanya dan panggil tanpa argument, seperti:
Django template
{%jump_link%}
Catat bahwa ketika anda menggunakan takes_context=True, tidak perlu melewatkan argumen ke etiket cetakan. Dia otomatis mendapatkan akses ke konteks.
Parameter takes_context awalnya adalah False. Ketika disetel ke True, etiket melewatkan obyek konteks, seperti dalam contoh ini. Itu hanya oerbedaan diantara kasus ini dan contoh inclusion_tag sebelumnya.
Fungsi inclusion_tag mungkin menerima angka apapun dari penempatan atau argumen kata kunci. Sebagai contoh:
Kemudian dalam cetakan angka apapun dari argumen, dipisahkan oleh spasi, mungkin dilewatkan ke cetakan etiket. Seperti dalam Python, nilai untuk argumen katakunci disetel menggunakan tanda sama dengan ("=") dan harus disediakan setelah penempatan argumen. Sebagai contoh:
Untuk meringankan pembuatan etiket pengaturan sebuah variabel dalam konteks, Django menyediakan sebuah fungsi pembantu, assignment_tag. Fungsi ini bekerja di jalan yang sama sebagai simple_tag() kecuali dia menyimpan hasil etiket dalam variabel konteks ditentukan daripada secara langsung mengeluarkannya.
Fungsi current_time awal kami dapat ditulis seperti ini:
Anda dapat menyimpan hasil dalam sebuah variabel cetakan menggunakan argumen as diikuti oleh nama variabel, dan keluarannya itu sendiri dimana anda melihatnya sesuai:
Django template
{%get_current_time"%Y-%m-%d %I:%M %p"asthe_time%}<p>The time is {{the_time}}.</p>
Terkadang fitur dasar untuk pembuatan etiket cetakan penyesuaian tidak cukup. Jangan khawatir, Django memberikan anda akses lengkap ke internal yang diwajibkan untuk membangun sebuah etiket cetakan dari bawah ke atas.
Sistem cetakan bekerja dalam dua langkah pengolahan: menyusun dan membangun. Untuk menentukan etiket cetakan penyesuaian, anda menentukan bagaimana penyusunan bekerja dan bagaimana pembangunan bekerja.
Ketika Django menyusun sebuah cetakan, dia memisahkan teks cetakan mentah kedalam ''nodes''. Setiap node adalah sebuah instance dari django.template.Node dan mempunyai sebuah cara render(). Sebuah cetakan tersusun adalah, sederhana, sebuah daftar dari obyek Node. Ketika anda memanggil render() pada sebuah obyek cetakan disusun, cetakan memanggil render() pada setiap Node dalam daftar nodenya, dengan konteks yang diberikan. Hasil adalah semua digabungkan bersama untuk membentuk keluaran dari cetakan.
Jadi, untuk menentukan penyesuaian cetakan etiket, anda menentukan bagaimana cetakan etiket mentah dirubah kedalam Node (fungsi penyusunan), dan apa cara render() node lakukan.
Untuk setiap etiket cetakan pertemuan pengurai cetakan, dia memanggil fungsi Python dengan isi etiket dan pengurai obyek itu sendiri. Fungsi ini bertanggungjawab untuk mengembalikan sebuah instance Node berdasarkan pada isi dari etiket.
Sebagai contoh, mari kita menulis penerapan lengkap dari cetakan etiket sederhana kami, {% current_time %}, yang menampilkan tanggal/waktu saat ini, dibentuk menurut sebuah parameter yang diberikan dalam etiket, di sintaksis strftime(). Adalah ide bagus untuk memutuskan etiket sintaksis sebelum yang lainnya. Dalam kasus kami, mari kita katakan etiket harus digunakan seperti ini:
Django template
<p>The time is {%current_time"%Y-%m-%d %I:%M %p"%}.</p>
Pengurai untuk fungsi ini harus mengambil parameter dan membuat obyek Node:
Code
fromdjangoimport template
defdo_current_time(parser, token):try:# split_contents() knows not to split quoted strings.
tag_name, format_string = token.split_contents()exceptValueError:raise template.TemplateSyntaxError("%r tag requires a single argument"% token.contents.split()[0])ifnot(format_string[0]== format_string[-1]and format_string[0]in('"',"'")):raise template.TemplateSyntaxError("%r tag's argument should be in quotes"% tag_name
)return CurrentTimeNode(format_string[1:-1])
Catatan:
parser` adalah obyek pengurai cetakan. Kami tidak membutuhkannya dalam contoh ini.
token.contents adalah deretan karakter dari isi mentah dari etiket. Dalam contoh kami, dia adalah 'current_time "%Y-%m-%d %I:%M %p"'.
Cara token.split_contents() memisahkan argumen pada ruang selama menjaga deretan karakter dikutip bersama. Lebih mudah token.contents.split() tidak akan menjadi kuat, karena akan naif pisah pada semua ruang, termasuk itu dalam deretan karakter dikutip. Adalah ide bagus untuk selalu menggunakan token.split_contents().
Fungsi ini bertanggungjawab untuk memunculkan django.template.TemplateSyntaxError, dengan pesan membantu, untuk sintakses kesalahan apapun.
Pengecualian TemplateSyntaxError menggunakan variabel tag_name. Jangan mengkode keraskan nama etiket dalam pesan-pesan kesalahan anda, karena itu memasangkan nama etiket ke fungsi anda. token.contents.split()[0] akan ''selalu'' menjadi nama dari etiket anda -- bahkan ketika etiket tidak mempunyai argumen.
Fungsi mengembalikan sebuah CurrentTimeNode dengan semuanya yang node butuhkan untuk mengetahui tentang etiket ini. Dalam kasus ini, dia hanya melewatkan argumen -- "%Y-%m-%d %I:%M %p". Awalan dan akhiran kutipan dari cetakan etiket dipindahkan dalam format_string[1:-1].
Penguraian adalah tingkat-rendah. Pengembang Django mempunyai percobaan dengan menulis kerangka kecil diatas dari sistem penyurai ini, menggunakan teknik seperti tata bahasa EBNF, tetapi percobaan tersebut membuat cetakan mesin terlalu lambat. Dia adalah tingkat-rendah karena itu tercepat.
__init__() mendapatkan format_string dari do_current_time(). Selalu melewati options/parameters/arguments apapun ke Node melalui __init__() nya.
Cara render() adalah dimana pekerjaan sebenarnya terjadi.
render() harus pada umumnya gagal secara diam, khususnya dalam lingkungan produksi. Dalam beberapa kasus bagaimanapun, khususnya jika context.template.engine.debug adalah True, cara ini mungkin memunculkan sebuah pengecualian untuk membuat mencari kesalahan lebih mudah. Sebagai contoh, beberapa etiket inti memunculkan django.template.TemplateSyntaxError jika mereka menerima nomor atau jenis salah dari argumen
Akhirnya, pemisahan ini dari hasil penyusunan dan pembangunan dalam sebuah sistem cetakan efesien, karena sebuah cetakan dapat membangun banyak konteks tanpa harus diuraikan banyak kali.
Keluaran dari cetakan etiket tidak* otomatis berjalan melalui penyaring pelolosan otomatis (dengan pencegualian dari simple_tag() seperti yang digambarkan diatas). Bagaimanapun, masih terdapat sepasang hal anda harus ingat ketika menulis sebuah cetakan etiket.
Jika fungsi render() dari cetakan anda menyimpan hasil dalam sebuah konteks variabel (daripada mengembalikan hasil dalam sebuah deretan karakter), dia harus berhati-hati untuk memanggil mark_safe() jika sesuai. Ketika variabel pada akhirnya dibangun, dia akan dipengaruhi oleh pengaturan pelolosan otomatis dalam efek pada saat itu, sehingga isi yang harus aman dari pelolosan lebih lanjut butuh ditandai saja.
Juga, jika cetakan etiket anda membuat konteks baru untuk melakukan beberapa sub pembangunan, setel atribut pelolosan otomatis ke nilai konteks saat ini. Cara __init__ untuk kelas Context mengambil parameter dipanggil autoescape dimana anda dapat menggunakan untuk tujuan ini. Sebagai contoh:
Code
fromdjango.templateimport Context
defrender(self, context):# ...
new_context = Context({'var': obj}, autoescape=context.autoescape)# ... Do something with new_context ...
Ini bukan situasi paling umum, tetapi ini berguna jika anda membangun cetakan anda sendiri. Sebagai contoh:
Code
defrender(self, context):
t = context.template.engine.get_template('small_fragment.html')return t.render(Context({'var': obj}, autoescape=context.autoescape))
Jika kami mempunyai terlantar untuk dilewatkan dalam nilai context.autoescape saat ini ke Context baru kami dalam contoh ini, hasil akan mempunyai selalu otomatis diloloskan, yang mungkin perilaku yang tidak diinginkan jika cetakan etiket digunakan dalam sebuah blok {% autoescape off %}.
Sekali node diuraikan, cara render dia mungkin dipanggil sejumlah kali. Sejak Django terkadang berjalan dalam lingkungan banyak-rangkaian, sebuah node tunggal mungkin secara berkelanjutan membangun dengan konteks berbeda dalam menjawab ke dua permintaan terpisah. Oleh sebab itu, sangat penting untuk memastikan cetakan etiket anda adalah aman.
Untuk memastikan cetakan etiket anda thread safe, anda harus jangan menyimpan informasi keadaan pada node itu sendiri. Sebagai contoh, Django menyediakan cetakan etiket cycle siap pakai yang berputar terhadap sebuah daftar dari deretan karakter yang diberikan setiap kali dia dibangun:
Tetapi, seandainya kami mempunyai dua cetakan membangun potongan cetakan dari atas pada waktu bersamaan:
Urutan 1 melakukan pengulangan pertamanya, CycleNode.render() mengembalikan 'row1'
Urutan 2 melakukan pengulangan pertamanya, CycleNode.render() mengembalikan 'row2'
Urutan 1 melakukan pengulangan keduanya, CycleNode.render() mengembalikan 'row1'
Urutan 2 melakukan pengulangan keduanya, CycleNode.render() mengembalikan 'row2'
CycleNode berulang-ulang, tetapi perulangannya secara umum. Sejauh Thread 1 dan Thread 2 dirisaukan, dia selalu mengembalikan nilai sama. Ini jelas bukan apa kami inginkan!
Untuk mengalamatkan masalah ini, Django menyediakan sebuah render_context yang terhubung dengan context dari cetakan yang saat ini sedang dibangun. render_context berperilaku seperti sebuah kamus Python, dan harus digunakan untuk menyimpan keadaan Node diantara permohonan dari cara render.
Mari kita refactor penerapan CycleNode kami untuk menggunakan render_context:
Catat bahwa ini sangat aman untuk menyimpan informasi umum yang tidak akan merubah keseluruhan hidup dari Node sebagai sebuah atribut. Dalam kasus CycleNode, argumen cyclevars tidak berubah setelah Node dipakai, jadi kami tidak butuh menaruhnya ke dalam render_context. Tetapi keadaan informasi yang khusus pada cetakan yang saat ini sedang dibangun, seperti putaran saat ini dari CycleNode, harus disimpan dalam render_context.
Meskipun anda dapat melewatkan angka apapun dari argumen ke etiket cetakan menggunakan token.split_contents(), argumen semua tidak dipaketkan sebagai harfiah string. Sedikit kerja lebih diwajibkan untuk melewatkan isi dinamis (variabel cetakan) ke etiket cetakan sebagai sebuah argumen.
Selagi contoh sebelumnya telah membentuk waktu saat ini ke dalam string dan mengembalikan string, misalkan anda ingin melewatkan di DateTimeField dari sebuah obyek dan mempunyai bentuk etiket cetakan yang tanggal-waktu:
Django template
<p>This post was last updated at {%format_timeblog_entry.date_updated"%Y-%m-%d %I:%M %p"%}.</p>
Mulanya, token.split_contents() akan mengembalikan tiga nilai:
Pembentukan deretan karakter '"%Y-%m-%d %I:%M %p"'. Mengembalikan nilai dari split_contents() akan menyertakan kutipan awal dan akhir untuk deretan karakter persis seperti ini.
Sekarang etiket anda harus kelihatan seperti ini:
Code
fromdjangoimport template
defdo_format_time(parser, token):try:# split_contents() knows not to split quoted strings.
tag_name, date_to_be_formatted, format_string = token.split_contents()exceptValueError:raise template.TemplateSyntaxError("%r tag requires exactly two arguments"% token.contents.split()[0])ifnot(format_string[0]== format_string[-1]and format_string[0]in('"',"'")):raise template.TemplateSyntaxError("%r tag's argument should be in quotes"% tag_name
)return FormatTimeNode(date_to_be_formatted, format_string[1:-1])
Anda juga mempunyai kesemparan pembangun untuk mengambil isi sebenarnya dari sifat date_updated dari obyek blog_entry. Ini dapat diselesaikan dengan menggunakan kelas Variable() dalam django.template.
Untuk menggunakan kelas Variable, berikan contoh dia dengan nama dari variabel untuk di selesaikan dan kemudian panggil variable.resolve(context). Jadi, sebagai contoh:
Keputusan variabel akan melempar sebuah pengecualian VariableDoesNotExist jika dia tidak dapat menyelesaikan deretan karakter dilewatkan ke dia dalam konteks saat ini dari halaman.
Contoh diatas mengeluarkan sebuah nilai. Umumnya, dia lebih fleksibel jika cetakan etiket anda mengatur cetakan variabel daripada mengeluarkan nilai. Cara tersebut, penulis cetakan dapat menggunakan nilai yang cetakan etiket anda buat.
Untuk mengatur sebuah variabel dalam konteks, cukup gunakan penugasan kamus pada obyek konteks dalam cara render(). Ini adalah versi terperbaharui dari CurrentTimeNode yang mengatur sebuah variabel cetakan current_time daripada mengeluarkannya:
Catat bahwa render() mengembalikan deretan karakter kosong. render() harus selalu mengembalikan keluaran deretan karakter. Jika semua cetakan etiket disetel variabel, ``render() harus mengembalikan deretan karakter kosong.
Ini adalah bagaimana anda akan menggunakan versi baru dari etiket:
Django template
{%current_time"%Y-%M-%d %I:%M %p"%}<p>The time is {{current_time}}.</p>
Tetapi, ada masalah dengan CurrentTimeNode2: Nama variabel current_time adalah dikode keraskan. Ini berarti anda akan butuh untuk memastikan cetakan anda tidak menggunakan {{ current_time }} dimanapun juga, karena {% current_time %} akan membabi buta meniban nilai variabel itu. Sebuah solusi pembersih adalah membuat cetakan etiket menentukan nama dari variabel keluaran, seperti begitu:
Django template
{%current_time"%Y-%M-%d %I:%M %p"asmy_current_time%}<p>The current time is {{my_current_time}}.</p>
Untuk melakukannya, anda akan butuh me refaktor kedua fungsi penyusun dan kelas Node, seperti begitu:
Code
importreclassCurrentTimeNode3(template.Node):def__init__(self, format_string, var_name):self.format_string = format_string
self.var_name = var_name
defrender(self, context):
context[self.var_name]= datetime.datetime.now().strftime(self.format_string)return''defdo_current_time(parser, token):# This version uses a regular expression to parse tag contents.try:# Splitting by None == splitting by spaces.
tag_name, arg = token.contents.split(None,1)exceptValueError:raise template.TemplateSyntaxError("%r tag requires arguments"% token.contents.split()[0])
m = re.search(r'(.*?) as (\w+)', arg)ifnot m:raise template.TemplateSyntaxError("%r tag had invalid arguments"% tag_name)
format_string, var_name = m.groups()ifnot(format_string[0]== format_string[-1]and format_string[0]in('"',"'")):raise template.TemplateSyntaxError("%r tag's argument should be in quotes"% tag_name
)return CurrentTimeNode3(format_string[1:-1], var_name)
Perbedaan disini adalah bahwa do_current_time() mengambil bentuk deretan karakter dan nama variabel, melewatkan keduanya ke CurrentTimeNode3.
Akhirnya, jika anda hanya butuh mempunyai sintaksis sederhana untuk penyesuaian pembaharuan-konteks cetakan etiket, pertimbangkan menggunakan jalan pintas simple_tag(), yang mendukung menetapkan hasil etiket ke cetakan variabel.
Cetakan etiket dapat bekerja dalam berduaan. Sebagai contoh, standar etiket {% comment %} menyembunyikan semua sampai {% endcomment %}. Untuk membuat sebuah cetakan etiket seperti ini, gunakan parser.parse() dalam fungsi penyusun anda
Ini adalah bagaimana menyederhanakan etiket {% comment %} mungkin diterapkan:
parser.parse() mengambil sebuah tuple dari nama dari blok etiket "untuk diurai sampai". Dia mengembalikan sebuah instance dari django.template.NodeList, yaitu sebuah daftar dari semua obyek Node yang pengurai jumpai "sebelum" dia menjumpai etiket dinamai apapun dalam tuple.
Dalam "nodelist = parser.parse(('endcomment',))" di contoh atas, nodelist adalah sebuah daftar dari semua node diantara {% comment %} dan {% endcomment %}, bukan menghitung {% comment %} dan {% endcomment %} mereka sendiri.
Setelah parser.parse() dipanggil, pengurai belum "mengkonsumsi" etiket {% endcomment %}, jadi kode butuh secara eksplisit memanggil parser.delete_first_token().
CommentNode.render() mengembalikan sebuah deretan karakter kosong. Apapun diantara {% comment %} dan {% endcomment %} diabaikan.
Di contoh sebelumnya, do_comment() menyingkirkan apapun diantara {% comment %} and {% endcomment %}. Daripada melakukan itu, dimungkinkan untuk melakukan sesuatu dengan kode diantara blok etiket.
Sebagai contoh, ini adalah penyesuaian cetakan etiket, {% upper %}, yang menghuruf besarkan semuanya diantara dia sendiri dan {% endupper %}.
Penggunaan:
Django template
{%upper%}This will appear in uppercase, {{your_name}}.{%endupper%}
Seperti di contoh sebelumnya, kami akan menggunakan parser.parse(). Tetapi kali ini, kami melewatkan hasil nodelist ke Node:
Satu-satunya konsep disini adalah self.nodelist.render(context) dalam UpperNode.render().
Untuk lebih contoh dari pembangunan rumit, lihat kode sumber dari {% for %} dalam django/template/defaulttags.py dan {% if %} di django/template/smartif.py.