วันอังคารที่ 10 ธันวาคม พ.ศ. 2556

Djangobook - Dynamic Urls and Views

มาเริ่มต้นกันโดยเปิดไฟล์ urls.py ในโปรเจคของเราจากนั้นแก้ไขโค้ดภายในดังนี้

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^time/$', 'Stamp.views.display_time'),
    url(r'^time/plus_m/(\d+)/$', 'Stamp.views.minutes_ahead'),
)

จากนั้นเข้าไปเพิ่มโค้ด ในไฟล์ views.py ดังนี้

from django.http import HttpResponse,Http404
import time,datetime

def display_time(request):
    now = time.localtime()
    return HttpResponse("""
<html>
    <head>
        <title>Test</title>
    </head>
    <body bgcolor="skyblue"><font color="brown" size="10">
    <center>Year : """ + str(now[0]) + """<br><br>
    Month : """ + str(now[1]) + """<br><br>
    Day : """ + str(now[2]) + """<br><br>
    Time : """ + str(now[3]) + ":" + str(now[4]) + ":" + str(now[5]) + """
    </center>
    </body>
    </html>""")

def minutes_ahead(request, offset):
    try:
        offset = int(offset)
    except ValueError:
        raise Http404()
    dt = datetime.datetime.now() + datetime.timedelta(minutes=offset)
    html = """<html><body bgcolor="skyblue"><center><font color="brown" size="10">In %s minute(s), it will be %s.</font><center></body></html>""" % (offset, dt)
    return HttpResponse(html)

จะสังเกตได้ว่าในโค้ดนีี้มีรูปแบบของ Urls อยู่สองแบบคือ

url(r'^time/$', 'Stamp.views.display_time'),
url(r'^time/plus_m/(\d+)/$', 'Stamp.views.minutes_ahead'),

    ซึ่งจะเห็นว่าแบบแรกนั้นหากเราพิม 127.0.0.1/time ก็จะแสดงรูปแบบของเพจออกมาได้เพียงแบบเดียวซึ่งเพจแบบนี้เรียกว่า Static เพจซึ่งการทำ Web Application ส่วนใหญ่จะใช้เพจแบบ Dynamic ซึ่งจะเป็นแบบที่สอง 127.0.0.1/time/plus_m/1 ก็จะเป็นการโชว์เวลาที่เพิ่มไปอีก 1 นาทีตามที่ได้เขียนไว้ใน Function minutes_ahead ใน Views โดยหากเพิ่มเป็น 127.0.0.1/time/plus_m/2 ก็จะเพิ่มเวลาไปอีก 2 นาทีเช่นที่เขียนใน Function


 
    โดย url(r'^time/plus_m/(\d+)/$', 'Stamp.views.minutes_ahead'), นั้นจะเห็นว่ามี (\d+) ซึ่งในส่วนนี้จะเป็นส่วนที่ทำให้โค้ดของเรา Dynamic เพราะในส่วนนี้นั้นจะเป็นการรับค่าจาก Url ที่พิมเข้าไปให้ Function minutes_ahead โดยค่าที่ส่งไปจะเป็นค่าของตัวแปร offset และนำไปใช้ใน Function ในส่วนของ Function นั้น จะมีโค้ด
    try:
        offset = int(offset)
    except ValueError:
        raise Http404()
ซึ่งทำหน้าที่ในการเช็คว่าค่าที่รับมาจาก Url นั้นเป็นตัวแปรประเภท int หรือไม่หาก ไม่ใช่ int ก็จะเกิด exception แล้วเพจจะเด้งไปที่หน้า Page not found 404 แทนเพราะหากไม่ใช่ตัวแปรที่เป็น int เมื่อนำไปคำนวณในบรรทัด
dt = datetime.datetime.now() + datetime.timedelta(minutes=offset)
ไม่ได้ซึ่ง exception นั้นมีไว้เพื่อป้องกันเหตุการณ์นี้โดยเฉพาะ


    โดย API ที่ใช้ใน Views มี 3 API คือ time , datetime และ HttpResponse , Http404 ซึ่ง time ใช้ใน Function display_time โดยเป็นการดึงเวลาในปัจจุบัน ณ ขณะเรียกเพจขึ้นมาแสดงผล

    datetime ใช้ใน Function minutes_ahead โดยเรียกเวลา ปัจจุบันขึ้นมาแล้วเพิ่มด้วยเวลาที่เพิ่มจากเวลาในปัจจุบันไปอีกตามค่าของ offset เป็นนาที

    HttpResponse ใช้ในการส่ง String กลับไปแสดงผลใน HTML ซึ่งจะเป็นรูปแบบตามในแต่ละ Function และ Http404 จะเรียก Page not found 404 มาแสดงผลแทนการส่ง String กลับไปแสดงผลใน HTML

    ดังนั้นในไฟล์ views นั้นเราสามารถใช้ในการกำหนดรูปแบบที่จะเอาไว้ใช้ในการแสดงผลโดยการเขียน Function ไว้ในนั้นและใช้ urls ในการเรียกใช้ Function ใน views มาแสดงผลออกทาง HTML
Read more ...

วันอาทิตย์ที่ 8 ธันวาคม พ.ศ. 2556

Django Tutorial Part 4 - Form & Generic Views

    เข้าสู่ Part 4 ของ Tutorial กันแล้วโดยในส่วนนี้จะนำ Form ของภาษา HTML เข้ามาใช้ใน Templates ที่เราได้สร้างไว้ใน Part 3 ก่อนหน้านี้พร้อมทั้งสามารถโหวต Choice ต่างๆของเราผ่านทาง Browser และบันทึกค่าลงใน Database ของเราได้อีกด้วยแถมท้ายด้วยการใช้ Generic View เพื่อให้โค้ดที่เขียนนั้นสั้นลงจากเดิม

