Django: form_for_model の代わりに、ModelForm Classを使うらしい version 1.0

久しぶりに、Djangoを触っていたら、form_for_model がなくなっていた。
で、代わりに、ModelForm Classが出来ていたので、そちらを使うみたいだ。
特に難しいこともなく、よりシンプルになったんじゃないかなぁ~
これを実装するために前はclassの継承とかしていたから・・・俺は凄い楽になったと思う。

Creating forms from models

from django import forms
from integra.profile.models import Profile
class ProfileForm(forms.ModelForm):
class meta:
model = Profile
fields = ('language', 'country', 'timezone', )

Python: DjangoでWebアプリケーション開発 Model Manager Version1.0版

class Attribute(models.Model):
"""
    Attribute of Task.
    """
user = models.ForeignKey(User, null=False)
name = models.CharField(max_length=100, null=False)
created = models.DateTimeField(auto_now_add=True)
timestamp = models.DateTimeField(auto_now=True)

こんなModel Classがあったとき、通常のModelの操作はこんな感じになる。
例えば、新しくAttributeオブジェクトを作成して、データベースにセーブする場合には、外部キーでUserオブジェクトに依存しているので、それも作成することなる。
普通に書くとこんな感じ。

from django.contrib.auth.models import User

user = User(username="higehige", password="hagehage")
user.save()

from integra.task.models import Attribute

attribute = Attribute(user=user, name="hage flash")
attribute.save()
search_result = Attribute.objects.filter(name__contains="hage")
search_result

こんな感じに、単純なSQLは標準のobjectsオブジェクトを使うだけで出来る。
ちなみに、name__containsは

SELECT * FROM integra_attribute WHERE name LIKE '%hage%';

と同じ意味になる。
更に、色々紹介すると、日付の絞込みはこうなる。

Attribute.objects.filter(user=user, created__range=('2008-08-25', '2008-09-01'))
[]

ここで注意したいのが、結果を見れば直ぐに分かるけれど、filter戻りはlist型という事だ。
一つだけ取得したい場合には、getを使う。中止しなければならいのは、戻りが一つになるような条件(ユニークキーで絞り込む)などをしないと、MultipleObjectsReturned という例外が返ってくる。
更に、LIMITなどを使う場合には、Pythonのsliceが使える。

attribute = Attribute(user=user, name="Hige jorijori")
attribute.save()

Attribute.objects.filter(user=user, created__range=('2008-08-25', '2008-09-01')).order_by('created')[:1]
[]

Attribute.objects.filter(user=user, created__range=('2008-08-25', '2008-09-01')).order_by('-created')[:1]
[]

さて、こんな感じに通常のObjectで事足りる場合が多いけれど、他のModelとの連携が入ってきた時、標準のObjectでは足りない場合があるし、色々な方法でSELECTを行う場合が多いので、それを毎回Viewに書くのもおかしい。という訳で、そういうコードはどこに書けばいいのか?という疑問を解決すると共に、非常に使いやすいのが
django.db.models.Manager Classだ。
使い方は簡単。
django.db.models.Manager Classを継承したClassを作成。
更に、既にあるModel Classにobjects = Manager() という形で代入する。
これで、既存のobjectsも使える上に、自前のobjectsも使える。

from django.db import models
class AttributeManager(models.Manager):
"""
    Manager Class for Attribute.
    """
  def create_attribute(self, user, name):
    try:
      attribute = Attribute.objects.get(user=user, name=name)
    except Attribute.DoesNotExist:
      attribute = Attribute(user=user, name=name)
      attribute.save()
      return attribute

  def add_attribute(self, user, task, name):
    attribute = self.get_or_create(user=user, name=name)[0]
    task.attribute.add(attribute)
    task.save()
    return attribute

  def delete_attribute(self, user, task, id):
    try:
      attribute = self.get(user=user, id=id)
    except Attribute.DoesNotExist:
      pass
    else:
      task.attribute.remove(attribute)
      # check referred from task
      if attribute.task_set.count() == 0:
        attribute.delete()

