画像ブログ

□未翻訳

□翻訳中

□翻訳完了(細田謙二)

■レビュー(中垣健志)

画像ブログ

ここでは、別の例として、管理者が画像を投稿して名前を付け、Webサイトの訪問者が画像を表示してコメントを送信できるようなWebアプリケーションを作成します。

Here, as another example, we wish to create a web application that allows the administrator to post images and give them a name, and allows the visitors of the web site to view the named images and submit comments.

前と同様に、 adminにあるsiteページで新しいアプリケーションを作成し、editページへ遷移してください:

As before, create the new application from the site page in admin and navigate to the editpage:

まずモデルを作成するところから始めます。モデルは、アプリケーション内の永続的なデータ(アップロードする画像、その名前、コメント)を表現します。初めに、モデルを作成/編集するためのファイルを作成します。余り深く考えず、このファイルは"db.py"とします。以下に示すコードは、db.py内の全ての既存のコードを置き換えることを想定します。モデルとコントローラは、Pythonコードなので.py拡張子を持つ必要があります。拡張子が指定されていない場合、web2pyによって追加されます。ビューは.html拡張子を代わりに持ちます。主にHTMLコードで構成されるからです。

We start by creating a model, a representation of the persistent data in the application (the images to upload, their names, and the comments). First, you need to create/edit a model file which, for lack of imagination, we call db.py. We assume the code below will replace any existing code in db.py. Models and controllers must have a .py extension since they are Python code. If the extension is not provided, it is appended by web2py. Views instead have a.html extension since they mainly contain HTML code.

"db.py"ファイルを、対応する"edit"ボタンをクリックして編集します:

Edit the db.py file by clicking the corresponding "edit" button:

そして次のように入力してください:

and enter the following:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

db = DAL("sqlite://storage.sqlite")

db.define_table('image',

Field('title'),

Field('file', 'upload'))

db.define_table('comment',

Field('image_id', db.image),

Field('author'),

Field('email'),

Field('body', 'text'))

db.image.title.requires = IS_NOT_IN_DB(db, db.image.title)

db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')

db.comment.author.requires = IS_NOT_EMPTY()

db.comment.email.requires = IS_EMAIL()

db.comment.body.requires = IS_NOT_EMPTY()

db.comment.image_id.writable = db.comment.image_id.readable = False

行を一つ一つ分析してみましょう。

Let's analyze this line by line.

1行目はdbと呼ばれるグローバル変数を定義します。dbはデータベース接続を表します。この場合、"applications/images/databases/storage.db"ファイルに保存されるSQLiteデータベースへの接続です。SQLiteの場合は、データベースが存在しない場合は、新たに作成されます。このファイルの名前は、グローバル変数dbの名前と同じように、変更することができます。しかし、覚えやすくするために、同じ名前にしておいた方が便利です。

Line 1 defines a global variable called db that represents the database connection. In this case it is a connection to a SQLite database stored in the file "applications/images/databases/storage.sqlite". In the SQLite case, if the database does not exist, it is created. You can change the name of the file, as well as the name of the global variable db, but it is convenient to give them the same name, to make it easy to remember.

3~5行目は、"image"テーブルを定義しています。define_tableは、dbオブジェクトのメソッドです。最初の引数"image"は、定義したテーブルの名前です。他の引数はこのテーブルに属するフィールドです。このテーブルは、"title"というフィールド、"file"というフィールド、プライマリキーとして機能する"id"というフィールドを持ちます("id"は明示的に宣言されません。すべてのテーブルはidフィールドをデフォルトで持つからです)。"title"フィールドは文字列であり、"file"フィールドはupload型です。uploadは、web2pyのデータ抽象化レイヤ(DAL)によって使用される特殊な型で、アップロードされたファイルの名前を保持します。web2pyは、ファイルのアップロード(サイズが大きいとストリーミングを介します)、ファイルの安全なリネーム、ファイルの保存をうまく行うことができます。

Lines 3-5 define a table "image". define_table is a method of the db object. The first argument, "image", is the name of the table we are defining. The other arguments are the fields belonging to that table. This table has a field called title, a field called file, and a field called id that serves as the table primary key (id is not explicitly declared because all tables have an id field by default). The field title is a string, and the field file is of type upload.upload is a special type of field used by the web2py Data Abstraction Layer (DAL) to store the names of uploaded files. web2py knows how to upload files (via streaming if they are large), rename them safely, and store them.