---Form---

1.เริ่มต้นด้วยการแก้ไข template detail.html ที่สร้างไว้ใน Part 3 กันเสียก่อนโดยแก้ตามนี้

<h1>{{ poll.question }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' poll.id %}" method="post">
{% csrf_token %}
{% for choice in poll.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>

    จาก Template ที่แก้ไขนั้นจะแสดงผลเป็น HTML โดยหากมี error_message ก็จะแสดงออกมาทาง Browser ด้วยและเป็นตัวหนาจากนั้นในส่วนของ form tag action มีหน้าที่เมื่อกดปุ่ม Vote จากโค้ด <input type="submit" value="Vote" /> แล้วจะเรียกหน้า vote ขึ้นมา ในส่วนของ method="post" ใส่เพื่อเป็นการบอกว่าข้อมูลจาก form นี้นำเข้าไปให้ทางฝั่ง server และ {% csrf_token %} มีไว้เพื่อป้องกัน Cross Site Request Forgeries ซึ่ง Cross Site Request Forgeries คือ รูปแบบของอันตรายของเว็บโดยส่งคำสั่งที่ไม่ได้รับอนุญาตจากผู้ใช้ที่เว็บไซต์

    จากนั้นเข้า loop for poll.choice_set.all ซึ่งก็คือ List ที่มี Choice ทั้งหมดของ poll นั้นๆผ่านตัวแปร choice จากนั้นสร้างปุ่มกดแบบ Radio และมี label เพื่อบอกว่าปุ่ม Radio นั้นเป็นปุ่มของ choice ไหน



2.จากนั้นเข้าไปแก้ไข Function vote ในไฟล์ views.py ใน polls/views.py ดังนี้

          def results(request, poll_id):
              poll = get_object_or_404(Poll, pk=poll_id)
              return render(request, 'polls/results.html', {'poll': poll})
          def vote(request, poll_id):
              p = get_object_or_404(Poll, pk=poll_id)
              try:
                  selected_choice = p.choice_set.get(pk=request.POST['choice'])
              except (KeyError, Choice.DoesNotExist):
                  # Redisplay the poll voting form.
                  return render(request, 'polls/detail.html', {
                      'poll': p,
                      'error_message': "You didn't select a choice.",
                  })
              else:
                  selected_choice.votes += 1
                  selected_choice.save()
                  # Always return an HttpResponseRedirect after successfully dealing
                  # with POST data. This prevents data from being posted twice if a
                  # user hits the Back button.
                  return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))


    ซึ่ง Function vote เมื่อเรียกใช้แล้วจะดึง Objects ของ poll ที่ pk ตรงกับ poll_id ออกมาเก็บใน p หากไม่มี Objects นั้นก็จะแสดงหน้า Page not found 404 แบบที่ได้อธิบายไว้ใน Part 3 แล้วจากนั้น p.choice_set.get(pk=request.POST['choice']) จะได้ choice ที่เราทำการเลือกในหน้า detail โดย request.POST['choice'] จะได้ค่าของ String ซึ่งเป็น ID ของ choice ออกมาโดยเมื่อได้ choice ที่เลือกออกมาแล้วก็ให้ selected_choice ชี้ไว้ แต่หากไม่สามารถหา choice นั้นได้ก็จะกลับไปแสดงหน้า detail อีกครั้งผ่าน render โดยมี error_message กลับไปแสดงด้วย แต่ถ้าหากหาได้แล้วก็ให้บวกค่า votes ของ choice นั้น 1 แต้มแล้วทำการ save ลง Database จากนั้นก็จะ Redirect ไปยังหน้า results ของ poll นั้นเองโดยอัตโนมัติ

    ส่วน Function results นั้นจะดึง Objects ของ poll ที่ pk ตรงกับ poll_id ออกมาเก็บใน p หากไม่มี Objects นั้นก็จะแสดงหน้า Page not found 404 แบบที่ได้อธิบายไว้ใน Part 3 แล้วจากนั้นจะ render template results.html ขึ้นมาแสดงผล



3.เพิ่ม template results.html โดยภายใน html มีโค้ดดังนี้

          <h1>{{ poll.question }}</h1>
          <ul>
          {% for choice in poll.choice_set.all %}
              <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
          {% endfor %}
          </ul>
          <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>


    โดยรูปแบบที่แสดงจะเริ่มด้วยคำถามของ poll นั้นๆเป็น Header จากนั้นจึงเข้าไปอ่านค่าใน choice ทั้งหมดของ poll นี้โดยนำทั้ง choice และจำนวนของการ vote ออกมาแสดงผลทั้งหมด จากนั้นมีลิ้งค์ขึ้นมาถามว่า Vote again? หาคลิกที่ลิ้งค์ก็จะกลับไปหน้า detail ของ poll นี้เพื่อเลือกโหวตอีกครั้ง



---Generic Views---


