[Django] 日本ひげ男協会のサイトを作成する。その1

趣旨

イグ・ノーベル賞 大真面目で奇妙キテレツな研究に拍手!

イグ・ノーベル賞 大真面目で奇妙キテレツな研究に拍手!

上記の本によると、イグノーベル賞の受賞式には「国際ひげ男組合」なる組織が入場行進したそうです。ひるがえって日本をみると、日本にはひげ男が相互に助け合う組織がありません。そこで、日本全国のひげ男が集まり、ひげ男の社会的地位の向上を目指す組織「日本ひげ男協会」(Japan Unshaven Men Association. 略してJUMA, ジューマ)を設立します。

その最初の活動として、ひげ男が集まれるウェブサイトを作成します。

実装方法

実装方法は、本当は仕様を決めてから決めるものなのですが、今回は私がDjangoの練習をしたいので、Djangoで実装します。

ユースケース

まず、どのような利用者がどのようにシステムを使うか、想定されるストーリーを定め、それを元にユースケースを作ることで、システムに必要な機能を整理します。

アクターですが、これは匿名ユーザ(ログインしていないユーザ)とログインユーザの2種類に分けられます。匿名ユーザもログインユーザも、協会に登録されているひげ男のプロフィールを閲覧できるようにします。ひげを生やしている匿名ユーザが協会を気に入れば、会員登録します。

会員登録されている匿名ユーザは、ログインしたらログインユーザになります。ログインユーザは、匿名ユーザと同じように会員のプロフィールが閲覧できるほか、自分のプロフィールの変更を行うことができます。また、退会することができます。

以下に、ユースケースをまとめます。

アクター アクション
匿名ユーザ 会員プロフィールを閲覧する。
匿名ユーザ 会員登録する。
匿名ユーザ ログインする。
ログインユーザ 会員プロフィールを閲覧する。
ログインユーザ 自分の情報を編集する。
ログインユーザ 退会する。
ログインユーザ ログアウトする。

モデル

このシステムに必要なモデルは、ひげ男を表すUserクラスだけです。Userクラスには、ユーザ名の他に、簡単な自己紹介、自分のひげの写真を属性として持たせます。モデルを記述する具体的な方法については、後述します。

画面

本来の開発であれば、このあたりで画面設計を行わなければならないのですが、面倒なのでとばします。作りながら、適当に決めることにします。

プロジェクト生成

Djangoは、django-admin.py startproject <プロジェクト名>コマンドで、プロジェクトの基本的な枠組みを生成します。

$ django-admin.py startproject juma                 [~/projects]

標準出力と標準エラー出力は何も出力されません。成功したら、jumaという名前のディレクトリが生成されているはずです。

データベースの設定

ここで、データベースの設定をおこないます。編集するファイルは、プロジェクトのトップディレクトリにあるsettings.pyです。今回はSQLite3を使用するので、以下のようにしました。

$ diff -u settings.py.orig settings.py
--- settings.py.orig    2007-01-20 04:09:11.408538500 +0900
+++ settings.py 2007-01-20 03:08:25.980713500 +0900
@@ -9,8 +9,9 @@  

 MANAGERS = ADMINS

-DATABASE_ENGINE = ''           # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql
'.
-DATABASE_NAME = ''             # Or path to database file if using sqlite3.
+DATABASE_ENGINE = 'sqlite3'    # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql
'.
+DATABASE_NAME = '/home/tom/projects/juma/data.sqlite'
+                               # Or path to database file if using sqlite3.
 DATABASE_USER = ''             # Not used with sqlite3.
 DATABASE_PASSWORD = ''         # Not used with sqlite3.
 DATABASE_HOST = ''             # Set to empty string for localhost. Not used wi
th sqlite3.
@@ -29,7 +30,7 @@

 # Absolute path to the directory that holds media.
 # Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = '' 
+MEDIA_ROOT = '/home/tom/projects/juma/media/'
 # URL that handles the media served from MEDIA_ROOT.
 # Example: "http://media.lawrence.com"

 # URL that handles the media served from MEDIA_ROOT.
 # Example: "http://media.lawrence.com"
@@ -41,7 +42,7 @@
 ADMIN_MEDIA_PREFIX = '/media/'

 # Make this unique, and don't share it with anybody.