class Attribute(models.Model):
"""
    Attribute of Task.
    """
  user = models.ForeignKey(User, null=False)
  name = models.CharField(max_length=100, null=False)
  created = models.DateTimeField(auto_now_add=True)
  timestamp = models.DateTimeField(auto_now=True)
  objects = AttributeManager()

add_attributeメソッドなんかは、task Modelとの関連付けを登録と同時に行っているのが分かるかと思う。
こんな感じに、自分が実装したModel Classに対しても関連付けを一度に行える事が出来るし
getメソッドなどで取得したAttribute objectには既にこれらのメソッドが含まれているので、非常に使いやすい。
当然shell上でもtabで補完してくれたりするから、他人が作ったModel objectを扱う時も便利だ。

from integra.task.models import Task

task = Task(user=user, description="hage task")
task.save()
Attribute.objects.add_attribute(user=user, task=task, name="hage task attribute")

Attribute.objects.filter(user=user).order_by('-created')[:1]
[]

最後の、外部キーを参照する時はこうなる。
結構凶悪で、かなり追いかけていけます・・・きちんとJOINもしてくれているので、高速なのがいいね。

Attribute.objects.filter(task__user=user)
Attribute.objects.filter(task__user__username__contains='hige')
[]

Python: DjangoでWebアプリケーション開発 Version1.0版 アプリケーション作成とModelの基本

Djangoが最近じわじわと国内でも注目を浴びつつあるみたいなので、ここらで自分も、解説記事を書いて、オープンソースにちょっとは協力してみようかなと。
とかいうのは建前で、ただの復習だねw
最初に、Djangoは、MVCなフレームワークではんく、MTVというスタイルを取っている。
なので、MVCなフレームワークに慣れている人からすると、ちょっとテンプレートに色々書きすぎじゃない?
という意見であるかもしれない。が、プログラマーが、テンプレートをいじらないWeb開発なんて、そうそうないでしょう?と思う。

開発環境は
openSUSE11.0
Python 2.5
Django revision 8370(Version1.0の開発バージョン)
MySQL 5.0
まず、DjangoのライブラリをPythonのPATH内に入れないといけない。

自分の場合は

svn co http://code.djangoproject.com/svn/django/trunk/ /usr/local/src/django-trunk/
sudo ln -s /usr/local/src/django-trunk/django/ [PYTHON_LIB_PATH]

といった感じになる。PYTHON_LIB_PATHの部分は環境によって違うので

python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"

を実行して調べて指定するのが通常。

これで、Djangoのインストールは完了。PHPのZendFrameworkもそうだけれど、PATHを通すだけで使えるフレームワークって最近多くていいよね。
アンインストールしたい時は、リンボリックリンクを消せばいいだけ。
次に、Djangoのコマンドを自分の環境に入れる。

ln -s /usr/local/src/django-trunk/django/bin/django-admin.py ~/bin/

これは、使いたいコマンドを一個ずつシンボリックリンク張るしかないかな。
django-admin.pyしか使わないので、今回はこれだけで。
さて、そしたら、プロジェクトを作りたい場所に移動して、早速Djangoアプリケーションを作成。
django-admin startproject [PROJECT_NAME]
と実行する。今回は、PROJECT_NAMEは integra と入れる事にする。
そうすると、プロジェクトの雛形が出来上がる。
さて、次に使うデータベースはMySQLなので、それ用の設定をする。

cd integra
vim setting.py を実行して編集。ちなみに、エディタはなんだっていいので、vimである必要性はどこにもない。
設定ファイルもPythonのコードで書かれている。一応、ReadOnlyとするために、tuple形式で書くのが通常。
今回は、MySQLを使うので、こんな感じにしてみる。

DATABASE_ENGINE = 'mysql'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'integra'             # Or path to database file if using sqlite3.
DATABASE_USER = 'integra'             # Not used with sqlite3.
DATABASE_PASSWORD = 'integra'         # Not used with sqlite3.
DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.

