django+apache2: สร้างแอพลิเคชั่น todo

จากครั้งก่อน django: ใช้กับ apache2 บนเดเบียน (มีการปรับปรุงให้เนื้อหาสมบูรณ์ขึ้นในหน้าเก่าด้วย)

ตอนนี้เราจะมาสร้างแอพลิเคชั่นชื่อ "to do" จาก sitepoint.com - Django Djumpstart: Build a To-do List in 30 Minutes

โดย

  • ใช้งานกับ apache2 ที่ติดตั้งเรียบร้อยแล้ว
  • เรียกใช้งานภายใต้ไดเรกทอรี่ http://www.example.com/dj
    (ซึ่งจริง ๆ แล้วไม่ค่อยดีเท่าไหร่ เวลาอ้างถึงหน้าหลักมันจะอ้างยาก หากเราต้องการเปลี่ยนไดเรกทอรี่มาเป็น root มันต้องตามเปลี่ยนในโค๊ดด้วย ลองดูจากตัวอย่างได้
    ที่ควรทำจริง ๆ คืออ้าง URL ใหม่ โดยใช้ไปอยู่ที่ root แทน จะยุ่งน้อยกว่า เช่น http://dj.example.com เป็นต้น)

    แต่ของเราเที่ยวนี้เอาแบบอยู่ภายใต้ไดเรคทอรี่ /dj ไปก่อน
  • ใช้ฐานข้อมูล postgresql ที่ติดตั้งไว้แล้ว
  • root ของ apache2 อยู่ที่ /home/user1/www
  • ชื่อผู้ใช้งานฐานข้อมูลคือ user1 รหัสผ่านคือ USER1_PASSWORD มีสิทธิ์ในการสร้างฐานข้อมูล
  • โครงการ (project) ชื่อ dj เหมือนเดิม ฉะนั้นไดเรกทอรี่ของโครงการจะไปอยู่ที่ /home/user1/www/dj

เริ่มเลย

สร้างแอพลิเคชั่นชื่อ "to do" เอาไว้สำหรับดูว่าจะทำงานอะไรบ้าง
$ cd ~/www/dj
$ python manage.py startapp todo

สร้างฐานข้อมูลผ่านโปรแกรมชื่อ todo/models.py
โดยเราจะสร้างเป็น 2 ตาราง โดยแต่ละตารางจะเป็นคลาสใน model.py
คือคลาส List สำหรับดูหัวข้อ และคลาส Item สำหรับเก็บรายละเอียดของข้อมูลของงานที่จะทำ
$ vi todo/models.py

...
#TABLE List
class List(models.Model):
  title = models.CharField(maxlength=250, unique=True)
  def __str__(self):
    return self.title
  class Meta:
    ordering = ['title']
  class Admin:
    pass


#TABLE Item
import datetime

PRIORITY_CHOICES = (
  (1, 'Low'),
  (2, 'Normal'),
  (3, 'High'),
)

class Item(models.Model):
  title = models.CharField(maxlength=250)
  created_date = models.DateTimeField(default=datetime.datetime.now)
  priority = models.IntegerField(choices=PRIORITY_CHOICES, default=2)
  completed = models.BooleanField(default=False)
  todo_list = models.ForeignKey(List)
  def __str__(self):
    return self.title
  class Meta:
    ordering = ['-priority', 'title']
  class Admin:
    pass

ถึงตอนนี้ต้องมีฐานข้อมูลอยู่แล้ว หากยังไม่ได้สร้างฐานข้อมูล ให้ย้อนไปดูคราวก่อน
ปรับตั้งให้ django รับรู้ถึงการเพิ่มตาราง ผ่านไฟล์ settings.py
$ vi settings.py

...
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'dj.todo',
)

สั่งสร้างตาราง/ปรับปรุงฐานข้อมูล
$ python manage.py syncdb

  title = models.CharField(maxlength=250, unique=True)
  title = models.CharField(maxlength=250)