テーブルが定義されるとき、web2pyは以下に示すいくつかの可能なアクションのどれか1つを取ります:

When a table is defined, web2py takes one of several possible actions:

  • テーブルが存在しない場合、テーブルが作成されます。

    • if the table does not exist, the table is created;

  • テーブルが存在するが、その定義に対応していない場合、テーブルは定義に沿ってに変更されます。フィールドが異なる型を持つ場合、web2pyはその内容を変更しようと試みます。

    • if the table exists and does not correspond to the definition, the table is altered accordingly, and if a field has a different type, web2py tries to convert its contents;

  • テーブルが存在し、その定義に対応する場合、web2pyは何もしません。

    • if the table exists and corresponds to the definition, web2py does nothing.

この挙動は、"マイグレーション"と呼ばれます。web2pyではマイグレーションは自動的に行われます。しかしmigrate=Falseをdefine_tableの最後の引数に渡すことによって、テーブル毎にこれを無効にすることができます。

This behavior is called "migration". In web2py migrations are automatic, but can be disabled for each table by passing migrate=False as the last argument of define_table.

7~11行目では、"comment"と呼ばれるテーブルを定義しています。コメントは、"author"フィールド、"email"フィールド(コメントの作者のメールアドレスを保存されます)、"text"型の"body"フィールド(その作者によって送信された実際のコメントを保存するために使用します)、idフィールドを介してdb.imageを指す参照型の"image_id"フィールドを持ちます。

Lines 7-11 define another table called "comment". A comment has an "author", an "email" (we intend to store the email address of the author of the comment), a "body" of type "text" (we intend to use it to store the actual comment posted by the author), and an "image_id" field of type reference that points to db.image via the id field.

13行目において、db.image.titleは"image"テーブルの"title"フィールドを表します。requires属性は、web2pyフォームによって強制されることになる要求/制約を設定することを可能にします。ここでは、"title"は一意であることを要求します:

In line 13, db.image.title represents the field "title" of table "image". The attribute requiresallows you to set requirements/constraints that will be enforced by web2py forms. Here we require that the "title" is unique:

1.

IS_NOT_IN_DB(db, db.image.title)

これらの制約を表現するオブジェクトはバリデータと呼ばれます。複数のバリデータは、リストにおいてグループ化できます。バリデータは表示されている順序で実行されます。IS_NOT_IN_DB(a, b)は特殊なバリデータです。これは、新規レコードに対するbフィールドの値が、aの中にすでに入っていないかをチェックします。

The objects representing these constraints are called validators. Multiple validators can be grouped in a list. Validators are executed in the order they appear. IS_NOT_IN_DB(a, b) is a special validator that checks that the value of a field b for a new record is not already in a.

14行目は、"comment"テーブルの"image_id"フィールドがdb.image.idに存在することを要求します。データベースに関する限り、"comment"テーブルを定義した時点で、これはすでに宣言されています。ここではさらに、明示的に、この制約がweb2pyによって強制されることをモデルに知らせています。この制約は、新規のコメントが送信されたとき、フォーム処理のレベルで強制されます。その結果、不正な値は入力フォームからデータベースへ伝搬しません。ここではまた、"image_id"が対応するレコードの"title"、'%(title)s'によって表現されるように要求しています。

Line 14 requires that the field "image_id" of table "comment" is in db.image.id. As far as the database is concerned, we had already declared this when we defined the table "comment". Now we are explicitly telling the model that this condition should be enforced by web2py, too, at the form processing level when a new comment is posted, so that invalid values do not propagate from input forms to the database. We also require that the "image_id" be represented by the "title", '%(title)s', of the corresponding record.

19行目は、writable=Falseで、"comment"テーブルの"image_id"フィールドがフォームに表示されないように指示しています。さらに、readable=Falseで、読み取り専用フォームでも表示されないようにしています。

Line 19 indicates that the field "image_id" of table "comment" should not be shown in forms,writable=False and not even in readonly forms, readable=False.

15~17行目のバリデータの意味は明らかです。

The meaning of the validators in lines 15-17 should be obvious.

なお、次のバリデータは、

Notice that the validator

1.

db.comment.image_id.requires = IS_IN_DB(db, db.image.id, '%(title)s')

次のように、imageを表現するフォーマットを指定した場合、(自動的に)無視されます:

could be omitted (and would be automatic) if we were to specify a format for representing an image:

1.

db.define_table('image',....,format='%(title)s')

ここで、フォーマットは文字列、または、レコードを受け取り文字列を返す関数にすることができます。