4.การที่โค้ดของเรานั้นอยู่ในรูปแบบ generic และ สั้นๆ ถือเป็น โค้ดที่ดีกว่าดังนั้นเราจะแก้ไขไฟล์ดังต่อไปนี้

    4.1 แก้ไขไฟล์ polls/urls.py ในส่วนของ urlpatterns ดังนี้

          url(r'^$', views.IndexView.as_view(), name='index'),
          url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
          url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'),
          url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),


    4.2 แก้ไขและเพิ่มบรรทัด import ในไฟล์ polls/views.py ดังนี้

          from django.views import generic

          class IndexView(generic.ListView):
              template_name = 'polls/index.html'
              context_object_name = 'latest_poll_list'

              def get_queryset(self):
                  """Return the last five published polls."""
                  return Poll.objects.order_by('-pub_date')[:5]

          class DetailView(generic.DetailView):
              model = Poll
              template_name = 'polls/detail.html'

          class ResultsView(generic.DetailView):
              model = Poll
              template_name = 'polls/results.html'


    จากโค้ดที่ได้เปลี่ยนแปลงไปข้างต้นนั้นในส่วนของ views.py เราได้ import API ที่ชื่อ generic จาก django.views มาซึ่งเป็น API สำหรับการทำ generic views มาให้โดย template_name ในแต่ละ Class นั้นเอาไว้กำหนดไฟล์ templates ที่จะเอามาใช้ในการ render ส่วน context_object_name เอาไว้กำหนดชื่อของตัวแปรที่ใช้ใน context นี้สำหรับ model เอาไว้แสดงข้อมูลใน Objects ที่ระบุไว้

    ในส่วนของ urls.py จะเห็นว่ามี .as_view() ต่อท้ายชื่อ Class ที่เราสร้างไว้ใน views.py ซึ่ง .as_view() มีหน้าที่ในการจัดการกับ request ที่เข้ามาแล้วตอบกลับไปซึ่งสิ่งที่ตอบกลับไปก็คือหน้า HTML ที่ render แล้วอีกส่วนที่เปลี่ยนไปก็คือ (?P<poll_id>\d+) ของ detail และ results เปลี่ยนไปเป็น (?P<pk>\d+) เพราะ DetailView นั้นสามารถจับ Primary Key จาก url ได้หรือที่เรียกกันว่า pk นั่นเองจึงเป็นเหตุผลให้เปลี่ยนไปเป็นแบบหลัง

    จาก Tutorial Part 4 นี้ทำให้เรารู้จักการใช้ Form ใน HTML ผสมกับการใช้ Template ใน Django ไปด้วยซึ่งสามารถใช้ Form เป็น Input ในการเก็บข้อมูลเข้าไปยัง Database ของเราหรือจะเอามาเพิ่มแต้ม Vote แบบในตัวอย่างก็ได้ นอกจากนี้ยังมีส่วนของ Generic View ใช้เพื่อลดความยาวของโค้ดให้อ่านง่ายสบายตาขึ้นจากแบบเก่า

ข้อมูลเพิ่มเติมเรื่อง Cross Site Request Forgeries จากเว็บ wikipedia.org
ข้อมูลเพิ่มเติมเรื่อง Generic View แบบ Detail View จากเว็บ djangoproject.com
ข้อมูลเพิ่มเติมเรื่อง Generic View แบบ List View จากเว็บ djangoproject.com
Read more ...

Django Tutorial Part 3 - Views, Urls and Templates

    ในส่วนของ Part 3 นี้เราจะศึกษาในส่วนของ การใช้งาน views.py, urls.py และ Templates ใน App polls ที่ได้สร้างใน Part 2

---Views, Urls---

1.เข้าไปในแก้ไขไฟล์ polls/views.py ดังนี้

from django.http import HttpResponse
def index(request):
    return HttpResponse("Hello, world. You're at the poll index.")

2.สร้างไฟล์ที่ชื่อว่า urls.py ไว้ในโฟลเดอร์ polls จะได้เป็น polls/urls.py และเขียนข้อความในไฟล์ดังนี้

from django.conf.urls import patterns, url
from polls import views
urlpatterns = patterns('',
    url(r'^$', views.index, name='index')
)

3.เข้าไปแก้ไฟล์ในส่วนของ Tutorial/urls.py โดยการเพิ่ม

url(r'^polls/', include('polls.urls')),

เข้าไปในส่วนของ urlpatterns

    เมื่อมีการเรียก url เป็น http://127.0.0.1:8000/polls/ ซึ่งการทำงานเริ่มต้นจะเช็ค urls ที่ไฟล์ Tutorial/urls.py ก่อนเนื่องจาก ใน Tutorial/setting.py ได้มีการตั้งค่าไว้ว่า ROOT_URLCONF = 'Tutorial.urls' นั่นเอง

    ซึ่งจากข้อ 3 ที่ได้เพิ่มโค้ดเข้าไปนั้นเราได้เขียนไว้หาก urls ลิ้งค์ไปที่ polls ให้ไปเรียก polls.urls หรือก็คือ ไฟล์ polls/urls.py ในข้อ 2 นั่นเองซึ่งจะเรียกต่อไปยังไฟล์ polls/views.py ซึ่งฟังก์ชั่น index ใน polls/views.py นี้จะทำการ return ค่ากลับไปแสดงผลทาง Browser ว่า "Hello, world. You're at the poll index."


4.เพิ่มโค้ดข้างล่างนี้เข้าไปใน polls/views.py

def detail(request, poll_id):
    return HttpResponse("You're looking at poll %s." % poll_id)
def results(request, poll_id):
    return HttpResponse("You're looking at the results of poll %s." % poll_id)
def vote(request, poll_id):
    return HttpResponse("You're voting on poll %s." % poll_id)

5.และในส่วนของ polls/urls.py ก็ให้เพิ่มโค้ดข้างล่างนี้เข้าไปใน urlpatterns