-SECRET_KEY = '&v&s#csgi71xkcv)aea7+(-tcuwb!)l03k1^eyp&#tzaeera)u'
+SECRET_KEY = 'c54m_xb764%o#i=fe-sbjc*y(n-q5c)f84!)#ar!j0w##(z-0#'

 # List of callables that know how to import templates from various sources.
 TEMPLATE_LOADERS = (
@@ -69,4 +70,8 @@
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.sites',
+       'juma.web',
+    'django.contrib.admin',
 )
+
+# vim: tabstop=4 shiftwidth=4 expandtab 

データベースに関係がない変更も含まれていますが、後ほど説明します。

アプリケーション生成

Djangoは「プロジェクト」と「アプリケーション」という言葉を、通常とは違う風に定義しています。Djangoでは、「プロジェクト」とは、「アプリケーション」の集まりです。よって、プロジェクトだけ作成しても、動作しません。動作させるためには、アプリケーションを生成する必要があります。ここでは、そのアプリケーションを生成します。

jumaという名前はプロジェクトで使ってしまったので、これから作るアプリケーションの名前はwebにします。

アプリケーションの生成は、以下のようにするだけです。

$ python manage.py startapp web                [~/projects/juma]

標準出力にも標準エラー出力にも何も出力されません。プロジェクトのトップディレクトリの下には、webという名前のディレクトリが生成されているはずです。

また、アプリケーションを生成したら、settings.pyの最後に、

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'juma.web', # <- これ
    'django.contrib.admin',
)

を追加してください。これで、jumaプロジェクトにjuma.webアプリケーションの存在を知らせることができます。

モデルの実装

webディレクトリの下には、

  1. __init__.py
  2. models.py
  3. views.py

の3つのファイルがあり、モデルはこのうちのmodels.pyに記述します。モデルは、以下のように記述します。

from django.db import models

class User(models.Model):
    class Admin:
        pass

    name = models.CharField(maxlength=1024)
    introduction = models.CharField(maxlength=1024)
    image = models.ImageField(upload_to='image')

    def __str__(self):
        return 'name="%s", introduction="%s"' % (self.name, self.introduction)

# vim: tabstop=4 shiftwidth=4 expandtab

クラス定義の中ほどにある、nameとintroduction, imageが、Userクラスの属性になります。nameとintroductionについては、ソースコードから想像がつくと思います。属性の値は文字列型で、最大長が設定されています。image属性のmodels.ImageFieldは、画像を扱います。upload_toという引数は、アップロードされた画像の保存先のディレクトリを表します。厳密には、settings.pyのMEDIA_ROOTで設定したディレクトリの下にupload_toで指定したディレクトリが作成され、画像はそのディレクトリに保存されます。

内部クラスAdminの記述は、Djangoが自動で生成する管理者画面で管理対象となることを宣言する文です(後述します)。

ここまできたら、実際のデータベースを生成します。以下のコマンドを実行します。管理者を作るか質問されますが、yesと答えます。あとの質問は、適当に答えます。

$ python manage.py syncdb                      [~/projects/juma]
Creating table auth_message
Creating table auth_group
Creating table auth_user
Creating table auth_permission
Creating many-to-many tables for Group model
Creating many-to-many tables for User model
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table web_user
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 'tom'): tom
E-mail address: tom@nekomimists.ddo.jp
Password:
Password (again):
Superuser created successfully.
Adding permission 'message | Can add message'
Adding permission 'message | Can change message'
Adding permission 'message | Can delete message'
Adding permission 'group | Can add group'
Adding permission 'group | Can change group'
Adding permission 'group | Can delete group'
Adding permission 'user | Can add user'
Adding permission 'user | Can change user'
Adding permission 'user | Can delete user'
Adding permission 'permission | Can add permission'
Adding permission 'permission | Can change permission'
Adding permission 'permission | Can delete permission'
Adding permission 'content type | Can add content type'
Adding permission 'content type | Can change content type'
Adding permission 'content type | Can delete content type'
Adding permission 'session | Can add session'
Adding permission 'session | Can change session'
Adding permission 'session | Can delete session'
Creating example.com Site object
Adding permission 'site | Can add site'
Adding permission 'site | Can change site'
Adding permission 'site | Can delete site'
Adding permission 'user | Can add user'
Adding permission 'user | Can change user'
Adding permission 'user | Can delete user'
Adding permission 'log entry | Can add log entry'
Adding permission 'log entry | Can change log entry'
Adding permission 'log entry | Can delete log entry'

どんなスキーマになったか、確認します。

