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

 

คราวนี้ทำบล๊อกจาก Falling Bullets - Blog - WordPress Clone in 27 Seconds (Part 1 of 40)

โดย

  • ใช้งานกับ 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
  • การติดตั้งพื้นฐาน ดูจาก django: ใช้กับ apache2 บนเดเบียน
  • เนื้อความในนี้อาจมีข้อความติดพันมาจากการทดลองจากคราวก่อน ๆ ดังนั้นถ้างง ฝากย้อนขึ้นไปดูคราวก่อน เริ่มตั้งแต่ /node/424 ลงมา

สร้างแอพลิเคชั่นชื่อ blog ในไดเรคทอรี่ dj จากครั้งก่อน
$ cd ~/www/dj
$ python manage.py startapp blog

สร้างตารางฐานข้อมูลด้วย models.py ให้มี 2 ตาราง คือเก็บแท็ก และเก็บเนื้อเรื่อง
$ vi blog/models.py

from django.db import models

class Tag(models.Model):
    name = models.CharField(maxlength=50, core=True)
    slug = models.SlugField(prepopulate_from=("name",))

    class Admin:
        pass

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return "/dj/blog/tags/%s/" % (self.slug)

class Entry(models.Model):
    title = models.CharField(maxlength=200)
    slug = models.SlugField(
        unique_for_date='pub_date',
        prepopulate_from=('title',),
        help_text='Automatically built from the title.'
    )
    summary = models.TextField(help_text="One paragraph. Don't add <p> tag.")
    body = models.TextField()
    pub_date = models.DateTimeField()
    tags = models.ManyToManyField(Tag, filter_interface=models.HORIZONTAL)

    class Meta:
        ordering = ('-pub_date',)
        get_latest_by = 'pub_date'

    class Admin:
        list_display = ('pub_date', 'title')
        search_fields = ['title', 'summary', 'body']

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return "/dj/blog/%s/%s/" % (self.pub_date.strftime("%Y/%b/%d").lower(), self.slug)

แก้ไข urls.py ให้สามารถเรียกไดเรกทอรี่เลียนแบบ Wordpress หรือเรียกแบบปกติ
$ vi urls.py

from django.conf.urls.defaults import *
from dj.blog.models import Entry

blog_dict = {
    'queryset': Entry.objects.all(),
    'date_field': 'pub_date',
}

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'),
    (r'^dj/blog/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\w{1,2})/(?P<slug>[-\w]+)/$', 'django.views.generic.date_based.object_detail', dict(blog_dict, slug_field='slug')),
    (r'^dj/blog/?$', 'django.views.generic.date_based.archive_index', blog_dict),
)

รัน syncdb ครั้งนึง เพื่อสร้างและปรับปรุงตาราง
$ python manage.py syncdb

  title = models.CharField(maxlength=250, unique=True)
  title = models.CharField(maxlength=250)

ต่อไปเป็นเรื่องอินเทอร์เฟสแสดงหน้าตา

(เที่ยวนี้แปลกไปนิดนึง เพราะเขาเรียกแสดงผลผ่านฟังก์ชั่นมาตรฐานของ django โดยไม่ได้ใช้ views ของเรา เลยต้องวางไดเรกทอรี่ไว้เป็นมาตรฐาน คือเอา templates ไว้ที่ root ของโครงการ)

แก้ไข settings.py ให้มาใช้ template ของเรา รวมทั้งบอกให้เปิดมอดูล blog ที่เราเพิ่งสร้างขึ้น
$ 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',
    '/home/user1/www/dj/templates',
)

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

สร้างเทมเพลตโดยการสร้างไดเรกทอรี่ชื่อ templates ไว้ที่ root ของโครงการ
ในไดเรกทอรี่ templates จะมีไฟล์ base.html เอาไว้ดูหน้าหลักซึ่งเป็นพวกเมนูต่าง ๆ
และสร้างไดเรกทอรี่ย่อยชื่อ templates/blog อีกที จะมีไฟล์ entry_archive.html ไว้ดูหัวข้อบล๊อก และ entry_detail.html ไว้ดูรายละเอียดของแต่ละรายการ
$ mkdir -p templates/blog
$ vi templates/base.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" xml:lang="en" lang="en">
<head>
    <title>My Site - {% block title %}{% endblock %}</title>

    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
    <div id="header">
        <h1><a href="/">My Interweb Tubes Blog</a></h1>

        <h2>It's not a truck!</h2>

        <ul id="nav">
            <li><a href="/dj/">Home</a></li>
            <li><a href="/dj/blog/">Blog</a></li>
            <li><a href="#">Photos</a></li>
            <li><a href="/dj/links/">Links</a></li>
            <li><a href="/dj/portfolio/">Work</a></li>
            <li><a href="/dj/colophon/">Colophon</a></li>
        </ul>
    </div>
    <div id="content">
        {% block content %}

        {% endblock %}
    </div>
</body>
</html>

$ vi templates/blog/entry_archive.html

{% extends "base.html" %}

{% block title %}Latest Blog Entries{% endblock %}

{% block content %}
    <h1>Latest Blog Entries</h1>

    <ol id="object-list">
        {% for object in latest %}
            <li>
                <h2><a href="{{ object.get_absolute_url }}">{{ object.title|escape }}</a></h2>
                <p class="post-date">{{ object.pub_date|date:"F j, Y" }}</p>
                <p class="summary">{{ object.summary }}</p>
            </li>
        {% endfor %}
    </ol>
{% endblock %}

$ vi templates/blog/entry_detail.html

{% extends "base.html" %}

{% block title %}Blog - {{ object.title|escape }}{% endblock %}

{% block content %}
    <h1>{{ object.title|escape }}</h1>

    <dl>
        <dt>Posted On:</dt>
        <dd>{{ object.pub_date|date:"F j, Y" }}</dd>
        <dt>Tags:</dt>
        <dd>
            {% for tag in object.tags.all %}
                <a href="{{ tag.get_absolute_url }}">{{ tag.name }}</a>{% if not forloop.last %}, {% endif %}
            {% endfor %}
        </dd>
    </dl>

    {{ object.body }}
{% endblock %}

เรียกปรับปรุงตารางอีกครั้ง
python manage.py syncdb

  title = models.CharField(maxlength=250, unique=True)
  title = models.CharField(maxlength=250)
  name = models.CharField(maxlength=50, core=True)
  title = models.CharField(maxlength=200)
Creating table blog_entry
Creating table blog_tag
Installing index for blog.Entry model
Installing index for blog.Tag model

เสร็จแล้ว เรียกผ่าน URL:http://www.example.com/dj/blog ได้ดังนี้
เรียก blog 1