MySQLのユーザーを設定ファイルに合わせて作成しよう。
CREATE DATABASE integra CHARACTER SET utf8;
GRANT ALL ON integra.* TO integra@localhost IDENTIFIED BY ‘integra’;
といった感じ。
自分は文字化けが嫌いなので、UTF-8を推奨するのでMySQLの設定ファイルにも以下を追加している。
default-character-set=utf8
skip-character-set-client-handshake
さて、ここまで来たら、早速、Djangoのコードを書いてみよう。
Djangoはプロジェクトの下にいくつ物アプリケーションが存在している。
ちなみに、Djangoプロジェクトで配布されているプロジェクトは、contributeという形で存在している。
なので、次にアプリケーションを作成。
python magage.py startapp task
とすると、integra/task というディレクトリが作成され、その下に初期ファイルが置かれる。
ここで忘れていけないのが、作ったアプリケーションはプロジェクトに登録しないといけない。という事だ。
vim settings.py
をして、設定ファイルを編集する。
ついでに、Djangoが持ってるアプリケーションも読み込んでおく。

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'integra.task',
)

Djangoでアプリケーション開発のベースとなるのが、Model部分なので、まず最初にmodels.pyを編集する。
何故かというと、Djangoでは、modelspy内のclassを元にデータベーステーブルを作成する仕組みになっているからだ。
RDBMSを扱うシステム開発において、データベース設計がまず最初に来るので、当然最初に作成する。
いきなり量があるけれど、まあこんな感じで作成してみる。

from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
class Attribute(models.Model):
"""
    Attribute of Task.
    """
  user = models.ForeignKey(User, null=False)
  name = models.CharField(max_length=100, null=False)
  created = models.DateTimeField(auto_now_add=True)
  timestamp = models.DateTimeField(auto_now=True)

  def __unicode__(self):
    return self.name

class Task(models.Model):
"""
    Task Model Class
    Save task informations. and view it some formats.
    """
  user = models.ForeignKey(User, null=False)
  description = models.TextField(null=False)
  start_date = models.DateTimeField(auto_now_add=True)
  end_date = models.DateTimeField(null=True)
  attribute = models.ManyToManyField(Attribute, verbose_name=_("Attribute"))
  created = models.DateTimeField(auto_now_add=True)
  timestamp = models.DateTimeField(auto_now=True)
  enabled = models.BooleanField(default=True)

  def __unicode__(self):
    return self.user.username + str(self.start_date) + unicode(self.attribute)

class Url(models.Model):
"""
    URL Model Class.
    This model have url associated with Task Model.
    """
  url = models.URLField()
  task = models.ForeignKey(Task)
  created = models.DateTimeField(auto_now_add=True)
  timestamp = models.DateTimeField(auto_now=True)
  enabled = models.BooleanField(default=True)

  def __unicode__(self):
    return self.url

さて、一つずつ解説していくと。
Djangoで使うModel Classを作成するには、Djangoライブラリの、django.db.models.Modelを継承する必要性がある。
更に、ユーザModelに関しては標準でDjangoが持っている django.contrib.auth.models.User Classを使う事にする。
寄って、以下のimport文が存在する。

from django.db import models
from django.contrib.auth.models import User

さて、次に、各テーブルの作成を行っている訳だが
Attribute Classから解説していこう。
Attribute Classが一つのTABLE, PropertyをCOLUMNとして捕らえる事が出来る。
データベースを操作するためのオブジェクトをプログラマーサイドで作ってしまい
それを元に、あらゆるオブジェクト、データベース設計を行うという発想なのだと思う。

user = models.ForeignKey(User, null=False)

の部分は、先ほど説明した、Djangoが標準で持っているユーザModelと外部キー接続をしている事を示す。
ここは、1:1ではなく、1:多な関係なので、models.ForeignKey Classを作成している。
第一引数には、外部キーでの接続対象となるModel名を入れる。
null=False は、キーワード引数として、オプションを設定している。
このオプションに関しては、unique=Trueなど、COLUMNの属性として指定出来る物は大抵指定できる。
詳しくは公式サイトに書いてある。
http://www.djangoproject.com/documentation/model-api/