Creating table todo_item
Creating table todo_list
Installing index for todo.Item model

ต่อไปเป็นการสร้างอินเทอร์เฟสผ่านไฟล์ชื่อ todo/views.py
ในไฟล์นี้เราจะสร้างฟังก์ชั่นในการแสดงรายงานสถานะของงานชื่อว่า status_report
$ vi todo/views.py

...
from django.shortcuts import render_to_response
from dj.todo.models import List

def status_report(request):
 todo_listing = []
 for todo_list in List.objects.all():
   todo_dict = {}
   todo_dict['list_object'] = todo_list
   todo_dict['item_count'] = todo_list.item_set.count()
   todo_dict['items_complete'] = todo_list.item_set.filter(completed=True).count()
   todo_dict['percent_complete'] = int(float(todo_dict['items_complete']) / todo_dict['item_count'] * 100)
   todo_listing.append(todo_dict)
 return render_to_response('status_report.html', { 'todo_listing': todo_listing })

เอาตารางมาใช้จากคลาส List ใน todo/models.py

ไฟล์ views.py นี้เป็นฟังก์ชั่นการทำงานล้วน ๆ ซึ่งเราจะต้องสร้างเทมเพลตในการแสดงผลอีกทีหนึ่ง
ในการสร้างเทมเพลต เราจะสร้างไดเรคทอรี่ย่อยชื่อ templates ไว้ใน todo เพื่อเอาไว้บรรจุไฟล์เทมเพลต คือไฟล์ HTML ในที่นี้ตั้งชื่อว่า status_report.html
$ mkdir todo/templates
$ vi todo/templates/status_report.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <title>To-do List Status Report</title>
 </head>
 <body>
   <h1>To-do list status report</h1>
{% for list_dict in todo_listing %}
   <h2>{{ list_dict.list_object.title }}</h2>
   <ul>
     <li>Number of items: {{ list_dict.item_count }}</li>
     <li>Number completed: {{ list_dict.items_complete }} ({{ list_dict.percent_complete }}%)</li>
   </ul>
{% endfor %}
 </body>
</html>

สังเกตุว่าคำสั่งจะอยู่ในบล๊อก {% COMMAND %} และตัวแปรจะอยู่ในบล๊อก {{ VARIABLE }} โดยตัวแปรที่ใช้ ใช้เสมือนเราอยู่ภายในมอดูล todo.views.status_report ซึ่งเราต้องกลับไปบอก django ในไฟล์ urls.py

ต้องกลับไปบอก django ว่าโครงการของเรามีเทมเพลตด้วย และเทมเพลตเราอยู่ที่ไหน ผ่าน settings.py
$ vi settings.py

...
TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    '/home/user1/www/dj/todo/templates',
)

และกำหนดให้ apache2 มาเรียกใช้ todo เมื่อเข้า URL:/dj/report/ ผ่าน urls.py
$ vi urls.py

...
urlpatterns = patterns('',
    # Example:
    # (r'^dj/', include('dj.foo.urls')),

    # Uncomment this for admin:
    (r'^dj/admin/', include('django.contrib.admin.urls')),
    (r'^dj/report/$', 'dj.todo.views.status_report'),
)

ตอนนี้ดูได้แล้ว ผ่าน URL:http://www.example.com/dj/report
หน้าจอแรก todo

ตอนนี้ยังไม่มีอะไร เพราะเรายังไม่ได้ใส่อะไรเข้าไป
ถึงตอนนี้เราสามารถใส่เนื้อหาใหม่เข้าไปได้ ผ่านทางหน้า admin
โดยต้องเพิ่ม List ก่อน ทาง URL:http://www.example.com/dj/admin/todo/list/add
ตามด้วย Item ทาง URL:http://www.example.com/dj/admin/todo/item/add
พอเข้าหน้า report ใหม่ ก็จะเห็นรายการตามต้องการ