$ sqlite3 data.sqlite                          [~/projects/juma]
SQLite version 3.3.5
Enter ".help" for instructions
sqlite> .schema
CREATE TABLE "auth_group" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(80) NOT NULL UNIQUE
);
CREATE TABLE "auth_group_permissions" (
    "id" integer NOT NULL PRIMARY KEY,
    "group_id" integer NOT NULL REFERENCES "auth_group" ("id"),
    "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"),
    UNIQUE ("group_id", "permission_id")
);
CREATE TABLE "auth_message" (
    "id" integer NOT NULL PRIMARY KEY,
    "user_id" integer NOT NULL,
    "message" text NOT NULL
);
CREATE TABLE "auth_permission" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(50) NOT NULL,
    "content_type_id" integer NOT NULL,
    "codename" varchar(100) NOT NULL,
    UNIQUE ("content_type_id", "codename")
);
CREATE TABLE "auth_user" (
    "id" integer NOT NULL PRIMARY KEY,
    "username" varchar(30) NOT NULL UNIQUE,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL,
    "email" varchar(75) NOT NULL,
    "password" varchar(128) NOT NULL,
    "is_staff" bool NOT NULL,
    "is_active" bool NOT NULL,
    "is_superuser" bool NOT NULL,
    "last_login" datetime NOT NULL,
    "date_joined" datetime NOT NULL
);
CREATE TABLE "auth_user_groups" (
    "id" integer NOT NULL PRIMARY KEY,
    "user_id" integer NOT NULL REFERENCES "auth_user" ("id"),
    "group_id" integer NOT NULL REFERENCES "auth_group" ("id"),
    UNIQUE ("user_id", "group_id")
);
CREATE TABLE "auth_user_user_permissions" (
    "id" integer NOT NULL PRIMARY KEY,
    "user_id" integer NOT NULL REFERENCES "auth_user" ("id"),
    "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"),
    UNIQUE ("user_id", "permission_id")
);
CREATE TABLE "django_admin_log" (
    "id" integer NOT NULL PRIMARY KEY,
    "action_time" datetime NOT NULL,
    "user_id" integer NOT NULL REFERENCES "auth_user" ("id"),
    "content_type_id" integer NULL REFERENCES "django_content_type" ("id"),
    "object_id" text NULL,
    "object_repr" varchar(200) NOT NULL,
    "action_flag" smallint unsigned NOT NULL,
    "change_message" text NOT NULL
);
CREATE TABLE "django_content_type" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(100) NOT NULL,
    "app_label" varchar(100) NOT NULL,
    "model" varchar(100) NOT NULL,
    UNIQUE ("app_label", "model")
);
CREATE TABLE "django_session" (
    "session_key" varchar(40) NOT NULL PRIMARY KEY,
    "session_data" text NOT NULL,
    "expire_date" datetime NOT NULL
);
CREATE TABLE "django_site" (
    "id" integer NOT NULL PRIMARY KEY,
    "domain" varchar(100) NOT NULL,
    "name" varchar(50) NOT NULL
);
CREATE TABLE "web_user" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(1024) NOT NULL,
    "introduction" varchar(1024) NOT NULL,
    "image" varchar(100) NOT NULL
);

Userモデルに対応するテーブルは、web_userになるようです。あとのテーブルは、Djangoが内部で使用するテーブルだと思われます。

管理者用サイト

Djangoには、各テーブルを操作するための管理者用サイトが用意されています。これを実行するためには、まず、settings.pyに、

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'juma.web', 
    'django.contrib.admin', # <- これ
)

を追加します。また、urls.pyを変更します。

--- urls.py.orig        2007-01-20 05:07:26.934995250 +0900
+++ urls.py     2007-01-20 03:08:25.984713750 +0900
@@ -5,5 +5,5 @@
     # (r'^juma/', include('juma.apps.foo.urls.foo')),

     # Uncomment this for admin:
-#     (r'^admin/', include('django.contrib.admin.urls')),
+     (r'^admin/', include('django.contrib.admin.urls')),
 )

管理者用サイトにアクセスするためには、

$ python manage.py runserver                   [~/projects/juma]
Validating models...
0 errors found.

Django version 0.95, using settings 'juma.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

としてサーバを起動し、http://127.0.0.1:8000/admin/にブラウザでアクセスします。ログインが面画表示されるので、python manage.py syncdbコマンドを実行したときに入力した名前とパスワードを入力します。そうすると、管理画面に遷移します。管理画面の操作方法は、いじっていれば分かると思います。

Userクラスの内部でAdminクラスを定義していたので、Userクラスも操作可能になっています。