name = models.CharField(max_length=100, null=False)

に関しては特に難しい事ないと思うが、CACHAR型のCOLUMNを作成する。

name = models.CharField(max_length=100, null=False)

は、どちらもDjangoでは同じ様に見えるが、オプションによって、MySQL内で作成されるCOLUMNが変わってくる。
createdは、MySQLのDATETIME型を作成し、アプリケーション側でRECORD作成時に一度だけ値を入れる操作を行っている。
timestampは、MySQLのTIMESTAMP型COLUMNを作成し、MySQL側でRECORDが更新される度に日付も更新している。
さて、ここで気になるのが

def __unicode__(self):
  return self.user.username + str(self.start_date) + unicode(self.attribute)

の部分だが、これは、Pythonの機能を利用した表示用Methodだ。
このオブジェクトをprintなどによって表示させようとした時に、呼び出されるMethod。
元々は、__str__だったのだが、UNICODEに対応する時に、__unicode__に変わったので、最近ではこちらを採用するほうが正しい。
ただ、たまに未対応のソースコードもあるので、そういう時は、str(object)として__str__を呼び出してあげるといい。
ちなみに、__unicode__もunicode(object)で呼び出す。
更にちなみに、UNICODE文字列のリテラルはPython2.5では、u”Hello UNICODE in the Python!!”として記述する。
さて、残りのTask, Url Classに関しても同じ様な構成が続く。
ManyToMany Classが一つ見受けられるが、特にDjangoに限ったことではなく、多:多の関係なんてどこにでもあるのだし、解説は要らないと思うので割愛。
さて、これを書いただけではまだ、TABLEは作成されていない。
そこで、DjangoにModelを元にTABLEを作成してもらおう。
python manage.py syncdb
これで、一気にTABLEが作成される。
もし、どういうSQLが流されているのか確認したい場合には
python manage.py sqlall task
とすると、表示される。
こんな感じ。

BEGIN;
CREATE TABLE `task_attribute` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id` integer NOT NULL,
`name` varchar(100) NOT NULL,
`created` datetime NOT NULL,
`timestamp` datetime NOT NULL
)
;
ALTER TABLE `task_attribute` ADD CONSTRAINT user_id_refs_id_4ecaf348 FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
CREATE TABLE `task_task` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id` integer NOT NULL,
`description` longtext NOT NULL,
`start_date` datetime NOT NULL,
`end_date` datetime NULL,
`created` datetime NOT NULL,
`timestamp` datetime NOT NULL,
`enabled` bool NOT NULL
)
;
ALTER TABLE `task_task` ADD CONSTRAINT user_id_refs_id_19df57a0 FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
CREATE TABLE `task_url` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`url` varchar(200) NOT NULL,
`task_id` integer NOT NULL,
`created` datetime NOT NULL,
`timestamp` datetime NOT NULL,
`enabled` bool NOT NULL
)
;
ALTER TABLE `task_url` ADD CONSTRAINT task_id_refs_id_3019f8a6 FOREIGN KEY (`task_id`) REFERENCES `task_task` (`id`);
CREATE TABLE `task_task_attribute` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`task_id` integer NOT NULL,
`attribute_id` integer NOT NULL,
UNIQUE (`task_id`, `attribute_id`)
)
;
ALTER TABLE `task_task_attribute` ADD CONSTRAINT task_id_refs_id_737b532c FOREIGN KEY (`task_id`) REFERENCES `task_task` (`id`);
ALTER TABLE `task_task_attribute` ADD CONSTRAINT attribute_id_refs_id_4e1badcc FOREIGN KEY (`attribute_id`) REFERENCES `task_attribute` (`id`);
CREATE INDEX `task_attribute_user_id` ON `task_attribute` (`user_id`);
CREATE INDEX `task_task_user_id` ON `task_task` (`user_id`);
CREATE INDEX `task_url_task_id` ON `task_url` (`task_id`);
COMMIT;