where the format can be a string or a function that takes a record and returns a string.

一旦モデルが定義されると、エラーがない場合、web2pyはデータベースを管理するためのアプリケーションの管理インターフェイスを作成します。このインターフェイスには、editページの"database administration"リンクからから、直接以下のURLからアクセスします。

Once a model is defined, if there are no errors, web2py creates an application administration interface to manage the database. You access it via the "database administration" link in theedit page or directly:

http://127.0.0.1:8000/images/appadmin

これは、appadminインターフェイスのスクリーンショットです:

Here is a screenshot of the appadmin interface:

このインターフェイスは、"appadmin.py"というコントローラと対応する"appadmin.html"ビューにおいて実装されています。以降、このインターフェイスを単にappadminと呼びます。これにより、管理者は新規のデータベースレコードを挿入し、既存のレコードを編集、削除し、テーブルを閲覧し、データベースの結合(join)を行うことができるようになります。

This interface is coded in the controller called "appadmin.py" and the corresponding view "appadmin.html". From now on, we will refer to this interface simply as appadmin. It allows the administrator to insert new database records, edit and delete existing records, browse tables, and perform database joins.

appadminに最初にアクセスしたときに、モデルが実行されテーブルが作成されます。web2pyのDALは、選択したデータベース・バックエンド(この例ではSQLite)固有のSQL文にPythonコードを変換します。生成されたSQLは、editページから"models"の下にある"sql.log"リンクをクリックして、見ることができます。ただし、テーブルが作成されるまでリンクは現れません。

The first time appadmin is accessed, the model is executed and the tables are created. The web2py DAL translates Python code into SQL statements that are specific to the selected database back-end (SQLite in this example). You can see the generated SQL from the edit page by clicking on the "sql.log" link under "models". Notice that the link is not present until the tables have been created.

モデルを編集し、再びappadminにアクセスしようとする場合、web2pyは既存のテーブル修正するSQLを生成します。生成されたSQLは"sql.log"にログとして記録されます。

If you were to edit the model and access appadmin again, web2py would generate SQL to alter the existing tables. The generated SQL is logged into "sql.log".

さて、appadminに戻って、新しい画像レコードを挿入してみましょう:

Now go back to appadmin and try to insert a new image record:

web2pyは、db.image.fileの"upload"フィールドを、ファイルをアップロードするためのフォームに変換します。フォームがサブミットされ、画像がアップロードされるとき、ファイルは、安全な方法で拡張子はそのままにリネームされ、アプリケーションの"uploads"フォルダの下に新しい名前で保存されます。新しい名前はdb.image.fileフィールドに保存されます。この処理は、ディレクトリトラバーサル攻撃を防ぐために設計されています。

web2py has translated the db.image.file "upload" field into an upload form for the file. When the form is submitted and an image file is uploaded, the file is renamed in a secure way that preserves the extension, it is saved with the new name under the application "uploads" folder, and the new name is stored in the db.image.file field. This process is designed to prevent directory traversal attacks.

なお、各フィールドの型はウィジェット(widget)によってレンダリングされています。デフォルトのウィジェットはオーバーライドすることができます。

Notice that each field type is rendered by a widget. Default widgets can be overridden.

appdaminにおいてテーブル名をクリックすると、web2pyは現在のテーブルのすべてのレコードの選択を実行します。これは、次のDALクエリで特定されます

When you click on a table name in appadmin, web2py performs a select of all records on the current table, identified by the DAL query

1.

db.image.id > 0

その結果は次のようにレンダリングされます。

and renders the result.

SQLクエリを編集し[Submit]ボタンを押して、異なるレコードセットを選択することができます。

You can select a different set of records by editing the SQL query and pressing [Submit].

単一のレコードを編集、または、削除するには、レコードのid番号をクリックします。

To edit or delete a single record, click on the record id number.

IS_IN_DBバリデータのおかげで、"image_id"参照フィールドはドロップダウンのメニューでレンダリングされます。ドロップダウンの項目はキー(db.image.id)として格納されますが、バリデータで指定したように、db.image.titleによって表現されます。

Because of the IS_IN_DB validator, the reference field "image_id" is rendered by a drop-down menu. The items in the drop-down are stored as keys (db.image.id), but are represented by their db.image.title, as specified by the validator.

バリデータは強力なオブジェクトです。これは、どのようにフィールドを表現し、フィールドの値をフィルタし、エラーを生成し、フィールドから取り出した値をフォーマットするかを知っています。

