You are hereBlogs > jittat's blog > ทำ simple tag ใช้เอง
ทำ simple tag ใช้เอง
ผมบังเอิญเกิดความจำเป็นต้องทำ tag ใช้เอง เพราะว่าการเขียนสคริปต์ใน template ของ django ค่อนข้างจำกัดมาโดยการออกแบบ
เรื่องมีอยู่ว่า ผมต้องการแสดงข้อมูลของผู้ใช้ที่ log in อยู่ รวมทั้งแสดงรายการของการบ้านทุกข้อที่มี อย่างไรก็ตาม ผมต้องการแสดงที่ปลายการบ้านแต่ละข้อด้วย ว่าผู้ใช้คนนี้ส่งการบ้านข้อนี้มาหรือยัง
ในโมเดลของผู้ใช้ ผมเขียนเมท็อดด้านล่างเอาไว้ครับ
#
# check if self has submitted task
ด้วยเมท็อดดังกล่าว ผมอยากจะเขียนใน template ว่า
{% if student.has_submitted(task) %}
submitted
{% else %}
not submitted
{% endif %}
{% endfor %}
แต่นั่นทำไม่ได้ เพราะว่าเราไม่สามารถเขียน student.has_submitted(task) ได้ หรือถ้าเขียน student.has_submitted.task ก็ไม่ได้ความหมายเหมือนกับที่เราต้องการ เพราะว่าในการอ้างด้วย '.' เราทำได้เพียงทำ dictionary lookup, อ้าง attribute, เรียกเมท็อด, และอ้างด้วยดัชนีในลิสต์
สาเหตุที่เป็นเช่นนั้นก็เพราะ template ของ django ออกแบบมาให้ไม่สามารถเขียนโปรแกรม Python ลงไปได้ เพื่อที่จะได้ช่วยเป็นกรอบให้เราต้องแยกการประมวลผลออกจากการแสดงผล
ไม่เป็นไรครับ เราสามารถเขียน tag ขึ้นมาเองได้ เอกสารอ้างอิงหลักของเรื่องนี้อยู่ที่ Custom template tags and filters
สำหรับ tag ที่เราจะเขียน เป็น tag แบบง่าย คือเป็น tag ที่รับอาร์กิวเมนท์แล้วนำไปประมวลผลเพื่อคืนค่าเป็นส่วนของ html ซึ่งใน django จะเรียกว่า simple tag
เราจะใส่ tag ของเราใน application นะครับ ขั้นแรกเราไปสร้างไดเร็กทอรี templatetags ในไดเร็กทอรีของ application (อยู่ในระดับเดียวกับ models.py หรือ views.py)
พอเรียบร้อยแล้ว เราไปสร้างไฟล์ __init__.py เปล่า ๆ เอาไว้ในไดเร็กทอรีดังกล่าว เพื่อบอก Python ว่าในไดเร็กทอรีนี้เป็น package
เราจะเขียนโปรแกรมของ tag ให้เป็นโมดูลใน package นี้ ในตัวอย่างเราสร้างเป็นชื่อ exercise_format.py (ได้โมดูลชื่อ exercise_format)
แล้วก็เขียนเลยครับ อยากได้ tag อะไรก็ประกาศเมท็อดนั้นขึ้นมาเลย
เมื่อเขียนเสร็จ ในโค้ดเราจะต้องระบุกับ django ด้วยว่าเรามี tag อะไรบ้าง โดยวิธีที่ผมใช้คือไปเขียน decorator @register.simple_tag หน้าฟังก์ชัน
ด้านล่างเป็นตัวอย่างที่ผมเขียนนะครับ
register = template.Library() # อย่าลืมบรรทัดนี้
@register.simple_tag
def format_submission_notice(task, student):
if student.has_submitted(task):
return 'SUBMITTED'
else:
return 'NOT SUBMITTED'
ทีนี้ เวลาจะใช้ใน template เราก็ต้องโหลด tag มาก่อน โดยสั่ง
ไว้ใน template แล้วเวลาใช้ก็เรียกได้เลยครับ
{{ task }} | {% format_submission_notice task student %}
{% endfor %}
นอกจากการเขียน simple tag แล้ว ในบรรดา custom tag ที่(น่าจะ)เขียนไม่ยาก ก็ยังมี tag อีกแบบที่ (น่าจะ) ใช้บ่อย คือ inclusion_tag ที่ใช้สำหรับเตรียมข้อมูลแล้วส่งต่อให้ template อื่น render มาตอบ ถ้ามีโอกาสได้เล่นจะมาเขียนใหม่ครับ
ถ้ามีใครมีวิธีการทำตามที่ผมต้องการข้างต้นโดยไม่ต้องเขียน tag เอง ก็รบกวนช่วยบอกด้วยนะครับ
- jittat's blog
- Login or register to post comments

โอ้ เพิ่งมาเจอ entry นี้วันนี้ ถ้าเจอเมื่อวานก่อนถามอาจารย์ คงเข้าใจ custom tag ได้เร็วขึ้น : )
ทางออกที่ดีกว่า Functional Filters
ดูแล้วเขียนสะดวกกว่าจริง ๆ แต่หน้าตาออกจะไม่ค่อยสวยเท่าไหร่อ่ะครับ