url(r'^(?P<poll_id>\d+)/$', views.detail, name='detail'),
url(r'^(?P<poll_id>\d+)/results/$', views.results, name='results'),
url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),

    จากข้อ 4 และ 5 เมื่อเราทำการเพิ่มโค้ดไปแล้วนั้น urls ของเราจะมีรูปแบบในการเข้าใช้งานเพิ่มขึ้นอีก 3 แบบคือ

    1.เพิ่มเลขเข้าไปหลัง polls เช่น http://127.0.0.1:8000/polls/34 จะได้ output ดังนี้


    2.เพิ่มเลขเข้าไปหลัง polls แล้วตามด้วย results เช่น http://127.0.0.1:8000/polls/34/results จะได้ output ดังนี้


    3.เพิ่มเลขเข้าไปหลัง polls แล้วตามด้วย vote เช่น http://127.0.0.1:8000/polls/34/vote จะได้ output ดังนี้


    โดยส่วนที่ทำให้รับค่าตัวเลขได้คือ (?P<poll_id>\d+) โดยที่ ?P<poll_id> เป็นส่วนที่เอาไว้ระบุว่าค่าที่รับนั้นไปเป็นตัวแปรไหนซึ่งในที่นี้คือ poll_id ส่วน \d+ คือ regular expressions ซึ่ง d+ หมายถึงตัวเลขกี่หลักก็ได้

    Regular Expressions คือ ลำดับของตัวอักษรซึ่งเป็นรูปแบบหรือ pattern ส่วนใหญ่ใช้เช็คว่ามีรูปแบบตรงกับที่เรากำหนดหรือไม่

---Templates---

6.ในโฟลเดอร์ polls ให้เราสร้างโฟลเดอร์ใหม่ที่ชื่อ templates และในโฟลเดอร์ templates ที่สร้างใหม่ให้สร้างโฟลเดอร์ที่ชื่อ polls อีกครั้งจากนั้นให้สร้างไฟล์ index.html ไว้ภายในโดยภายในไฟล์ index.html มีโค้ดดังนี้

{% if latest_poll_list %}
    <ul>
    {% for poll in latest_poll_list %}
        <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

รูปแบบของโฟลเดอร์จะสรุปได้ดังนี้ polls > templates > polls > index.html

7.จากนั้นแก้ไข Functions index และเพิ่มการ import ของไฟล์ views.py ใน polls ดังนี้

from polls.models import Poll
def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    context = {'latest_poll_list': latest_poll_list}
    return render(request, 'polls/index.html', context)

    ซึ่งรูปแบบของ template index.html นี้หากใน latest_poll_list นั้นไม่มี Poll เลยก็จะแสดงผลว่า No polls are available. ที่ Browser ของเราแต่หากมีก็จะสร้างลิ้งค์ให้ผู้ใช้สามารถกดเข้าไปดูต่อได้ซึ่งจะเป็นการลิ้งค์แบบ polls/เลข id โดยจะเรียก Functions detail ออกมาแสดงผล

    ในส่วนของ views.py ที่แก้ไขนั้นได้ทำการ import Poll เข้ามาเพื่อใช้ในการเข้าถึงข้อมูลใน database ที่เก็บไว้ใน Functions index โดยตัวแปร latest_poll_list จะเป็นการสร้าง List ที่เก็บ Objects ของ Poll ไว้ 5 ตัวโดยเรียงลำดับตาม pub_date ที่มีการเพิ่มเข้ามาล่าสุดหากมีเกิน 5 ตัวก็จะนำมาแสดงเพียง 5 ตัวเท่านั้นจากนั้นในตัวแปร context มีไว้เพื่อกำหนดตัวแปร latest_poll_list ให้ตรงกับ 'latest_poll_list' ที่อยู่ใน templates index.html

    จากนั้นจึง เรียกใช้คำสั่ง render(request, 'polls/index.html', context) โดย render นั้นคือการเรียกไฟล์ templates index.html มาแล้วส่งตัวแปรทั้งหมดใน context ไปแปลงเป็นโค้ด HTML จากนั้นส่งโค้ดทั้งหมด return กลับไปแสดงผล


8.เข้าไปแก้ไข polls/views.py อีกครั้งโดยแก้ไขตามนี้

from django.shortcuts import render, get_object_or_404
def detail(request, poll_id):
    poll = get_object_or_404(Poll, pk=poll_id)
    return render(request, 'polls/detail.html', {'poll': poll})

9.จากนั้นสร้าง template ขึ้นมาอีกอันชื่อ detail.html เก็บไว้ใน polls > templates > polls > detail.html (ที่เดียวกับที่เก็บ templates index.html) โดยภายในมีรายละเอียดดังนี้

<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

    จากการแก้ไขในข้อ 9 และ 10 จะเมื่อลองเข้าไปที่ http://127.0.0.1:8000/polls/1 จะแสดงผลออกมาเป็น Choice ทั้งหมดของ polls What's up? ซึ่งคือ Poll ตัวแรกหรือ pk = 1 โดยในส่วนของ views.py จะเห็นว่ามีการ import get_object_or_404 เข้ามาซึ่ง get_object_or_404 มีไว้เพื่อให้ Functions นี้ดึง Objects จาก Poll โดยที่ pk = poll_id ที่รับค่ามาโดยหาก poll_id ที่ได้รับนั้นไม่มีอยู่ใน Poll แล้วจะเด้งไปยังหน้า Page not found 404 แทนโดยที่ทำแบบนี้เพื่อเป็นเงื่อนไขในการดักหากมีการส่งค่า poll_id ที่ไม่มีใน Poll ก็จะเป็นการบอกผู้ใช้ได้ในทันทีว่าไม่มี Objects นั้นอยู่แต่หากมีค่าก็จะได้ค่าเป็น Objects Poll กลับมาใช้ในการ render ได้เลยหากเทียบแล้วจริงๆจะพบว่า get_object_or_404 นั้นให้ผลเหมือนกัน โค้ดด้านล่างนี้

