You are hereBlogs > jittat's blog > ทำ simple tag ใช้เอง

ทำ simple tag ใช้เอง


By jittat - Posted on 15 September 2008

ผมบังเอิญเกิดความจำเป็นต้องทำ tag ใช้เอง เพราะว่าการเขียนสคริปต์ใน template ของ django ค่อนข้างจำกัดมาโดยการออกแบบ

เรื่องมีอยู่ว่า ผมต้องการแสดงข้อมูลของผู้ใช้ที่ log in อยู่ รวมทั้งแสดงรายการของการบ้านทุกข้อที่มี อย่างไรก็ตาม ผมต้องการแสดงที่ปลายการบ้านแต่ละข้อด้วย ว่าผู้ใช้คนนี้ส่งการบ้านข้อนี้มาหรือยัง

ในโมเดลของผู้ใช้ ผมเขียนเมท็อดด้านล่างเอาไว้ครับ

    def has_submitted(self,task):
        #
        # check if self has submitted task

ด้วยเมท็อดดังกล่าว ผมอยากจะเขียนใน template ว่า

  {% for task in tasks %}
    {% 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 หน้าฟังก์ชัน

ด้านล่างเป็นตัวอย่างที่ผมเขียนนะครับ

from django import template

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 มาก่อน โดยสั่ง

{% load exercises_format %}

ไว้ใน template แล้วเวลาใช้ก็เรียกได้เลยครับ

{% for task in tasks %}
  {{ task }} | {% format_submission_notice task student %}
{% endfor %}

นอกจากการเขียน simple tag แล้ว ในบรรดา custom tag ที่(น่าจะ)เขียนไม่ยาก ก็ยังมี tag อีกแบบที่ (น่าจะ) ใช้บ่อย คือ inclusion_tag ที่ใช้สำหรับเตรียมข้อมูลแล้วส่งต่อให้ template อื่น render มาตอบ ถ้ามีโอกาสได้เล่นจะมาเขียนใหม่ครับ

ถ้ามีใครมีวิธีการทำตามที่ผมต้องการข้างต้นโดยไม่ต้องเขียน tag เอง ก็รบกวนช่วยบอกด้วยนะครับ

tonkla's picture

โอ้ เพิ่งมาเจอ entry นี้วันนี้ ถ้าเจอเมื่อวานก่อนถามอาจารย์ คงเข้าใจ custom tag ได้เร็วขึ้น : )

ทางออกที่ดีกว่า Functional Filters

ดูแล้วเขียนสะดวกกว่าจริง ๆ แต่หน้าตาออกจะไม่ค่อยสวยเท่าไหร่อ่ะครับ