Todoアプリケーションの作成

(先日)Djangoの導入とプロジェクトの作成まで行ったので、簡単なTODOアプリケーション(のとりあえず、一覧兼検索ページまで)実装してみます。(開発のプロが教える標準Django完全解説の前半のチュートリアルを参考にしました)。

管理スクリプトよりアプリケーションを生成
#./manage.py startapp todo

プロジェクトフォルダで上記のように実行すると、その下層にアプリケーションのフォルダが作成されます。
アプリケーションのフォルダには、モデル(models.py),ビュー(views.py)のテンプレートが生成されています。

settings.pyへのアプリケーション登録
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'djsite.todo',

)

モデルの生成

modelds.pyにモデルの定義を行います。今回はTodo件名を格納するモデルとそれから外部参照されるカテゴリーと優先度のモデルを実装しました。

 # -*- coding: utf-8 -*-
from django.db import models

class Category(models.Model):
	name = models.CharField(max_length=64, unique=True)

class Priority(models.Model):
	name = models.CharField(max_length=64, unique=True)

class Todo(models.Model):
	title = models.CharField(max_length=200)
	description = models.TextField(blank=True, max_length=4000)
	datetime_created = models.DateTimeField(auto_now_add=True)
	is_finished = models.BooleanField(default=False)
	priority=models.ForeignKey(Priority)
	category=models.ForeignKey(Category)

これで管理スクリプトでDB同期(syncdb)を実行します。

#python ./manage.py syncdb

この結果データベース上に対応するテーブルが生成されます。

次の管理スクリプトで対話shellを実行して、いくつかデータを登録してみます。

$./manage.py  shell
Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
>>> 
>>> from djsite.todo.models import Category
>>> from djsite.todo.models import Priority
>>> c = Category()
>>> c.name = 'Private'
>>> c.save()
>>> c = Category()
>>> c.name = 'Public'
>>> c.save()
>>> c = Category()
>>> c.name = 'Private'
>>> c.save()
>>> c = Category()
>>> c.name = 'Public'
>>> c.save()
>>> p = Priority()
>>> p.name = u'now'
>>> p.save()
>>> p = Priority()
>>> p.name = u'soon'
>>> p.save()
>>> p = Priority()
>>> p.name = u'later'
>>> p.save()
>>> from djsite.todo.models import Todo
>>> t = Todo()
>>> t.title = u'dinner'
>>> t.description = u'Dinner with moomin'
>>> t.category = 1
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Library/Python/2.5/site-packages/django/db/models/fields/related.py", line 264, in __set__
    self.field.name, self.field.rel.to._meta.object_name))
ValueError: Cannot assign "1": "Todo.category" must be a "Category" instance.
>>> t.category_id = 1
>>> t.priority_id = 2
>>> t.save
<bound method Todo.save of <Todo: Todo object>>
>>> t.save()

それから登録したデータをいくつか検索してみます。

>>> a = Category.objects.all()
>>> a
[<Category: Category object>, <Category: Category object>]
>>> a[0].name
u'Private'
>>> a[1].name
u'Public'
>>> 
>> t = Todo.objects.get(id=1)
>>> t.title
u'launch'

検索フォームの実装

# -*- coding: utf-8 -*-

from django import  forms
from djsite.todo.models import Category

class TodoSearchForm(forms.Form):
	class CategoryModelChoiceField(forms.ModelChoiceField):
	    def label_from_instance(self, obj):
	        return obj.name
	title =  forms.CharField(max_length=100, label=u'タイトル', required=False)
	category = CategoryModelChoiceField(Category.objects.all(),
						required=False,label=u'カテゴリー')
	include_finished = forms.BooleanField(required=False, 
						label=u'完了分も含める')

Viewの生成

from django.http import HttpResponse
from django.shortcuts import render_to_response
from djsite.todo.models import Todo
from djsite.todo.forms import TodoSearchForm
# Create your views here.
def show_list(request):
	"""docstring for show_list"""
	form = TodoSearchForm(request.GET)
	queryset = Todo.objects.all()
	if form.is_valid():
		cleaned = form.clean()
		title = cleaned['title']
		category = cleaned['category']
		finished = cleaned['include_finished']
		if category:
			queryset = queryset.filter(category=category)
		if title:
			queryset = queryset.filter(title=title)
		if finished:
			queryset = queryset.filter(is_finished=True)
		
	return render_to_response('list_todo.html',
		dict(todos=queryset,form=form))
    1. モデルのobjects.all()で全件を返すための検索セットが返されますが、それに対してfilterメソッドをかけていき条件の絞り込みを行います。
    2. 送信パラメータのコレクションを指定してフォームを生成します。そのフォームのインスタンスにclean()メソッドをかけるとフォームの値のディクショナリが返されます。
    3. テンプレートファイル名とテンプレートに渡す変数のディクショナリを引数の指定してrender_to_responseメソッドを実行。クライアントへの送信、表示を行います。

Urlパターンの追加

プロジェクトのurls.pyにurlパターンを定義します。

from django.conf.urls.defaults import *


urlpatterns = patterns('djsite.todo.views',
	(r'^list$','show_list'),
    # Example:
    # (r'^djsite/', include('djsite.foo.urls')),

    # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
    # to INSTALLED_APPS to enable admin documentation:
    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    # (r'^admin/(.*)', admin.site.root),
)
    1. pattersの最初のパラメータ'djsite.todo.views'がそれ以降のパターンの対象とするアプリケーションの指定です。
    2. pattersの2つ目以降のパラメータはurlパターンとViewのメソッドの組み合わせです。今回の(r'^list$','show_list')は「http://<ホスト>/list」とリクエストされた場合,views.pyに定義されたshow_listが実行されるよう定義しました。

テンプレート

まず、テンプレートファイルをおくディレクトリをプロジェクトのsettings.pyに定義します。

TEMPLATE_DIRS = (
	'/Users/nyaago/workspace/djsite/todo/templates'
    # 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.
)

表示するテンプレートを作成。settings.pyのTEMPLATE_DIRSで指定した場所に、views.pyのrender_to_responseの呼び出しパラメータで指定した名前で作成します。

<html>
<head>
</head>
<body>
	<form action="./list" method="GET">
		<table>{{form}}</table>
		<input type="submit">
	</form>
	<table>
	<tr>
		<td>タイトル
		</td>
		<td>内容
		</td>
		<td>カテゴリ
		</td>
		<td>優先度
		</td>
		<td>日付
		</td>
	</tr>
    {% for todo in todos %}
	<td>{{todo.title}}
	</td>
	<td>{{todo.description}}
	</td>
	<td>{{todo.category.name}}
	</td>
	<td>{{todo.priority.name}}
	</td>
	<td>{{todo.datetime_created|date:"Y年m月d日 H時i分"}}
	</td>
    {% endfor %}
	</table>
</body>
	
</html>

これで、とりあえず一覧表示されました。データも1件入れただけなので、おもしろくもなんともないですが。。

TODO

    1. ユーザ認証と認証ユーザで絞りこんだ検索
    2. TODOの登録、編集機能の実装