try:
    poll = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
    raise Http404

จะเห็นได้ว่าหาก มีการ get Objects แล้ว Objects นั้นไม่มีอยู่ก็จะเด้งไปหน้า Page not found 404 เช่นเดียวกับการใช้คำสั่ง get_object_or_404 เลย ซึ่งทั้ง render และ get_object_or_404 นั้นทั้งคู่เป็น API ที่ทางผู้สร้าง django สร้างไว้ให้เราเรียกใช้ได้ทันที

    ในส่วนของ templates detail.html นั้นจะเป็นการแสดงผลโดยจะใช้ชื่อคำถามของ Poll เป็น Header จากนั้นจึงเข้าไปอ่านข้อมูล Choice ทั้งหมดใน Poll นั้นๆและนำมาแสดงเป็นข้อๆผ่านทาง Browser ซึ่งใช้ render ในการนำข้อมูลใส่ลงใน templates เช่นกัน


10.แก้ไขไฟล์ Tutorial/urls.py จาก

url(r'^polls/', include('polls.urls')),

ให้เป็นแบบนี้

url(r'^polls/', include('polls.urls', namespace="polls")),

11.เข้าไปแก้ไข templates index.html ของ polls โดยแก้บรรทัด

<li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>

ให้เป็นแบบนี้

<li><a href="{% url 'polls:detail' poll.id %}">{{ poll.question }}</a></li>

    ผลที่ได้จากการแก้นั้นคือการลดการใช้ Code แบบตายตัวใน templates หากในอนาคตเราเปลี่ยนรูปแบบลิ้งค์ของ detail เราก็ไม่จำเป็นที่จะต้องเข้ามาแก้ไขใน templates เองเพราะ templates เราจะเรียกรูปแบบของลิ้งค์จาก urls.py เองหรือเรียกว่าลิ้งค์แบบ Dynamic โดย polls นั้นมาจากชื่อของ namespace ใน Tutorial/urls.py และ detail นั้นมากจากชื่อของ name ใน polls/urls.py

    ในส่วนของ urls.py ที่เพิ่ม namespace เข้าไปก็เพื่อลดความสับสนที่อาจเกิดขึ้นในอนาคตได้หากมี App หลายๆตัวสังเกตุได้จาก {% url 'polls:detail' poll.id %} หากเราไม่ใส่ namespace ใน url แบบแรกเราก็สามารถใช้ {% url 'detail' poll.id %} แบบนี้แทนได้ซึ่งก็ Dynamic เหมือนกันแต่อย่างที่กล่าวไว้ข้างต้นหากเรามี App มากๆเราอาจจะสับสนได้ว่า detail นี้คือของอันไหนนั่นเอง

    จาก Tutorial Part 3 นี้จะเห็นได้ว่า เราสามารถใช้ templates เพื่อแก้ปัญหาในส่วนของการสร้างไฟล์ HTML เองหลายๆไฟล์ได้โดย django จะเรียก templates ที่เราเขียนไว้มา render ออกเป็น Output แสดงผลทาง Browser ให้ผู้ใช้สามารถดูได้โดยที่เราไม่ต้องเขียน HTML ของ poll ทั้งหมดเอง และนอกจากนี้ ในส่วนของ urls เรายังสามารถดึงค่าจาก urls มาใช้ใน Functions เพื่อเลือก Objects หรือรูปแบบการแสดงผลต่างๆได้

ข้อมูลเพิ่มเติมของ Regular Expressions จาก wikipedia.org
Read more ...

วันอาทิตย์ที่ 1 ธันวาคม พ.ศ. 2556

Django Tutorial Part 2 - Admin site

    เริ่มต้นเปิด terminal แล้วทำการ cd ไปยัง Directory ที่เก็บไฟล์ manage.py ของ Project เอาจากนั้นพิม python manage.py runserver จะได้ดังรูป


    จากลองเข้าไปที่ 127.0.0.1:8000/admin จะปรากฏหน้าให้ Login เข้าระบบหากใครจำได้ใน Part 1 นั้นเราได้ทำการสร้าง Superuser เอาไว้แล้วนำ Username พร้อม Password ที่ตั้งไว้มาใส่ได้เลย


    จากนั้นไปแก้ไขไฟล์ admin.py ที่อยู่ใน polls/admin.py ของ Project ของเราเพื่อให้ สามารถแสดง App polls ที่เราสร้างใน Tutorial 1 ได้โดยแก้ไขให้เป็นดังนี้

from django.contrib import admin
from polls.models import Poll
admin.site.register(Poll)

แล้วลองเข้าไปที่ admin ใน Browser อีกครั้งจะได้ดังรูป



จากนั้นเข้าไปที่ Polls จะพบ What's up? ซึ่งเป็นข้อมูลที่เราได้สร้างไว้ใน Tutorial 1 คลิกเข้าไปเพื่อลองแก้ไข


จากนั้นลองแก้ไขในส่วนของ Date published เป็นเท่าไหร่ก็ได้


เมื่อแก้ไขเสร็จกด Save แล้วลองกดที่ History ดูจะเห็นข้อมูลว่าเราแก้ไขอะไรไป