さて、今回は、Modelの解説までで終わらせたいので、Viewを使わずにModelを扱う
Pythonらしいやり方を紹介。
python manage.py shell
と実行すると、対話型コマンドプロンプトへと入る事が出来る。
ここで

from django.contrib.auth.models import User
from integra.task.models import Task, Attribute, Url

とすれば、後はPythonらしく色々試す事が出来る。
例えば

>>> user = User(username="hige", password="higehige")
>>> user.save()
>>> user.id
2L

となる。
Modelオブジェクトの扱いについては次回にでも詳しく解説しようかなと思う。
ただ、Pythonのこういうところが開発効率を非常に高める部分なので、こういう事が可能だというのは、最初にお知らせしておこうかなと。
ま、正直な話、Python, Django使いに取っては当たり前の事なんだけれどね。

Djang : MySQL get_or_create() ではまった

最近、はまってばかりやな・・・w
Django SVN version 0.97 (revision 7925)
MySQL 5.0.31
Python 2.5
という環境です。
さて、Django + MySQLで遊んでいて、ちょっとはまったのが get_or_create だ。
これ、createしたときは要素一個しか返さないのに、getしたときは何故かtuple型で返ってくる。
なので、一個だけ取得すること前提で組むときは
get_or_create()[0]
とやって回避した。
参考にしたサイト。サイトの人のは作成じゃなくて、取得だけれどね。
http://yuji.wordpress.com/2008/05/14/djangomysql-operand-should-contain-1-columns/
ちなみに、get_or_create()で、UNIQUE KEYがかかっているColumnを指定すると、DoesNotExistを激しく投げてくる・・・分けわからん!!w
UNIQUE KEY がかかってるColumnは全て引数で指定しているんだけれどなぁ・・・
shellで何度か試してみたら、DoesNotExist投げる時と投げない時があった。
普通になぞ過ぎる。

user = models.ForeignKey(User, null=False)
name = models.CharField(max_length=50, null=False)

の部分が、

user = models.ForeignKey(User, null=False, unique=True)
name = models.CharField(max_length=50, null=False, unique=True)

だと、AttributeManager Class の

def add_attribute(self, user, task, attribute_name):
  attribute = self.get_or_create(user=user, name=attribute_name)[0]
  task.attribute.add(attribute)
  task.save()
  return attribute

attribute = self.get_or_create(user=user, name=attribute_name)[0]

の行で、何故かAttribute.DoesNotExistがraiseされちゃうんだよねぇ

Djangoの公式を見ても、これに関しては余り書いてなかった。
ぐぐっても、情報が余り出てこない。っていうか、内部で except Attribute.DoesNotExist して、createのほうを走らせてくれるはずなんだけれど・・・w
まあ、バグなのかもしれない。
結局、unique=True を外して実装しました。
アプリケーション側で、UNIQUEになるようにすればいいかなぁ~と。
問題があるようだったら、後日いじってみようかな。

openSUSE 10.3 に Navicat(正規版)を入れてみた。

