ลอง django
เที่ยวนี้เอาแบบดิบ ๆ เลย
http://www.djangoproject.com/documentation/install/
Install Python
# apt-get install python
+++ mime-support python python-minimal python2.4 python2.4-minimal
Install Apache and mod_python
# apt-get install apache2 libapache2-mod-python
+++ apache2 apache2-mpm-worker apache2-utils apache2.2-common libapache2-mod-python libapr1 libaprutil1 libexpat1 libmagic1 libpcre3 libpq4 libsqlite3-0 python-central
http://www.djangoproject.com/documentation/modpython/
Django requires Apache 2.x and mod_python 3.x, and you should use Apache's prefork MPM, as opposed to the worker MPM.
# apt-get install apache2-mpm-prefork
--- apache2-mpm-worker
+++ apache2-mpm-prefork
Get your database running
Django works with PostgreSQL (recommended), MySQL and SQLite.
# apt-get install postgresql-8.1
+++ openssl postgresql-8.1 postgresql-client-8.1 postgresql-client-common postgresql-common ssl-cert
แก้ปัญหาการจัดเรียงภาษาไทยของ postgresql
ตรวจ locales ให้มีภาษาไทย
# dpkg-reconfigure locales
<<<--- (*) th_TH TIS-620
<<<--- (*) th_TH.UTF-8 UTF-8
inintdb ใหม่ให้เรียงตามภาษาไทย โดยจะสร้างไดเรคทอรี่ของข้อมูลใหม่ ให้อยู่ที่ /server1/var/lib/postgresql/8.1/main
# /etc/init.d/postgresql-8.1 stop
# mkdir -p /server1/var/lib/postgresql/8.1/main
# chown -R postgres:postgres /server1/var/lib/postgresql
# su postgres
$ /usr/lib/postgresql/8.1/bin/initdb -D /server1/var/lib/postgresql/8.1/main --locale=th_TH.UTF-8 --pgdata=/server1/var/lib/postgresql/8.1/main
$ cd /server1/var/lib/postgresql/8.1/main
$ ln -sf /etc/postgresql-common/root.crt .
$ ln -sf /etc/ssl/certs/ssl-cert-snakeoil.pem server.crt
$ ln -sf /etc/ssl/private/ssl-cert-snakeoil.key server.key
$ exit
# cd /etc/postgresql/8.1/main
# rm pgdata
# ln -sf /server1/var/lib/postgresql-8.1/main pgdata
# /etc/init.d/postgresql-8.1 start
ปรับให้ผู้ใช้ในระบบสามารถเข้ามาใช้งานโดยใช้รหัสผ่านของระบบ
# vi /etc/postgresql/8.1/main/pg_hba.conf
... # TYPE DATABASE USER CIDR-ADDRESS METHOD host all all 192.168.1.0/24 md5 ...
สร้างผู้คุมฐานข้อมูล
# su postgres
$ psql template1
template1=# CREATE USER superx SUPERUSER PASSWORD 'superx';
template1=# \q
$ exit
ติดตั้ง phppgadmin
# apt-get install phppgadmin
+++ libapache2-mod-php4 libzzip-0-12 php4-common php4-pgsql phppgadmin wwwconfig-common
# dpkg-reconfigure phppgadmin
Which web server would you like to reconfigure automatically?
<<<--- Apache2
# vi /etc/apache2/conf.d/phppgadmin
# deny from all allow from all
# /etc/init.d/apache2 restart
If you're using PostgreSQL, you'll need the psycopg package
# apt-get install python-psycopg python-psycopg2
+++ python-egenix-mxdatetime python-egenix-mxtools python-psycopg python-psycopg2
Download Django-0.95.tar.gz from our download page.
# cd /usr/src
# tar xfz Django-0.95.tar.gz
# cd Django-0.95
Note that the last command will automatically download and install setuptools if you don't already have it installed. This requires a working Internet connection.
# apt-get install python-setuptools
+++ python-setuptools
# python setup.py install
เสร็จ Django
http://www.sitepoint.com/article/build-to-do-list-30-minute
จะสร้างไดเรคทอรี่ของเว็บ โดยให้ webmaster เป็นเจ้าของ
# useradd -m -g www-data webmaster
# su webmaster
$ cd
$ mkdir django
$ cd django
Diving In
จะสร้างโปรเจคต์ ชื่อ gtd
$ django-admin.py startproject gtd
$ cd gtd
รันเซิร์ฟเวอร์ที่พอร์ต 8000 ไอพี 192.168.1.5
$ python manage.py runserver 192.168.1.5:8000
ทดสอบโดย เอาบราวเซอร์ไปที่ http://192.168.1.5:8000/
หยุดเซิร์ฟเวอร์ด้วย Ctrl-C
จะสร้างแอปพลิเกชั่น todo
$ python manage.py startapp todo
$ cd todo
$ vi models.py
class List(models.Model): title = models.CharField(maxlength=250, unique=True) def __str__(self): return self.title class Meta: ordering = ['title'] class Admin: pass 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
$ cd ..
$ su postgres
postgres@server1$ psql template1
template1=# CREATE DATABASE "django" WITH ENCODING='UTF8';
template1=# \q
postgres@server1$ exit
$ vi settings.py
... DATABASE_ENGINE = 'postgresql' DATABASE_NAME = 'django' DATABASE_USER = 'USER1' DATABASE_PASSWORD = 'USER1PASSWORD' ... DATABASE_PORT = '5432' ... TIME_ZONE = 'Asia/Bangkok' ... INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'gtd.todo', )
$ python manage.py syncdb
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no):
<<<--- yes
Username (Leave blank to use 'webmaster'):
<<<--- {DEFAULT}
E-mail address:
<<<--- webmaster@example.com
Password:
<<<--- {WEBMASTER-PASSWORD}
Password (again):
<<<--- {WEBMASTER-PASSWORD}
$ vi settings.py
... INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'gtd.todo', 'django.contrib.admin', )
$ vi urls.py
... (r'^admin/', include('django.contrib.admin.urls')), ...
$ python manage.py syncdb
เริ่มรัน
$ python manage.py runserver 192.168.1.5:8000
เอาบราวเซอร์ไปที่ http://192.168.1.5:8000/admin
Username:
<<<--- webmaster
Password:
<<<--- {WEBMASTER-PASSWORD}
ลองเพิ่มผู้ใช้และกรุ๊ปดู
$ cd todo
$ vi views.py
... from django.shortcuts import render_to_response from gtd.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 })
$ mkdir templates
$ cd templates
$ vi 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>
$ cd ../..
$ vi settings.py
... TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates". # Always use forward slashes, even on Windows. '/home/webmaster/django/gtd/todo/templates', )
$ vi urls.py
... urlpatterns = patterns('', # Example: # (r'^gtd/', include('gtd.apps.foo.urls.foo')), # Uncomment this for admin: (r'^admin/', include('django.contrib.admin.urls')), (r'^report/$', 'gtd.todo.views.status_report'), )
$ python manage.py runserver 192.168.1.5:8000
ลองดูที่บราวเซอร์ http://192.168.1.5:8000/report
เรียบร้อย
ในการติดตั้งของ django เขาใช้พอร์ต 8000 เป็นค่าปริยาย
ทำนองเดียวกัน apache ก็ใช้พอร์ต 80 เป็นค่าปริยาย
หากต้องการไปรัน django ที่พอร์ต 80 ต้องทำดังนี้
แก้ไข /etc/apache2/ports.conf ให้ไปใช้พอร์ตอื่น สมมุติว่าเป็น 8088
$ sudo vi /etc/apache2/ports.conf
Listen 8088
เวลาสั่งรัน django ต้องใช้สิทธิ์รูตในการรัน (พอร์ตที่ต่ำกว่า 1000)
$ sudo python manage.py runserver 192.168.1.5:80
เอามาจาก Falling Bullets - Blog - WordPress Clone in 27 Seconds (Part 1 of 40)
เราชื่อ webmaster
# su webmaster
เราตั้งให้ไฟล์ของเราอยู่ใน ~/django
$ cd ~/django
ก่อนเริ่ม ให้ลบ database ชื่อ mysite ที่เราเคยทำไว้ออกก่อน
แล้วจึงค่อยสร้างใหม่แบบว่าง ๆ
$ psql template1 -U superx
Password for user superx <<<--- SUPERX-PASSWORD
template1=# DROP DATABASE mysite;
template1=# CREATE DATABASE mysite;
template1=# \q
เริ่มสร้างโปรเจกต์
$ django-admin.py startproject mysite
$ cd mysite
แก้ไข settings.py ให้เรียบร้อย
$ vi settings.py
... DATABASE_ENGINE = 'postgresql' DATABASE_NAME = 'mysite' DATABASE_USER = 'superx' DATABASE_PASSWORD = 'SUPERX-PASSWORD' DATABASE_PORT = '5432' ... TIME_ZONE = 'Asia/Bangkok' ...
สร้างแอปพลิเกชั่นชื่อ blog
$ django-admin.py startapp blog
แก้ไข models.py เพื่อสร้าง table
$ 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 "/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 "/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 mysite.blog.models import Entry blog_dict = { 'queryset': Entry.objects.all(), 'date_field': 'pub_date', } urlpatterns = patterns('', (r'^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'^blog/?$', 'django.views.generic.date_based.archive_index', blog_dict), )
รัน syncdb ครั้งนึง เพื่อสร้าง table
$ python manage.py syncdb
...
Would you like to create one now? (yes/no): <<<--- yes
Username (Leave blank to use 'webmaster'): <<<--- [ENTER]
E-mail address: <<<--- webmaster@example.com
Password: <<<--- WEBMASTER-PASSWORD
Password (again): <<<--- WEBMASTER-PASSWORD
Superuser created successfully.
...
แก้ไข settings.py ให้มาใช้ template ของเรา
$ vi settings.py
... TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates". # Always use forward slashes, even on Windows. "/home/webmaster/django/mysite/templates" ) ...
สร้าง template หลัก ชื่อ base.html ในไดเรกทอรี่ ~/django/mysite/templates
$ mkdir templates
$ 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="/">Home</a></li> <li><a href="/blog/">Blog</a></li> <li><a href="#">Photos</a></li> <li><a href="/links/">Links</a></li> <li><a href="/portfolio/">Work</a></li> <li><a href="/colophon/">Colophon</a></li> </ul> </div> <div id="content"> {% block content %} {% endblock %} </div> </body> </html>
สร้าง template ย่อย สำหรับดู Entry archive
$ mkdir templates/blog
$ 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 %}
แก้ไข settings.py อีกครั้ง ให้รับโมดูล blog และโมดูล admin
$ vi settings.py
... INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'mysite.blog', 'django.contrib.admin', )
syncdb อีกครั้ง
$ python manage.py syncdb
เสร็จแล้ว ลองรันได้เลยด้วยคำสั่ง
$ python manage.py runserver 192.168.1.5:8000
สร้างเนื้อหา blog ได้จาก http://192.168.1.5:8000/admin
โดยล๊อกอินในชื่อ webmaster และสร้างเนื้อหาในหมวด blog
ดูเนื้อหาที่สร้างแล้วที่ http://192.168.1.5:8000/blog
ลองติดตั้ง django เพื่อใช้งานกับ apache2 บนเดเบียน
เที่ยวนี้ทำไปบันทึกไป จึงไม่มีกำหนดเสร็จครับ
งานของ admin เจ้าของเซิร์ฟเวอร์
เอา django มาก่อน
# aptitude install subversion
# svn co http://code.djangoproject.com/svn/django/trunk/
ติดตั้ง django สู่ระบบ
# cd trunk
# python setup.py install
ลบซอร์ส หากไม่ต้องการดูโค๊ดของ django
# cd ..
# rm -rf trunk
กันเหนียวให้ apache2 เปิดมอดูล env
(ส่วนใหญ่จะเปิดมาอยู่แล้วมั้ง)
# a2enmod env
ติดตั้ง mod_python
และเปิดให้ใช้งาน
# aptitude install libapache2-mod-python
# a2enmod mod_python
งานของเรา เจ้าของเว็บ
สมมุติว่า admin ติดตั้ง ให้ DocumentRoot ของ apache2 สำหรับโดเมน www.example.com อยู่ที่ไดเรกทอรี่ของเรา /home/user1/www
และเราจะให้หน้าของ django ไปอยู่ที่ http://www.example.com/dj
มาที่ไดเรกทอรี่ของเราก่อน
$ cd ~/www
เริ่มโปรเจคต์ใหม่ชื่อ dj
$ django-admin.py startproject dj
ไปที่ไดเรคทอรี่ dj และเตรียมการให้ apache2 โดยการสร้างไฟล์ .htaccess
$ cd dj
$ vi .htaccess
SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv SERVER_ADMIN webmaster@example.com SetEnv DJANGO_SETTINGS_MODULE dj.settings PythonDebug On PythonPath "['/home/user1'] + sys.path" RewriteEngine On RewriteBase /dj/
เสร็จแล้ว ดูที่หน้า www.example.com/dj ได้ดังนี้
เพิ่มเติม
สำหรับการทำงานให้เต็มรูปแบบ ต้องสร้างหน้า admin ด้วย
การที่จะทำให้หน้า admin ทำงานได้สมบูรณ์ ต้องสร้างลิงก์โยงจากทรัพยากรของซอร์สมาที่ไดเรกทอรี่รากของ apache2 ด้วย
สมมุติถ้าใช้ไพธอน 2.4 บนเดเบียน
$ cd ~/www
$ ln -sf /usr/lib/python2.4/site-packages/django/contrib/admin/media/ .
สร้างหน้า admin โดยการลบคอมเมนต์ในไฟล์ dj/urls.py
$ cd dj
$ vi urls.py
... (r'^admin/', include('django.contrib.admin.urls')), ...
แต่ถ้าหากเราจะให้เพจของ django อยู่ในหน้า www.example.com/dj เราต้องแก้ไฟล์ด้วย
... (r'^dj/admin/', include('django.contrib.admin.urls')), ...
แต่...ก่อนจะใช้งานหน้า admin ได้ เราต้องสร้างฐานข้อมูลก่อน สมมุติว่าจะใช้กับ postgresql
สร้างฐานข้อมูลไว้รองรับ ตั้งชื่อว่า djdb
$ createdb djdb
Password: >>> --- USER1_PASSWORD
ปรับตั้งไฟล์ settings.py
ให้ django รับรู้ฐานข้อมูลและให้เรียกใช้มอดูล admin
$ vi settings.py
... DATABASE_ENGINE = 'postgresql' DATABASE_NAME = 'djdb' DATABASE_USER = 'user1' DATABASE_PASSWORD = 'USER1_PASSWORD' ... TIME_ZONE = 'Asia/Bangkok' ... LANGUAGE_CODE = 'th' ... INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', )
สั่งปรับปรุงฐานข้อมูล
$ python manage.py syncdb
Creating table auth_message Creating table auth_group Creating table auth_user Creating table auth_permission Creating table django_content_type Creating table django_session Creating table django_site Creating table django_admin_log You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): >>>--- yes Username (Leave blank to use 'user1'): >>>--- ENTER E-mail address: >>>--- user1@example.com Password: >>>--- USER1_PASSWORD Password (again): >>>--- USER1_PASSWORD Superuser created successfully. Installing index for auth.Message model Installing index for auth.Permission model Installing index for admin.LogEntry model
เสร็จแล้ว ดูจาก URL:http://www.example.com/dj/admin
จะได้ดังนี้
อ้างอิง
คราวนี้ทำบล๊อกจาก Falling Bullets - Blog - WordPress Clone in 27 Seconds (Part 1 of 40)
โดย
http://www.example.com/dj
http://dj.example.com
เป็นต้น)/dj
ไปก่อน/home/user1/www
user1
รหัสผ่านคือ USER1_PASSWORD
มีสิทธิ์ในการสร้างฐานข้อมูล/home/user1/www/dj
สร้างแอพลิเคชั่นชื่อ 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
ได้ดังนี้
จากครั้งก่อน django: ใช้กับ apache2 บนเดเบียน (มีการปรับปรุงให้เนื้อหาสมบูรณ์ขึ้นในหน้าเก่าด้วย)
ตอนนี้เราจะมาสร้างแอพลิเคชั่นชื่อ "to do" จาก sitepoint.com - Django Djumpstart: Build a To-do List in 30 Minutes
โดย
http://www.example.com/dj
http://dj.example.com
เป็นต้น)/dj
ไปก่อน/home/user1/www
user1
รหัสผ่านคือ USER1_PASSWORD
มีสิทธิ์ในการสร้างฐานข้อมูล/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
ตอนนี้ยังไม่มีอะไร เพราะเรายังไม่ได้ใส่อะไรเข้าไป
ถึงตอนนี้เราสามารถใส่เนื้อหาใหม่เข้าไปได้ ผ่านทางหน้า admin
โดยต้องเพิ่ม List ก่อน ทาง URL:http://www.example.com/dj/admin/todo/list/add
ตามด้วย Item ทาง URL:http://www.example.com/dj/admin/todo/item/add
พอเข้าหน้า report
ใหม่ ก็จะเห็นรายการตามต้องการ
รายการเพิ่มเติมสำหรับ django รุ่น svn (ระหว่าง 0.96-)
$ sudo aptitude install python-docutils
$ sudo vi /usr/lib/python2.4/site-packages/django/contrib/admin/media/css/global.css
body { margin:0; padding:0; font-size:84%; font-family:"Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; color:#333; background:#fff; } /* LINKS */ a:link, a:visited { color: #5b80b2; text-decoration:none; } a:hover { color: #036; } a img { border:none; } /* GLOBAL DEFAULTS */ p, ol, ul, dl { margin:.2em 0 .8em 0; } p { padding:0; line-height:140%; } h1,h2,h3,h4,h5 { font-weight:bold; } h1 { font-size:1.4em; color:#666; padding:0 6px 0 0; margin:0 0 .2em 0; } h2 { font-size:1.3em; margin:1em 0 .5em 0; } h2.subhead { font-weight:normal;margin-top:0; } h3 { font-size:1.2em; margin:.8em 0 .3em 0; color:#666; font-weight:bold; } h4 { font-size:1.1em; margin:1em 0 .8em 0; padding-bottom:3px; } h5 { font-size:1em; margin:1.5em 0 .5em 0; color:#666; text-transform:uppercase; letter-spacing:1px; } ul li { list-style-type:square; padding:1px 0; } ul.plainlist { margin-left:0 !important; } ul.plainlist li { list-style-type:none; } li ul { margin-bottom:0; } li, dt, dd { font-size:.9em; line-height:1.2em; } dt { font-weight:bold; margin-top:4px; } dd { margin-left:0; } form { margin:0; padding:0; } fieldset { margin:0; padding:0; } blockquote { font-size:.9em; color:#777; margin-left:2px; padding-left:10px; border-left:5px solid #ddd; } code, pre { font-family:"Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; background:inherit; color:#666; font-size:.9em; } pre.literal-block { margin:10px; background:#eee; padding:6px 8px; } code strong { color:#930; } hr { clear:both; color:#eee; background-color:#eee; height:1px; border:none; margin:0; padding:0; font-size:1px; line-height:1px; } /* TEXT STYLES & MODIFIERS */ .small { font-size:.9em; } .tiny { font-size:.8em; } p.tiny { margin-top:-2px; } .mini { font-size:.7em; } p.mini { margin-top:-3px; } .help, p.help { font-size:.8em !important; color:#999; } p img, h1 img, h2 img, h3 img, h4 img, td img { vertical-align:middle; } .quiet, a.quiet:link, a.quiet:visited { color:#999 !important;font-weight:normal !important; } .quiet strong { font-weight:bold !important; } .float-right { float:right; } .float-left { float:left; } .clear { clear:both; } .align-left { text-align:left; } .align-right { text-align:right; } .example { margin:10px 0; padding:5px 10px; background:#efefef; } .nowrap { white-space:nowrap; } /* TABLES */ table { border-collapse:collapse; border-color:#ccc; } td, th { font-size:.9em; line-height:1.2em; border-bottom:1px solid #eee; vertical-align:top; padding:5px; font-family:"Lucida Grande", Verdana, Arial, sans-serif; } th { text-align:left; font-size:1em; font-weight:bold; } thead th, tfoot td { color:#666; padding:2px 5px; font-size:.9em; background:#e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x; border-left:1px solid #ddd; border-bottom:1px solid #ddd; } tfoot td { border-bottom:none; border-top:1px solid #ddd; } thead th:first-child, tfoot td:first-child { border-left:none !important; } thead th.optional { font-weight:normal !important; } fieldset table { border-right:1px solid #eee; } tr.row-label td { font-size:.7em; padding-top:2px; padding-bottom:0; border-bottom:none; color:#666; margin-top:-1px; } tr.alt { background:#f6f6f6; } .row1 { background:#EDF3FE; } .row2 { background:white; } /* SORTABLE TABLES */ thead th a:link, thead th a:visited { color:#666; display:block; } table thead th.sorted { background-position:bottom left !important; } table thead th.sorted a { padding-right:13px; } table thead th.ascending a { background:url(../img/admin/arrow-down.gif) right .4em no-repeat; } table thead th.descending a { background:url(../img/admin/arrow-up.gif) right .4em no-repeat; } /* ORDERABLE TABLES */ table.orderable tbody tr td:hover { cursor:move; } table.orderable tbody tr td:hover { cursor:move; } table.orderable tbody tr td:first-child { padding-left:14px; background-image:url(../img/admin/nav-bg-grabber.gif); background-repeat:repeat-y; } table.orderable-initalized .order-cell, body>tr>td.order-cell { display:none; } /* FORM DEFAULTS */ input, textarea, select { margin:2px 0; padding:2px 3px; vertical-align:middle; font-family:"Lucida Grande", Verdana, Arial, sans-serif; font-weight:normal; font-size:.9em; } textarea { vertical-align:top !important; } input[type=text], input[type=password], textarea, select, .vTextField { border:1px solid #ccc; } /* FORM BUTTONS */ input[type=submit], input[type=button], .submit-row input { background:white url(../img/admin/nav-bg.gif) bottom repeat-x; padding:3px; color:black; border:1px solid #bbb; border-color:#ddd #aaa #aaa #ddd; } input[type=submit]:active, input[type=button]:active { background-image:url(../img/admin/nav-bg-reverse.gif); background-position:top; } input[type=submit].default, .submit-row input.default { border:2px solid #5b80b2; background:#7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x; font-weight:bold; color:white; } input[type=submit].default:active { background-image:url(../img/admin/default-bg-reverse.gif); background-position:top; } /* MODULES */ .module { border:1px solid #ccc; margin-bottom:5px; background:white; } .module p, .module ul, .module h3, .module h4, .module dl, .module pre { padding-left:10px; padding-right:10px; } .module blockquote { margin-left:12px; } .module ul, .module ol { margin-left:1.5em; } .module h3 { margin-top:.6em; } .module h2, .module caption { margin:0; padding:2px 5px 3px 5px; font-size:.9em; text-align:left; font-weight:bold; background:#7CA0C7 url(../img/admin/default-bg.gif) top left repeat-x; color:white; } .module table { border-collapse: collapse; } /* MESSAGES & ERRORS */ ul.messagelist { padding:0 0 5px 0; margin:0; } ul.messagelist li { font-size:1em; display:block; padding:4px 5px 4px 25px; margin:0 0 3px 0; border-bottom:1px solid #ddd; color:#666; background:#ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat; } .errornote { font-size:1em !important; display:block; padding:4px 5px 4px 25px; margin:0 0 3px 0; border:1px solid red; color:red;background:#ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat; } ul.errorlist { margin:0 !important; padding:0 !important; } .errorlist li { font-size:1em !important; display:block; padding:4px 5px 4px 25px; margin:0 0 3px 0; border:1px solid red; color:white; background:red url(../img/admin/icon_alert.gif) 5px .3em no-repeat; } td ul.errorlist { margin:0 !important; padding:0 !important; } td ul.errorlist li { margin:0 !important; } .error { background:#ffc; } .error input, .error select { border:1px solid red; } div.system-message { background: #ffc; margin: 10px; padding: 6px 8px; font-size: .8em; } div.system-message p.system-message-title { padding:4px 5px 4px 25px; margin:0; color:red; background:#ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat; } .description { font-size:1em; padding:5px 0 0 12px; } /* BREADCRUMBS */ div.breadcrumbs { background:white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x; padding:2px 8px 3px 8px; font-size:.9em; color:#999; border-top:1px solid white; border-bottom:1px solid #ccc; text-align:left; } /* ACTION ICONS */ .addlink { padding-left:12px; background:url(../img/admin/icon_addlink.gif) 0 .2em no-repeat; } .changelink { padding-left:12px; background:url(../img/admin/icon_changelink.gif) 0 .2em no-repeat; } .deletelink { padding-left:12px; background:url(../img/admin/icon_deletelink.gif) 0 .25em no-repeat; } a.deletelink:link, a.deletelink:visited { color:#CC3434; } a.deletelink:hover { color:#993333; } /* OBJECT TOOLS */ .object-tools { font-size:.8em; font-weight:bold; font-family:Arial,Helvetica,sans-serif; padding-left:0; float:right; position:relative; margin-top:-2.4em; margin-bottom:-2em; } .form-row .object-tools { margin-top:5px; margin-bottom:5px; float:none; height:2em; padding-left:3.5em; } .object-tools li { display:block; float:left; background:url(../img/admin/tool-left.gif) 0 0 no-repeat; padding:0 0 0 8px; margin-left:2px; height:16px; } .object-tools li:hover { background:url(../img/admin/tool-left_over.gif) 0 0 no-repeat; } .object-tools a:link, .object-tools a:visited { display:block; float:left; color:white; padding:.1em 14px .1em 8px; height:14px; background:#999 url(../img/admin/tool-right.gif) 100% 0 no-repeat; } .object-tools a:hover, .object-tools li:hover a { background:#5b80b2 url(../img/admin/tool-right_over.gif) 100% 0 no-repeat; } .object-tools a.viewsitelink, .object-tools a.golink { background:#999 url(../img/admin/tooltag-arrowright.gif) top right no-repeat; padding-right:28px; } .object-tools a.viewsitelink:hover, .object-tools a.golink:hover { background:#5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat; } .object-tools a.viewsitelink:hover, .object-tools a.golink:hover { background:#5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat; } .object-tools a.addlink { background:#999 url(../img/admin/tooltag-add.gif) top right no-repeat; padding-right:28px; } .object-tools a.addlink:hover { background:#5b80b2 url(../img/admin/tooltag-add_over.gif) top right no-repeat; } /* OBJECT HISTORY */ table#change-history { width:100%; } table#change-history tbody th { width:16em; }
คราวหน้า ถ้าจะปรับเปลี่ยนเพิ่มเติม เพียงเปลี่ยนเฉพาะบรรทัดแรกจาก 84% ไปเป็นตัวเลขอื่นก็ปรับเฉพาะตัวนี้ตัวเดียว
เมื่อแปลงแล้วได้ภาพดังนี้