การปรับเปลี่ยนรูปแบบการแสดงผล

    หากเราต้องการจะปรับแต่งรูปแบบการแสดงผลของการเข้าไปแก้ไขข้อมูลของ admin ก็สามารถทำได้โดยเพิ่ม Class เข้าไปในไฟล์ admin.py ใน polls/admin.py

แบบที่ 1 เลือกรูปแบบการจัดอันดับโดยเรียงตามลำดับข้อมูลใน list fields


class PollAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question'] 
admin.site.register(Poll, PollAdmin)


แบบที่ 2 แยกข้อความออกเป็น 2 บล็อก


class PollAdmin(admin.ModelAdmin):
    fieldsets = [
          (None,                    {'fields': ['question']}),
          ('Date information', {'fields': ['pub_date']}),
    ] 


แบบที่ 3 ต่อจากแบบที่ 2 นอกจากแยก 2 บล็อกแล้วยังซ่อนไว้ได้อีกด้วย


class PollAdmin(admin.ModelAdmin):
    fieldsets = [
         (None,                    {'fields': ['question']}),
         ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]


การเพิ่มObjectที่สัมพันธ์กันเข้ามาในหน้า Admin

    หากใครจะได้ว่าใน Tutorial Part 1 นั้นเรามี Choice ด้วยดังนั้นเราจะเพิ่ม Choice เข้ามาในส่วนของ Admin ได้ก็ต้องแก้ไขในไฟล์เดิม admin.py ใน polls/admin.py

from django.contrib import admin
from polls.models import Choice, Poll
class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3
class PollAdmin(admin.ModelAdmin):
    fieldsets = [
         (None,                    {'fields': ['question']}),
         ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]
admin.site.register(Poll, PollAdmin)

    โดยจากโค้ดที่เขียนไปจะทำให้ เราสามารถเพิ่มหรือ แก้ไข Choice ใน Poll ได้เลยโดยเพิ่มได้สูงสุด 3 Choice ซึ่งโค้ดนี้เป็นการบอก admin ว่า Choice นั้นให้มาแก้ไขใน Poll ได้เลย


    จากบรรทัดนี้ class ChoiceInline(admin.StackedInline):ในส่วนของ StackedInline นั้นเป็นรูปแบบของหน้าต่าง Choice ที่เพิ่มเข้าหากเราเปลี่ยนไปใช้เป็น
class ChoiceInline(admin.TabularInline): ก็จะได้รูปแบบการจัดเป็นอีกแบบ


การปรับเปลี่ยนแก้ไขในส่วนของ admin change list
    ในส่วนของ Class Poll ใน Tutorial Part 1 นั้นเราจะมี 2 Variable 1 Method ซึ่งเราสามารถแสดงทั้งหมดนั้นในหน้า admin change list ได้โดยการเพิ่มโค้ดข้างล่างนี้ไปไว้ล่างสุดของ class PollAdmin ใน admin.py

list_display = ('question', 'pub_date', 'was_published_recently')


    จากการเพิ่มโค้ดเข้าไปแล้วนอกจากจะแสดงผลแบบที่เราต้องการแล้วยังสามารถเรียงตามลำดับโดยหัวข้อได้อีกด้วยแต่มี 1 ช่องที่เรียงไม่ได้คือ was_published_recently ดังนั้นวิธีแกก็คือการเพิ่มโค้ดเข้าไปในไฟล์ model.py ล่างสุดของ Class Poll


was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'




    และนอกจากนี้เรายังสามารถเพิ่ม Filter และช่อง Serach ได้อีกโดยใส่โค้ดเพิ่มไปในไฟล์ admin.py ใต้ class PollAdmin

list_filter = ['pub_date']search_fields = ['question']



    จาก Tutorial Part 2 นี้เราสามารถที่จะปรับแต่งหน้า admin ของเราให้แสดงผลในรูปแบบตามที่ต้องการได้ เช่น เรียงลำดับตามการแก้ไขก่อนหลัง, การใช้ filter ในการช่วยค้นหาข้อมูล เป็นต้น เราสามารถเพิ่ม , ลบ หรือ แก้ไข Database ของ Apps แต่ละตัวได้ โดยการแก้ไขในไฟล์ admin.py 
Read more ...

Mercurial - Version Control System App

เริ่มต้นกันด้วยการ Install Mercurial ใน Ubuntu กันก่อนนะครับ
เปิด Terminal แล้วพิม sudo apt-get install mercurial


    จากนั้น cd ไปยัง Directory ที่ต้องการให้ Mercurial จัดการให้ และพิม hg init เพื่อเป็นการให้ Mercurial เริ่มทำงานจากนั้นลองพิม hg status จากนั้นพิม hg add จะสังเกตุเห็นว่าหน้าชื่อจะมีสัญลักษณ์ 2 แบบคือ ? กับ A ซึ่ง A หมายถึงว่าไฟล์นี้ได้ถูกเลือกเพื่อทำการจัดเก็บแล้วส่วน ? นั่นคือเรายังไม่ได้เลือกว่าจะให้ทำอย่างไรกับไฟล์เหล่านั้น



hg add เป็นคำสั่งเลือกไฟล์ให้ Mercurial จัดการโดยมีรูปแบบดังนี้

1.hg add เลือกไฟล์ทั้งหมดทุกไฟล์เข้าระบบ
2.hg add filename เลือกเฉพาะไฟล์ที่ตรงกับ filename เท่านั้น