Validators are powerful objects that know how to represent fields, filter field values, generate errors, and format values extracted from the field.

次図は、検証を通らないフォームをサブミットしたときに何が起こるかを示しています:

The following figure shows what happens when you submit a form that does not pass validation:

appadminによって自動生成されたものと同じフォームは、SQLFORMヘルパーを介してプログラム的に生成し、ユーザのアプリケーションに埋め込むことができます。これらのフォームは、CSSフレンドリで、カスタマイズすることができます。

The same forms that are automatically generated by appadmin can also be generated programmatically via the SQLFORM helper and embedded in user applications. These forms are CSS-friendly, and can be customized.

すべてのアプリケーションにはappadminが存在します。したがって、appadmin自体、他のアプリケーションに影響を与えずに変更することができます。

Every application has its own appadmin; therefore, appadmin itself can be modified without affecting other applications.

ここまで、アプリケーションはデータの保存をうまくやることができ、appadminを介してどのようにデータベースにアクセスするかを見てきました。appadminへのアクセスは管理者に対して制約されていて、アプリケーションのための本番用のwebインターフェイスとして意図されたものではありません。したがって、このウォークスルーの次のパートがあります。具体的には、次のものを作成します:

So far, the application knows how to store data, and we have seen how to access the database via appadmin. Access to appadmin is restricted to the administrator, and it is not intended as a production web interface for the application; hence the next part of this walk-through. Specifically we want to create:

  • "index"ページ。これは、すべての利用可能な画像をtitleでソートして一覧表示します。そして、それらの画像に詳細ページへのリンクを張ります。

    • An "index" page that lists all available images sorted by title and links to detail pages for the images.

  • "show/[id]"ページ。これは、リクエストされた画像を訪問者に提示します。そして、コメントを見たり投稿したりできるようにします。

    • A "show/[id]" page that shows the visitor the requested image and allows the visitor to view and post comments.

  • "download/[name]"アクション。アップロードした画像をダウンロードするために用いられます。

    • A "download/[name]" action to download uploaded images.

これはその図式です:

This is represented schematically here:

editページに戻り、"default.py"コントローラを編集し、その内容を次のものと入れ替えてください:

Go back to the edit page and edit the "default.py" controller, replacing its contents with the following:

1.

2.

3.

def index():

images = db().select(db.image.ALL, orderby=db.image.title)

return dict(images=images)

このアクションは、辞書を返します。辞書の項目のキーは、アクションに関連付けられたビューに渡される変数として解釈されます。ビューが存在しない場合、アクションは"generic.html"ビューにより表示されます。これは、すべてのweb2pyアプリケーションで用意されています。

This action returns a dictionary. The keys of the items in the dictionary are interpreted as variables passed to the view associated to the action. If there is no view, the action is rendered by the "generic.html" view that is provided with every web2py application.

indexアクションは、db.image.titleのよってソートされた、imageテーブルからのすべてのフィールド(db.image.ALL)の選択を実行します。選択の結果はレコードを格納するRowsオブジェクトです。これを、アクションによってビューへ返されるimagesと呼ばれるローカル変数に割り当てます。imagesは、反復可能で、その要素は選択された行になります。各行において、カラムは辞書のように、つまり、images[0]['title']のように、アクセスできます。また同様に、images[0].titleのようにもアクセスできます。

The index action performs a select of all fields (db.image.ALL) from table image, ordered bydb.image.title. The result of the select is a Rows object containing the records. Assign it to a local variable called images returned by the action to the view. images is iterable and its elements are the selected rows. For each row the columns can be accessed as dictionaries:images[0]['title'] or equivalently as images[0].title.

ビューを記述しない場合、辞書は"views/generic.html"によってレンダリングされます。indexアクションの呼び出しは次のように表示されます:

If you do not write a view, the dictionary is rendered by "views/generic.html" and a call to the index action would look like this:

まだアクションのビューが作成されていないので、web2pyはレコードをシンプルな表形式で表示しています。

You have not created a view for this action yet, so web2py renders the set of records in plain tabular form.

では、indexアクション用のビューを作成します。adminに戻り、"default/index.html"を編集して、その内容を次のように置き換えます:

Proceed to create a view for the index action. Return to admin, edit "default/index.html" and replace its content with the following:

1.

2.

3.

4.

5.

6.

7.

{{extend 'layout.html'}}

<h1>Current Images</h1>

<ul>

{{for image in images:}}

{{=LI(A(image.title, _href=URL("show", args=image.id)))}}

