1.ทดลองการใช้ Templates ใน shell ดังต่อไปนี้
1.1 ตัวอย่างที่ 1
>>> from django import templateจากตัวอย่างนี้จะเป็นการ import API template ของ Django มาใช้ โดยให้ t เป็น template ที่เราสร้างขึ้นแบบง่ายๆ จะสังเกตเห็นว่า name นั้นมี {{ }} ครอบอยู่ ซึ่งนั่นเป็น Object ที่เราต้อง Context ไปใส่ใน Template เพื่อใช้ในการแสดงผล ต่อมาจะเห็นว่า c เป็นตัวแปรที่กำหนดค่า name ให้เป็น Adrian เก็บไว้จากนั้นจึงนำ c ไป render ใส่ t หรือก็คือการนำ name ที่เก็บไว้ใน c ไปใส่ให้ name ใน t ที่รอค่ามาใส่อยู่นั่นเองจึึงทำให้ผลเมื่อ Print ออกมาเป็น My name is Adrian. อย่างที่บอกไว้ข้างต้นนั้นว่า Template สามารถนำมาใช้ใหม่ได้ หากตัวแปร c ของเรานั้นไม่ได้ Context เป็นชื่อ Adrian แต่เปลี่ยนเป็น Fred เมื่อเรานำมา render ใหม่จากค่าที่เป็น Adrian ก็จะเปลี่ยนแปลงเป็น Fred ตามค่าของตัวแปร name ใน Context ที่เรา render เข้าไป
>>> t = template.Template(’My name is {{ name }}.’)
>>> c = template.Context({’name’: ’Adrian’})
>>> print t.render(c)
My name is Adrian.
>>> c = template.Context({’name’: ’Fred’})
>>> print t.render(c)
My name is Fred.
1.2 ตัวอย่างที่ 2
>>> from django.template import Templateจากตัวอย่างนี้จะเป็นการใช้ tag ใน template ซึ่ง tag ที่นำมาใช้นั้นไม่มีอยู่จึงเกิด Error ขึ้นแบบนี้ ซึ่งการเกิด Error แบบนี้เป็นได้หลายกรณีเช่น เป็น tag ที่ไม่มีอยู่จริง, เป็น filters ที่ไม่มีอยู่จริง, ลืมใส่ tag ปิด (tag บางตัวต้องมี tag ปิดด้วย) เป็นต้น
>>> t = Template(’{% notatag %}’)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
...
django.template.TemplateSyntaxError: Invalid block tag: ’notatag’
1.3 ตัวอย่างที่ 3
>>> from django.template import Template, Contextจากตัวอย่างสังเกตว่าจะมี {{ ship_date|date:"F j, Y" }} ซึ่งใน tag นี้มี filter ที่ชื่อ date ด้วยซึ่งเอาไว้จัดรูปแบบในการแสดงผลของวันจากตัวอย่างจะเห็นว่าผลลัพธ์ที่ออกมาจะเป็น เดือน วัน, ปี และยังมี tag if ซึ่งใช้หลักการเดียวกับการเขียน if else ในโปรแกรมทั่วไปซึ่งตัวแปรที่ใช้เช็ค True False คือ ordered_warranty นั่นเองซึ่งหากเป็น True ก็จะใส่ <p>Your warranty information will be included in the packaging.</p> ลงในผลลัพธ์ที่ได้จากการ render แต่ถ้าเป็น False ก็จะใส่ <p>You didn’t order a warranty, so you’re on your own when the products inevitably stop working.</p> ลงในผลลัพธ์แทน
>>> raw_template = """<p>Dear {{ person_name }},</p>
...
... <p>Thanks for placing an order from {{ company }}. It’s scheduled to
... ship on {{ ship_date|date:"F j, Y" }}.</p>
...
... {% if ordered_warranty %}
... <p>Your warranty information will be included in the packaging.</p>
... {% else %}
... <p>You didn’t order a warranty, so you’re on your own when
... the products inevitably stop working.</p>
... {% endif %}
...
... <p>Sincerely,<br />{{ company }}</p>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({’person_name’: ’John Smith’,
... ’company’: ’Outdoor Equipment’,
... ’ship_date’: datetime.date(2009, 4, 2),
... ’ordered_warranty’: False})
>>> t.render(c)
u"<p>Dear John Smith,</p>\n\n<p>Thanks for placing an order from Outdoor
Equipment. It’s scheduled to\nship on April 2, 2009.</p>\n\n\n<p>You
didn’t order a warranty, so you’re on your own when\nthe products
inevitably stop working.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment
</p>"
1.4 ตัวอย่างที่ 4
>>> from django.template import Template, Contextจากตัวอย่างนี้แสดงให้เห็นว่าหาก Object ที่เรา Context ไปยัง Template เป็น Object ใหญ่ที่มีตัวแปรต่างๆอยู่ภายใน เราสามารถดึงค่าเหล่านั้นมาใช้ได้โดยการพิมชื่อของ Object ตามด้วย "." แล้วตามด้วยชื่อตัวแปรที่ต้องการดังตัวอย่างข้างบนนี้
>>> class Person(object):
... def __init__(self, first_name, last_name):
... self.first_name, self.last_name = first_name, last_name
>>> t = Template(’Hello, {{ person.first_name }} {{ person.last_name }}.’)
>>> c = Context({’person’: Person(’John’, ’Smith’)})
>>> t.render(c)
u’Hello, John Smith.’
1.5 ตัวอย่างที่ 5
>>> from django.template import Template, Contextจากตัวอย่างนี้นอกจากจะสามารถดึงตัวแปรที่อยู่ภายใน Object ได้แล้วจากตัวอย่างที่ 1.4 Templates ก็ยังสามารถเรียกใช้ Method ของ Object นั้นๆได้อีกด้วยเช่นกันเห็นได้จากตัวอย่างนี้
>>> t = Template(’{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}’)
>>> t.render(Context({’var’: ’hello’}))
u’hello -- HELLO -- False’
>>> t.render(Context({’var’: ’123’}))
u’123 -- 123 -- True’
1.6 ตัวอย่างที่ 6
>>> from django.template import Template, Contextจากตัวอย่างหากเรา Context ตัวแปรที่ไม่มีอยู่ใน Templates หรือ มีแต่ตัวพิมเล็กใหญ่ไม่ตรงกันนั้นก็จะทำให้ Templates มองว่าตัวแปรที่ Context ไปไม่ใช่ตัว Templates ต้องการซึ่งเดิมๆแล้วนั้นหากตัวแปรที่รอ Context มานั้นหากไม่มีค่าส่งมาให้ Django ก็จะใส่เป็น Empty String ให้เอง
>>> t = Template(’Your name is {{ name }}.’)
>>> t.render(Context())
u’Your name is .’
>>> t.render(Context({’var’: ’hello’}))
u’Your name is .’
>>> t.render(Context({’NAME’: ’hello’}))
u’Your name is .’
>>> t.render(Context({’Name’: ’hello’}))
u’Your name is .’
2. เรียนรู้เกี่ยวกับการใช้ Tag
2.1 Tag if else
{% if today_is_weekend %}แบบที่ 1 มี Tag if อย่างเดียวหากเข้าเงื่อนไขหรือเป็น True ก็จะนำสิ่งที่อยู่ใน if ใส่เข้าไปในผลลัพธ์จากการ render ด้วย
<p>Welcome to the weekend!</p>
{% endif %}
{% if today_is_weekend %}แบบที่ 2 มี Tag if else ซึ่งก็ใช้หลักการเดียวกับการเขียนโปรแกรมทั่วไปหากไม่เงื่อนไข if ก็จะทำใน else โดยเรายังสามารถใส่ if ซ้อนไว้ใน else ได้อีกด้วยแต่ไม่มีรูปแบบของการเขียน elif
<p>Welcome to the weekend!</p>
{% else %}
<p>Get back to work.</p>
{% endif %}
โดยทั้ง 2 แบบนั้นต้องใส่ tag endif เข้าไปด้วยเสมอหากไม่ใส่ก็จะเกิด TemplateSyntaxError.
สิ่งที่น่ารู้เกี่ยวกับ Tag if else ใน Templates ของ Django คือ หากตัวแปรที่เอามาเช็ค if else นั้นมีคุณสมบัติ ตามข้างล่างนี้จะได้ผลเป็น False เสมอ
- เป็น empty list
- เป็น empty tuple
- เป็น empty dictionary
- เป็น empty string
- มีค่าเป็น 0
- เป็น None Object
2.2 Tag for
{% for athlete in athlete_list %}การใช้ for loop ใน Templates ก็สามารถทำได้เช่นกันโดยสามารถใช้วนลูปเข้าถึงตัวแปรทั้งหมดภายใน list ได้สามารถใช้ for ซ้อนไว้ข้างใน for ก็ได้เช่นกัน หรือจะใส่ if else ใน for หรือเอา for ไว้ใน if else ก็สามารถทำได้เช่นกัน
<h1>{{ athlete.name }}</h1>
<ul>
{% for sport in athlete.sports_played %}
<li>{{ sport }}</li>
{% endfor %}
</ul>
{% endfor %}
{% for athlete in athlete_list %}อีกหนึ่งตัวอย่างที่น่าสนใจการมีการเข้าลูป for แต่ตัวที่จะเอามาวนลูปนั้นดันเป็นตัวแปรที่เป็น empty list เราก็สามารถใส่ tag empty ไว้หากเป็น empty list ก็จะใส่ข้อมูลที่อยู่หลัง tag empty ลงไปแทน
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% endfor %}
นอกจากนี้ for ยังมีสิ่งที่น่ารู้คือ for ใน Templates นั้นจะมีตัวแปรที่สร้างขึ้นตอนที่อยู่ใน for สามารถดึงมาใช้ได้ซึ่งก็คือตัวแปรที่ชื่อ forloop และเมื่อจบลูปตัวแปรนี้ก็จะถูกทำลายทิ้งไป ซึ่งวิธีใช้ก็ต้องใส่ Tag ด้วยดังนี้ {{ forloop }} ซึ่ง forloop นั้นมี attributes ดังนี้
- forloop.counter จะได้ค่าออกมาเป็นเลขที่บอกจำนวนรอบของ loop ที่กำลังวนอยู่ในขณะนั้น โดยจะเริ่มนับจำนวนรอบที่ 1
- forloop.counter0 คุณสมบัติเหมือน forloop.counter ทุกประการ แต่สิ่งที่แตกต่างจาก forloop.counter คือจะเริ่มนับจำนวนรอบที่ 0
- forloop.revcounter จะได้ค่าออกมาเป็นจำนวนของรอบคงเหลือที่ต้องวนลูป ในขณะนั้น โดยจะเริ่มนับจากจำนวนรอบทั้งหมดลดลงไปเรื่อยๆ จนถึง 1
- forloop.revcounter0 คุณสมบัติเหมือน forloop.revcounter ทุกประการแต่ตอนเริ่มนับจะเริ่มจากจำนวนรอบทั้งหมด - 1 และลดลงเรื่อยๆจนเหลือ 0
- forloop.first จะได้ผลลัพธ์ออกมาเป็นค่าความจริง True ก็ต่อเมื่อรอบที่วนลูปอยู่นั้นเป็นรอบแรก รอบอื่นๆจะได้ False ทั้งหมด
- forloop.last จะได้ผลลัพธ์ออกมาเป็นค่าความจริง True ก็ต่อเมื่อรอบที่วนลูปอยู่นั้นเป็นรอบสุดท้าย รอบอื่นๆจะได้ False ทั้งหมด
- forloop.parentloop จะดึงตัวแปรของ forloop ของลูปชั้นบนออกมาซึ่งมี attributes เหมือนกันทุกประการวิธีเรียก เช่น forloop.parentloop.last เป็นต้น
ตัวอย่างการใช้ forloop
{% for object in objects %}ตัวอย่างการใช้ forloop.parentloop
{% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
{{ object }}
</li>
{% endfor %}
{% for country in countries %}2.3 Tag ifequal ifnotequal (ใช้รูปแบบเดียวกันต่างกันที่เช็คว่าเท่ากันไหม หรือ ไม่เท่ากัน)
<table>
{% for city in country.city_list %}
<tr>
<td>Country #{{ forloop.parentloop.counter }}</td>
<td>City #{{ forloop.counter }}</td>
<td>{{ city }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
{% ifequal user currentuser %}แบบที่ 1 ใช้เปรียบเทียบระหว่างตัวแปรกับตัวแปร จากตัวอย่างนี้เป็นการเทียบว่า user = currentuser หรือไม่ ถ้าเท่ากันก็จะใส่ <h1>Welcome!</h1> เข้าไปในผลลัพธ์
<h1>Welcome!</h1>
{% endifequal %}
{% ifequal section ’sitenews’ %}แบบที่ 2 Tag ifequal และ ifnotequal ก็รองรับการใช้ Tag else ได้เช่นกับเดียวกับ Tag if แบบปกติ นอกจากนั้น ifequal และ ifnotequal ยังสามารถใช้เทียบระหว่างตัวแปร กับ ข้อมูลที่ต้องการเช็คตรงๆเลยก็ได้เช่นกันแต่มีข้อจำกัดอยู่ว่ารูปแบบนี้สามารถใช้ตัวแปรเทียบได้แค่ 4 รูปแบบนี้เท่านั้นคือ
<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}
- 1
- 1.23
- ’foo’
- "foo"
3. หลักจากศึกษาวิธีการใช้และรูปแบบการเขียน Templates กันไปแล้วมาเริ่มทดลองใช้ Templates กันเลยดีกว่า
3.1 สร้างโฟลเดอร์สำหรับการเก็บ Templates ขึ้นมาก่อนจะไว้ที่ไหนก็ได้แต่แนะนำให้ไว้ที่เดียวกับที่มีไฟล์ manage.py เพราะง่ายต่อการเข้าถึงและเห็นชัดเจนเมื่อสร้างแล้วจะได้รูปแบบประมาณนี้
3.2 เมื่อสร้างโฟลเดอร์แล้วเราต้องบอกให้ Django รู้ก่อนว่าโฟลเดอร์นี้นะเราจะใช้เก็บ Templates ทั้งหมดของเราไว้ ดังนั้นให้เข้าไปแก้ไขไฟล์ settings.py ในโฟลเดอร์ mysite โดยเพิ่มข้อความดังนี้
TEMPLATE_DIRS = (เพื่อเป็นการระบุที่อยู่ของ Templates ให้ Django รู้แต่การกำหนดรูปแบบนี้ไม่สมควรใช้เนื่องจากหากมีเปลี่ยนเครื่องก็จะทำให้ Directory ที่ลงไว้ใน settings ไม่ตรงกับเครื่องนั้นแล้วทำให้หาไฟล์ Templates ไม่เจอในที่สุดดังนั้นวิธีแก้เปลี่ยนไปใช้รูปแบบของที่อยู่ดังนี้
'/home/django/mysite/templates',
)
TEMPLATE_DIRS = (BASE_DIR เป็นตัวแปรที่มีอยู่แล้วใน settings ซึ่งจะดึงที่อยู่ ณ ปัจจุบันมาให้เราใช้ได้เลย
os.path.join(BASE_DIR, 'Templates').replace('\\','/'),
)
3.3 เข้าไปยังโฟลเดอร์ Templates ที่เราสร้างขึ้นในข้อ 3.1 แล้วสร้างไฟล์ชื่อ current_datetime.html แล้วใส่โค้ดในไฟล์ HTML ดังนี้
<html><body>It is now {{ current_date }}.</body></html>3.4 เข้าไปแก้ไขไฟล์ views.py ในโฟลเดอร์ mysite โดยเพิ่มโค้ดและแก้ไขดังนี้
from django.template.loader import get_template
from django.template import Context
เมื่อแก้ไขเสร็จแล้วลองเข้าไปที่ http://localhost:8000/time/ ดูจะพบว่าหน้าเว็บของเราจะแสดงเวลา ณ ปัจจุบันออกมาเหมือนกับฟังก์ชั่นที่เราเขียนไว้ก่อนหน้าใน Chapter 3
def current_datetime(request):
now = datetime.datetime.now()
t = get_template('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
แต่ฟังก์ชั่นที่เราแก้ไขเมื่อสักครู่เราสามารถทำให้สั้นลงได้อีกด้วยการใช้ render_to_response ดังนี้
from django.shortcuts import render_to_responseก็จะให้ผลเหมือนกันแต่โค้ดสั้นลงเนื่องจาก Django มี API ที่ชื่อ render_to_response อยู่แล้วซึ่งเรา import มาจาก django.shortcuts แล้วนำมาใช้ได้เลย แต่ก็ยังมีวิธีที่สั้นลงไปอีกโดยการใช้ locals() แทนการเขียนตัวแปรที่ใช้ Context ทั้งหมดดังนี้
def current_datetime(request):
now = datetime.datetime.now()
return render_to_response('current_datetime.html', {'current_date': now})
def current_datetime(request):โดยหลักการของ locals() คือการนำ local variable ทั้งหมดที่มีในฟังก์ชั่นนี้ส่งไปให้ Templates ทั้งหมด ดังนั้นการตั้งชื่อตัวแปรจึงสำคัญเพราะตัวแปรที่เราจะแทนค่าใน Templates หากไม่มีชื่อตรงกับใน local variable ก็จะไม่มีตัวแปรไป render และที่สำคัญอีกอย่างคือการส่งตัวแปร local variable ไปทั้งหมดนั่นหมายถึงการส่ง request ไปให้ Templates ด้วยเช่นกันดังนั้นหากจะใช้วิธีลัดแบบ locals() นั้นต้องระมัดระวังเป็นพิเศษและการใช้ locals() นี้มีข้อดีเพียงแค่เขียนสั้นลงเท่านั้น
current_date = datetime.datetime.now()
return render_to_response('current_datetime.html', locals())
3.5 สร้างโฟลเดอร์ includes โดยเข้าไปเก็บไว้ในโฟลเดอร์ Templates ที่สร้างในข้อ 3.1 ให้เป็น Sub Directory และสร้าง Templates ชื่อ nav.html มีโค้ดภายในดังนี้
<div id="nav">จากนั้นเข้าไปแก้ไข Templates current_datetime.html โดยเพิ่ม Tag includes เข้าไปจะได้ดังนี้
You are in: {{ current_section }}
</div>
<html>เมื่อใส่ Tag includes เข้าไปสังเกตจะเห็นว่า ที่อยู่เป็น includes/nav.html ซึ่งหากใน views ของเรานั้นอยากใช้ Temmplates ที่อยู่ในโฟลเดอร์ includes ก็สามารถพิมที่อยู่ของไฟล์แบบเดียวกับในตัวอย่างนี้ได้เลย และเนื่องจากเราดึง Templates อื่นเข้ามาใช้ใน Templates current_datetime.html ด้วยดังนั้นตัวแปรที่ nav.html ต้องการให้ Context ไปเราก็ต้อง Context ไปให้ด้วยเช่นกันดังนั้นเข้าไปแก้ไขไฟล์ Views โดยเพิ่มโค้ดเข้าไปดังนี้
<body>
{% include "includes/nav.html" %}
It is now {{ current_date }}.
</body>
</html>
def current_datetime(request):โดยเบื้องต้นกำหนด current_section ให้เป็น /time/ เพื่อนำไปแสดงผลหากเราไม่ Context current_section ไปหน้าเว็บส่วนที่ต้อง Context ก็จะกลายเป็นช่องว่างๆไป
current_date = datetime.datetime.now()
current_section = "/time/"
return render_to_response('current_datetime.html', locals())
ตัวอย่างเมื่อ Context current_section ไป
ตัวอย่างเมื่อไม่ Context current_section ไป
ไม่มีความคิดเห็น:
แสดงความคิดเห็น