จากนั้นลองพิม hg revert ./Tutorial1/*.pyc และ hg revert ./polls/*.pyc


hg revert ยกเลิกการเลือกไฟล์เข้าระบบโดยการยกเลิกมีรูปแบบดังนี้

1.hg revert * ยกเลิกการเลือกไฟล์ทั้งหมด
2.hg revert filename จะยกเลิกการเลือกไฟล์ที่ตรงกับ filename นั้นๆ

ซึ่งไฟล์ที่ไม่ควรเก็บก็จะมีไฟล์พวก .pyc .elc เป็นต้นซึ่งเราจะแก้ปัญหานี้โดยการเพิ่มไฟล์ .hgignore เข้าไปแต่ก่อนอื่นให้เราพิมคำสั่งใน terminal ว่า hg serve -p 9000


และลองเปิด Browser ขึ้นมาจากนั้นไปยัง address 127.0.0.1:9000/ จะโหลดหน้าของ Mercurial ขึ้นไปยังเมนูด้านซ้ายคลิกที่ help>hgignore จากนั้นลงไปล่างสุด Copy ตัวอย่างโค้ดของ hgignore ไว้


 จากนั้นกลับไปสร้างไฟล์ .hgignore โดยใช้โค้ดที่เราก็อปมาเมื่อกี้ใส่ลงในไฟล์ดังรูป


จากนั้นกลับไปที่ Terminal แล้วลองพิม hg status ดูจะพบว่าไม่มีไฟล์ .pyc อยู่ในรายชื่อเลยเนื่องจากไฟล์ .hgignore ที่เราสร้างไว้สักครู่เป็นตัวกรองออกไปจากนั้นพิม hg add เพื่อเลือกไฟล์ทุกไฟล์


 จากนั้นให้พิม hg commit -m "Description" -u Author จากตัวอย่าง Description ใช้เป็น Start Project และ Author เป็น jinkatana ซึ่งคำสั่ง commit นี้ใช้ในการสั่งให้ Mercurial เก็บค่าของไฟล์ในขณะนั้นเป็น 1 Version ของเรา


ลองแก้ไขไฟล์อะไรก็ได้ไฟล์นึงใน Project ของเราจากนั้นลองพิม hg status จะมีชื่อไฟล์ที่เราแก้ไขโผล่ขึ้นมาพร้อมตัวอักษร M นำหน้าหมายถึงว่าไฟล์ถูกแก้ไขไปวิธีการอัพเดทให้ Mercurial เก็บค่าล่าสุดของ Project ก็พิมคำสั่ง hg commit -m "Description" -u Author เช่นเดิม


เมื่อ commit เสร็จแล้วลองเปิด Browser เข้าไปที่ 127.0.0.1:9000 เช่นเดิมจะพบว่ามี Versions ใหม่เพิ่มเข้ามาโดยเราสามารถกดเข้าไปดูได้ว่ามีการเปลี่ยนแปลงอะไรไปบ้างในแต่ละ Versions ครับ


---เพิ่มความแตกต่างระหว่างเวอร์ชั่นให้ดู
Read more ...

Django Tutorial Part 1 - Database&models


Django Tutorial Part1

1.เริ่มต้นให้ทำการสร้าง Project โดยการเปิด terminal แล้วทำการ cd ไปยัง Directory ที่ต้องการจะเก็บ Project ไว้จากนั้นพิมคำสั่ง django-admin.py startproject Tutorial1

2.ทำการแก้ชื่อเพื่อป้องกันความสับสนเช่นเดิมโดยใช้คำสั่ง mv Tutorial1/ Django_Tutorial1


3.เข้าไปแก้ไขไฟล์ settings.py โดยเปลี่ยน TIME_ZONE เป็น Asia/Bangkok หรือเปลี่ยนไปยังที่ ที่เราอยู่

  

4.จากนั้นให้ cd กลับไปยัง Directory ที่มีไฟล์ manage.py อยู่แล้วพิมคำสั่ง python manage.py syncdb

5.จะมีข้อความขึ้นมาถามว่าเราต้องการสร้าง superusers ไหมให้ตอบ yes แล้วใส่รายละเอียดตามที่กำหนดลงไป



6.เริ่มสร้าง models โดยพิมคำสั่ง python manage.py startapp polls



7.จะพบว่ามีโฟลเดอร์ polls เพิ่มเข้ามาจากนั้นให้ cd เข้าไปใน polls จะพบไฟล์ models.py ให้ทำแก้ไฟล์เป็นตามนี้

from django.db import models
class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    จากโค้ดที่ใส่เข้าไปใน models.py นั้นเป็นการสร้าง Class 2 Class ที่ชื่อ Poll และ Choice ซึ่งเป็น Class ที่สืบทอดมาจาก models.Model ซึ่งเป็น API ของ django.db ที่เราได้ import มาในบรรทัดแรก

    โดยใน Class Poll ประกอบด้วยตัวแปรสองตัวคือ question ซึ่งเป็นตัวแปรที่มาจาก Class CharField โดยมีความยาวสูงสุด 200 ตัวอักษร และตัวแปร pub_date ซึ่งมาจาก Class DateTimeField ซึ่งใช้เก็บวัน เดือน ปี และ เวลา

    ส่วน Class Choice ประกอบด้วยตัวแปร 3 ตัวคือ poll เป็นการสร้างความสัมพันธ์ ว่า Class Choice นั้นมีความสัมพันธ์กับ Class Poll ตัวแปร choice_text เป็นตัวแปรที่มาจาก Class CharField ซึ่งกำหนดความยาวสูงสุดไว้ที่ 200 ตัวอักษร และตัวแปร votes มาจาก Class IntegerField ซึ่งหลักการจะคล้ายๆกับ CharFiels เพียงแต่ Class นี้เก็บตัวแปรในรูปแบบของ Integer แทน

   
8.กลับไปแก้ไขไฟล์ settings.py ที่เราแก้ TIME_ZONE อีกครั้งโดยคราวนี้ให้แก้ไขที่ INSTALLED_APPS โดยเพิ่ม polls เข้าไปในส่วนของ INSTALLED_APPS ดังรูป


9.จากนั้นพิมคำสั่ง cd กลับไปยัง Directory ที่มีไฟล์ manage.py อยู่แล้วพิม python manage.py shell แล้วพิมคำสั่งตามรูปนี้


10.จะเห็นว่า Output รอบสุดท้ายจะได้เป็น [<Poll: Poll object>] ซึ่งแสดงผลออกมาว่าเป็น Poll Object แต่จะไม่สามารถดึงค่าที่อยู่ใน Object มาแสดงผลได้ดังนั้นให้ไปแก้โค้ดอีกครั้งในไฟล์ models.py ที่แก้ไปก่อนหน้านี้ดังนี้ เพื่อให้สามารถดึงค่าภายใน Object ออกมาแสดงได้

from django.db import models
import datetime
from django.utils import timezone
class Poll(models.Model):
    # ...
    def __unicode__(self): # Python 3: def __str__(self):
        return self.question
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Choice(models.Model):
    # ...
    def __unicode__(self): # Python 3: def __str__(self):
        return self.choice_text

    จากโค้ดนี้ได้มีการ import API เพิ่มเข้ามาอีก 2 อันคือ datetime จาก python และ timezonne จาก django.utils เข้ามาเพื่อใช้ใน function was_published_recently

    โดย Function __unicode__ นั้นมีไว้เพื่อ return ตัวแปร question และ choice_text กลับออกไปเพื่อให้เวลาเรียกคำสั่ง Poll.objects.all() นั้นแสดงค่าของตัวแปร question และ choice_text ออกมาโดยตรง

    ส่วน Function was_published_recently นั้นคือการ return ค่าออกไปว่าเป็น True หรือ False โดยจะเป็น True ก็ต่อเมื่อ เวลาใน pub_date จะมีค่ามากกว่าหรือเท่ากับ เวลา ณ ปัจจุบัน ลบ ด้วยเวลาถัดไปอีก 1 วัน นั่นหมายความว่า Function นี้จะได้ค่า True หากเราเรียกเมื่อเวลาหลังจากที่เราสร้าง Poll ไม่เกิน 1 วันนั่นเอง

11.เมื่อแก้เสร็จแล้วกลับไปพิมคำสั่ง python manage.py syncdb และ python manage.py shell อีกครั้งหากใครยังไม่ได้ปิด shell ตัวเก่าก็ให้ปิดแล้วพิมคำสั่งรัน shell ใหม่อีกครั้งแล้วพิมคำสั่งตามรูปนี้

       
คำอธิบาย Code ใน Shell

โดยบรรทัดที่ 1 และ 5 เป็นการ import class เข้ามาใช้งาน

บรรทัดที่ 2, 3, 4, 7, 8, 9 เป็นการอ่านค่าจาก Poll ใน Database ออกมาแสดงผล

แต่จะเห็นว่าบรรทัดที่ 8 มี error เนื่องจากเรามี Object ใน Database เพียงแค่ตัวเดียวแต่บรรทัดที่ 8 เรียกหาตัวที่ 2 ซึ่งไม่มีใน Database จึงทำให้เกิด error

บรรทัดที่ 6 ดึงปีปัจจุบันออกมาเก็บไว้ใน current_year

บรรทัดที่ 10 และ 12 ให้ตัวแปร p ชี้ไปยัง Object ใน Database ผ่าน Poll.Object.get(pk=1) pk = Primary Key ซึ่งให้ผลเหมือนกับ Poll.Object.get(id=1)

บรรทัดที่ 11 เป็นการเรียกใช้ method was_published_recently โดยให้ค่ากลับมาเป็น True เมื่อ timezone.now() - datetime.timedelta(days=1) น้อยกว่าหรือเท่ากับ pub_date ที่เก็บไว้ตอนสร้าง Object เก็บใน Database

บรรทัดที่ 13, 18, 23 เป็นการแสดง set ของ Choice ที่มีความสัมพันธ์กับ Object ของ Database p

บรรทัดที่ 14, 15, 16 เป็นการสร้าง Choice 3 อันเข้าไป

บรรทัดที่ 17 เป็นการแสดง poll ที่สัมพันธ์กับ Choice นั้นๆ

บรรทัดที่ 19 แสดงจำนวน Choice ที่สัมพันธ์กับ poll นั้น

บรรทัดที่ 20 จะแสดง Choice โดยต้องเป็น Choice ที่ตั้งในปีนี้

บรรทัดที่ 21 ให้ c ชี้ไปที่ Object ของ Choice ที่เริ่มต้นด้วย Just hacking

บรรทัดที่ 22 ลบ Object ที่ c ชี้ไปจากบรรทัดที่ 21

    จาก Tutorial Part 1 ทำให้เรารู้จักการสร้าง models (Apps) เพื่อใช้สำหรับ Web Application โดยในเว็บ 1 เว็บเราสามารถมี Application ได้มากกว่า 1 Application การใช้งาน Database ในการเก็บข้อมูลใน Apps ซึ่งช่วยให้ง่ายต่อการจัดการข้อมูลสำหรับคนที่ไม่มีพื้นฐานทางด้าน Database มาก่อนเลย
Read more ...