{{pass}}

</ul>

最初に注目する点は、ビューが特別な{{...}}タグを持つ純粋なHTMLということです。{{...}}に埋め込まれたコードは純粋なPythonのコードです。ただし、インデントが無意味になるという注意があります。コードのブロックは、行末にコロン(:)がついた行で始まり、passというキーワードで始まる行で終わります。ブロックの終わりが明らかな場合には、passは不要です。

The first thing to notice is that a view is pure HTML with special {{...}} tags. The code embedded in {{...}} is pure Python code with one caveat: indentation is irrelevant. Blocks of code start with lines ending in colon (:) and end in lines beginning with the keyword pass. In some cases the end of a block is obvious from context and the use of pass is not required.

5~7行目は、imagesの行をループで回し、各行の画像に対し次のように表示します:

Lines 5-7 loop over the image rows and for each row image display:

1.

LI(A(image.title, _href=URL('show', args=image.id))

これは、image.titleを含む<a href="...">...</a>タグを含んだ<li>...</li>タグになります。ハイパーテキスト参照(href属性)の値は次のようになります:

This is a <li>...</li> tag that contains an <a href="...">...</a> tag which contains theimage.title. The value of the hypertext reference (href attribute) is:

1.

URL('show', args=image.id)

つまり、これは、"show"という関数を呼んでいる現在のリクエストと同じアプリケーションとコントローラの範囲内にあるURLで、かつ、その関数に単一の引数args=images.idを渡すようなURLです。LI、Aなどはweb2pyのヘルパーで、対応するHTMLタグをマッピングします。無名引数はシリアライズされるオブジェクトとして解釈され、そのタグのinnerHTMLにて挿入されます。アンダースコアで始まる名前付き引数(例えば_href)はそのタグの属性として解釈されます。ただし、アンダースコアは付かない属性です。たとえば、_hrefはhref属性、_classはclass属性、などになります。

i.e., the URL within the same application and controller as the current request that calls the function called "show", passing a single argument to the function, args=image.id. LI, A, etc. are web2py helpers that map to the corresponding HTML tags. Their unnamed arguments are interpreted as objects to be serialized and inserted in the tag's innerHTML. Named arguments starting with an underscore (for example _href) are interpreted as tag attributes but without the underscore. For example _href is the href attribute, _class is the class attribute, etc.

例として、次の文は:

As an example, the following statement:

1.

{{=LI(A('something', _href=URL('show', args=123))}}

次のようにレンダリングされます:

is rendered as:

1.

<li><a href="/images/default/show/123">something</a></li>

少数のヘルパ(INPUT、EXTAREA、OPTION、SELECT)はまた、アンダースコアで始まらない特別な名前付き引数(valueとrequires)をサポートしています。これらはカスタムフォームを構築するために重要で、後で説明します。

A handful of helpers (INPUT, TEXTAREA, OPTION and SELECT) also support some special named attributes not starting with underscore (value, and requires). They are important for building custom forms and will be discussed later.

editページに戻ってください。すると、"default.py exposes index"というものが示されます。"index"をクリックして、新しく作成したページを訪れることができます:

Go back to the edit page. It now indicates that "default.py exposes index". By clicking on "index", you can visit the newly created page:

http://127.0.0.1:8000/images/default/index

これは次のように表示されます:

which looks like:

画像名のリンクをクリックすると、次に遷移します:

If you click on the image name link, you are directed to:

http://127.0.0.1:8000/images/default/show/1

これはエラーになります。なぜなら、"default.py"コントローラには"show"というアクションがまだ作成されていないからです。

and this results in an error, since you have not yet created an action called "show" in controller "default.py".

"default.py"コントローラを編集して、その内容を次のものと置き換えてみましょう:

Let's edit the "default.py" controller and replace its content with:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

def index():

images = db().select(db.image.ALL, orderby=db.image.title)

return dict(images=images)

def show():

image = db(db.image.id==request.args(0)).select().first()

form = SQLFORM(db.comment)

form.vars.image_id = image.id

if form.accepts(request.vars, session):

response.flash = 'your comment is posted'

comments = db(db.comment.image_id==image.id).select()

return dict(image=image, comments=comments, form=form)

def download():

return response.download(request, db)

このコントローラは、"show"と"download"の2つのアクションを保持しています。"show"アクションは、request.argsから解析されたidを持つ画像と、その画像に関連するすべてのコメントを選択します。そして、すべてを"default/show.html"ビューに渡します。

The controller contains two actions: "show" and "download". The "show" action selects the image with the id parsed from the request args and all comments related to the image. "show" then passes everything to the view "default/show.html".

画像のidは、次のようにして:

The image id referenced by:

1.

URL('show', args=image.id)

"default/index.html"ビューにおいて参照され、"show"アクションにおいてrequest.args(0)からアクセスすることができます。

in "default/index.html", can be accessed as: request.args(0) from the "show" action.

"download"アクションは、request.args(0)にファイル名を期待します。そして、ファイルがあるはずの場所へのパスを構築し、クライアントにそのファイルを返します。ファイルが大きすぎる場合、いかなるメモリのオーバーヘッドも発生させずに、ファイルをストリーミングします。

The "download" action expects a filename in request.args(0), builds a path to the location where that file is supposed to be, and sends it back to the client. If the file is too large, it streams the file without incurring any memory overhead.

以下の文について注意してください:

Notice the following statements:

  • 7行目は、指定したフィールドだけを用いて、db.commentテーブルに対するフォーム、SQLFORMを作成します。

    • Line 7 creates an insert form SQLFORM for the db.comment table using only the specified fields.

  • 8行目は、参照フィールドの値を設定します。これは入力フォームの一部ではありません。なぜなら、上で指定したフィールドのリストにないからです。

    • Line 8 sets the value for the reference field, which is not part of the input form because it is not in the list of fields specified above.

  • 9行目は、現在のセッションの中で(セッションは二重送信の防止とナビゲーションの実施のために使われます)、サブミットされたフォーム(サブミットされたフォームの変数はrequest.varsにあります)を処理します。サブミットされたフォームの変数が検証を通った場合、db.commentテーブルに新規のコメントが挿入されます。そうでない場合、フォームはエラーメッセージを含むように修正されます(たとえば、作者のメールアドレスが不適当である場合)。これは、すべての9行目で行われます!

  • Line 9 processes the submitted form (the submitted form variables are inrequest.vars) within the current session (the session is used to prevent double submissions, and to enforce navigation). If the submitted form variables are validated, the new comment is inserted in the db.comment table; otherwise the form is modified to include error messages (for example, if the author's email address is invalid). This is all done in line 9!.

  • 10行目は、フォームが受理された場合にのみ、データベース・テーブルにレコードが挿入された後に実行されます。response.flashは、web2pyの変数で、ビューにおいて表示され、訪問者に何が行われたのかを通知するために使われます。

    • Line 10 is only executed if the form is accepted, after the record is inserted into the database table. response.flash is a web2py variable that is displayed in the views and used to notify the visitor that something happened.

  • 11行目は、現在の画像を参照するすべてのコメントを選択します。

    • Line 11 selects all comments that reference the current image.

"download"アクションは、雛形アプリケーションの"default.py"コントローラにおいてすでに定義されています。

The "download" action is already defined in the "default.py" controller of the scaffolding application.

"download"アクションは辞書を返さないので、ビューは必要ありません。一方、"show"アクションはビューを持つべきです。したがって、adminに戻って"default/show.html"という新規のビューを作成してください。

The "download" action does not return a dictionary, so it does not need a view. The "show" action, though, should have a view, so return to admin and create a new view called "default/show.html".

この新規のファイルを編集し、その内容を次のものと置き換えてください:

Edit this new file and replace its content with the following:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

{{extend 'layout.html'}}

<h1>Image: {{=image.title}}</h1>

<center>

<img width="200px"

src="{{=URL('download', args=image.file)}}" />

</center>

{{if len(comments):}}

<h2>Comments</h2><br /><p>

{{for comment in comments:}}

<p>{{=comment.author}} says <i>{{=comment.body}}</i></p>

{{pass}}</p>

{{else:}}

<h2>No comments posted yet</h2>

{{pass}}

<h2>Post a comment</h2>

{{=form}}

このビューは、<img ... />タグ内において、"download"アクションを呼び出すことによってimage.fileを表示します。コメントがある場合は、それらに対してループを回し、一つ一つ表示します。

This view displays the image.file by calling the "download" action inside an <img ... /> tag. If there are comments, it loops over them and displays each one.

どのようにすべてが表示されるかを以下に示します:

Here is how everything will appear to a visitor.

訪問者がこのページでコメントをサブミットするとき、コメントがデータベースに保存され、ページの下部に追加されます。

When a visitor submits a comment via this page, the comment is stored in the database and appended at the bottom of the page.