前回は、Liteの話だったんだけれど、今回は正規版を入れて操作してみることにした。
前回 >>> openSUSE 10.3 に Navicat-Lite を入れてみた。
なんで Navicat 使ってるの?っていうと、MySQLの公式サイトで配られている専用のGUIツールが使えないからだ・・・ShiftJISとかEUC_JPとか関係なく、マルチバイト文字にかなり弱いので、非常に怖い。特に、Webシステムなんて、お客さんの大事なデータをデータベースに入れる訳だし、仕事であれを使うのはきついんだよね。
ということで、職場の人に教えてもらったNavicatを使っていたんだけれど、Linux版もあるということで、お、それはいいね!!普段Linuxで開発しているし、ありがたい。ってことで入れてみている訳だったり。
ってことで、製品版のインストールもメモしました。
ま、殆ど lite と変わらないけれどね〜
wiki: Navicat
とりあえず、製品版という事で色々出きるようになっています。
Liteでもそうなんだけれど、各操作をファイルとして記録しておいて、後で再現出来るのも大きいかも・・・
Data Synchronization はそのまんまデータベース単位で同期取ってくれます。
これはあくまでも同期なので、片方にしか無いテーブルに関しては何もしないみたい。
それでも、データ同期をボタンぽちぽちでやってくれて、一応Transaction使ってのPreviewもあったりするので
各開発サーバ間で、テストデータをやりとりしたり、ステージングサーバにデータを入れるのに良いかも。
これはまあ、ダンプでコピーしまくるよりも、簡単に共有するためのツールかな。
で、もっと面白いのが Structure Synchronization.
これは,データベース同士の構造を合わせてくれるって意味そのまんまで、足りないテーブルを追加したり、多いテーブルを削除したりまでしてくれる。
こんな感じで情報もその場で見られるのと、SQL の選択も出来たりするので、中々芸が細かいなぁ・・・
screenshot
で、これの何が良いのかっていうと、Django の Database の部分ってちょっと欠点があって
運用中のサーバに対して、データ構造の変更を行う場合、 models.py を変更しただけじゃ、データベースの構造までは買えてくれないんだよね。
models.py の Model Class を変えた上で、ALTER文を自分で書いて走らせないといけない。
詳しくはこちらをどうぞ。
The Django Book: Making Changes to a Database Schema
で、こういう場合一々SQLを自分で書くのはミスもあるだろうし、手間もかかるし結構大変。
なので、Navicat のこの機能を使うと中々便利なのです!!
どう使うかというと、新しいデータベースを作って一旦Django の接続先をそちらへと変更する。
その後に、python manage.py syncdb を行い、真新しいスキーマーを作成してもらう。
その後、新しいデータベーススキーマーから、古いデータベーススキーマーへ、 Structure Synchronization を行えば良いのだ。
なんて便利なんだろう!!
って、最後は Django の話になっちゃったけれど、Django + MySQL の開発に Navicat は中々良いかもしれないぜ(・・
っていう話でした。

Djangoをやろう!! django.contrib.admin基礎編

今思ったんだけれどさ、みんな検索する時は、 “django 入門” とかで検索する訳じゃん。Pythonもさ。
でも、自分のエントリーって全部やろう!!なんだよね。ま、いいか?w
硬いのは嫌いだしね~
ってことで、Djangoのcontributeの一つのadminに付いてやります。これはちょっとしたCMSちっくです(・・
最初に、
INSTALLED_APPS = (
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.sites’,
‘django.contrib.admin’,
‘redhawk.task’,
)
のように、’django.contrib.admin’を追加します。
次に、
python manage.py syncdb
を実行。admin権限のユーザーを作成するか訊いてくるので、作成します。
次に、redhawk.task.modelsにこんなことをしてみます。
python manage.py 192.168.11.40:8000
でサーバ起動。IPアドレスとポート番号は自分の環境にあわせて選んでください。
この場合、アクセスするURLは、

http://192.168.11.40:8000/admin/

になります。

上の様な画面が出るはずなので、ぐりぐり弄くりまわしてみましょう。
で、色々物足りないと思うので、こんな物を追加してみる。
models.py
公式ドキュメントや、本では
def __str__(self)
となっているかもしれないが、最近のバージョンでは
def __unicode__(self)
が推奨となっている。
まあ、どの言語にもよくあることで、__str__はasciiで__unicode__はユニコード企画に変換してくれる訳ですよ。
datetimeに__unicode__がないのは、__str__だけで事足りるからかな?
Rubyは日本人が作っただけあって例外なのかな?使ったことないから分からないけれど、そう聞いたことがある。
さて、これをすると、Objectが、printに渡されたときに、自動的に__unicode__の戻り値が表示されるようになるわけだ。
前のバージョンのままでは、User Objectと表示されていただろうが、これでUser.nameが表示されるので誰だか分かる。
更に、admin画面をカスタマイズ!!こんな事をしてみよう。

更に色々カスタマイズできるけれど、これまたアプリを作りながら見ていったほうが良いと思うので
サンプルアプリつくりながら色々出しておこうかなと。