1.1. Archetypesの紹介

一つ上に移動

Archetypesによってコンテンツタイプを素早く開発したり、最小限のコード量で開発したりすることができるようになります

Archetypesは、Ploneでプロダクトの開発を自動化するためのフレームワークです。コンテンツためのコードがPythonで書かれる と、Archetypesはそれ以外のほとんどすべてのこと(ビューを生成したり、フォームを編集したり、など)を処理してくれます。これによってコンテ ンツタイプを素早く開発したり、最小限のコード量で開発したりすることができるようになります。コードの量が少ないということはバグが潜む可能性が少なく なりますし、Ploneの変化の際にメンテナンスするコードの量も少なくなります。開発サイクルが素早くなり、コストも低くなるわけです。

プロダクトはこういったオブジェクトの記述に基づいているので、そのプロダクトを生成するためのツールを利用することができます。例えば後述する ArchGenXMLはプロダクトをUMLツールで生成することを可能にしてくれます。すなわちUMLモデルの出力をArchGenXMLを通して渡し、 Ploneにプロダクトを素早く登場させることができるわけで、実質なにもコードを書く必要がないということになるわけです。(12章において紹介したよ うな)Pythonでプロダクトを書くことが、少し難しいと感じるならば、この章で紹介することが役に立つでしょう。

しかしこれはArchetypesを使うことがすべてのプロダクトにとって正しいということを意味しているわけではありません。時々 Archetypesがやや冗長に感じることもあります。例えば1つのフィールドを持つコンテンツタイプがあったとして、そこに入るデータに16の異なる 順列があるような場合はArchetypesフレームワークを使う意味はほとんどありません。ただしもちろんこれは極端な例で、ほとんどの場合は Archetypesがまさに必要なものであると感じることでしょう。

あるウェブサイトを開発している会社に関して耳にした話ですが、プログラマを連れてクライアントの元へ訪問しクライアントの要望を聞いているとき に、プログラマはArchetypesにそれを実装していきました。彼らはミーティングが終わる前に、クライアントに簡単なプロトタイプを提示することが できたそうです。

全体的にほとんどのPloneプロダクト開発チームはArchetypesをプロダクト開発の方法として採用していますので、これが概念共有となっており、実際Plone開発の標準となっています。Archetypesの特徴的な機能としては以下のようなものがあります。

  • 自動的にビューや編集ページを生成するので、ページテンプレートコードを書く必要がありません
  • ユニークなオブジェクトIDを管理します。生成したすべてのオブジェクトはユーザが変更することのできないユニークなIDを持つでしょう。すなわちオブジェクトが移動されない限りは常にオブジェクトを見つけることができるのです。
  • オブジェクト間のリファレンスを生成します。それぞれのオブジェクトは他のオブジェクトとたくさんの関係を持つことができます。例えばニュースアイテムにたくさんのリンクオブジェクトを付け加えることができます。
  • 標準的なセキュリティをセットアップします。すべてのセキュリティ動作が成されるので、デフォルトのセットアップをしたいときに実は何も変更することはないのです。
  • 代替的な保存オプションをもっています。データをZopeの標準データベースではなく、リレーショナルデータベースに保存することができます。
  • データ変換が可能です。たとえばMSWordのデータをHTMLに変換できます。

ArchetypesはPloneに特化したものではなく、例えばCMFなどのZopeフレームワークでも利用できますが、現時点ではほとんど Ploneによって利用されています。PloneがZope3に移行したときには、ArchetypesとZope3のスキーマは一つにまとまる予定で す。すなわちArchetypesを利用することはプロダクトの将来を保証してくれますし、今後のPloneにおいてもうまく動いてくれるでしょう。

この章ではArchetypesを用いて新しいコンテンツタイプを構築していきます。この章は他の章で学習した情報のすべてをまとめあげるもので、 いくつかの基本的な概念を通して素早く説明していきます。Archetypesのインストールの仕方を説明した後、基本的なコンテンツタイプを生成するや り方をお見せしましょう。

Archetypesの紹介

ArchetypesはPloneのインストーラやパッケージに同梱されてきますので、すでにArchetypesはインストールされていることと 思われます。もしインストールされているかどうかわからない場合には、ZMIのコントロールパネルのProductsを調べることでそれを確認することが できます。今回の例ではArchetypes 1.2.5-rc4のPloneでテストを行っています。

Archetypesをインストールするにはhttp://sf.net/projects/archetypesへ行く必要があります。 Filesをクリックし、最新のArchetypesをダウンロードします。この章で扱うのはarchetypes-1.2.5-rc4.tgzです。ま ずは以下のように解凍する必要があります。

$ tar -zxf archetypes-1.2.5-rc4.tgz
$ cd archetypes-1.2.5-rc4

この時点で、何をインストールするか決定する必要があります。インストールする最小限のものはArchetypesディレクトリと generatorモジュール、validationモジュールの3つです。これらをインストールするには、これらをインスタンスホームの Productsディレクトリに移動します。私の場合はインスタンスホームが/var/zopeなので、以下のようにコマンドを打ちます。

$ mv Archetypes /var/zope/Products
$ mv generator /var/zope/Products
$ mv validation /var/zope/Products

ArchExampleおよびArchGenXMLは両方ともオプションで、Ploneを動かすのに必須ではありません。しかしこの章では両方を例として用いますのでインストールしておくとよいでしょう

ArchExampleをインストールするにはArchExampleをインスタンスホームのProductsディレクトリへ移動します。

$ cd ..
$ mv ArchExample /var/zope/Products

ArchGenXMLを利用したい場合、これをどこか特定の場所へインストールする必要はありません。忘れないような場所へ置いておけばいいので す。私はふつうこれを他のプロダクトと同じようにインスタンスホームのProductsディレクトリへおいておきます。何も害はありませんし、忘れないよ うにおいておくだけです。

$ ArchGenXML /var/zope/Products

ArchGenXMLのドキュメントに述べられているように、ArchGenXMLはPyXMLがインストールされていることを必要とします。 WindowsやMacのインストーラを用いている場合はこれは既に含まれています。層でない場合にはhttp://pyxml.sf.netへいって パッケージをダウンロードしてください。私の場合には最新のパッケージは0.8.3でしたので、次のようにしました。

$ tar -xvf PyXML-0.8.3.tar.gz
$ cd PyXML-0.8.3
$ python setup.py install

注意:ふつうこのようにインストールすると、root権限を要求されます

これですべてがインストール・セットアップされました。

Archetypesに飛びこむ

Archetypesのすばらしい例が入手可能ですので、何かを構築するよりもArchetypesに付いてくるArchExampleを見ていきましょう。これはArchetypesのパワーを示す例として「article」というコンテンツタイプを追加します。

Article.pyはメインのプロダクトコードを含んでいます。これまでの例とは全く異なるコードが見えるでしょう。これはスキーマを含んでいます。

StringField("blurb",
           searchable = 1,
           widget = TextAreaWidget(),
           ),

このコードは「blurb」というコンテンツにアトリビュートがあり、これは文字列で、HTMLのテキストエリアとして表示されることを意味してい ます。フィールドとウィジェットのすべてのオプションを説明しますが、今はPloneのコンテンツタイプを見てみましょう。下図ではblurbコンテンツ タイプを追加しました。

article-blurb-part

たった4行のコードを書くことでコンテンツタイプにフィールドを追加することができます。この標準的なフォーム要素が表示されたわけです。これを修 正するのがどれだけ簡単かを見せるために、フォームのラベルを「Article Blurb」とし、このフィールドを必須にしてみます。これを行うためには以下のように変更します。

StringField("blurb",
           required = 1,
           searchable = 1,
           widget = TextAreaWidget(label="Article Blurb"),
           ),

ここでは「required = 1」というパラメータを追加しました。これによってこのフィールドは必須となり、パラメータにラベルを付けます。Ploneを再起動し、新しい articleを追加すると、ユーザインターフェイスはこの新しいスキーマを反映したものに変更されます。このフィールドは「Article Blurb」と呼ばれることになります。

blurb-part

これは単なる修飾的な変更ではありません。この変更は背後にあるスキーマの変更を反映したものであり、これがArchetypesの本当のパワーな のです。これを実際にPythonで書くことと比較すると、大変な重労働から解放されたことがわかるでしょう。すなわち、スキーマを定義すればそれをまと めて即座にArchetypesにまとめることができます。あとはそれを簡単に修正したり、その変更を反映させたりすることができるのです。

これから紹介するいくつかの例を自分で変更したときはPloneを再起動する必要があります。しかしそれだけですべての新しい変更点は読み込まれ、適切に登録されます。

スキーマ、フィールド、ウィジェット

下図はスキーマとフィールドとウィジェットの関係を示しています。

relationship-between-schemas-fields-widgets

スキーマとベーススキーマ

スキーマを生成するためには、スキーマオブジェクトへタプルとしてフィールドを渡します。例えばarticleスキーマは「group」「blurb」「body」という3つのフィールドを持っています。以下のコードがスキーマのはじめの部分です。

Schema((
    StringField('group',
        vocabulary=ARTICLE_GROUPS,
        widget=SelectionWidget(),
        ),
        # 他のフィールドが続く
        )

2つ以上のスキーマを合わせることも可能です。実はこれがArchExampleが実際にやっていることで、コンテンツタイプの中で定義されたスキーマをBaseSchemaと呼ばれるスキーマに追加しているのです。

BaseSchemaは、すべてのPloneのコンテンツタイプが持っている2つの要素である「title」と「ID」を持っています。タイトルは ユーザインターフェイスに何かを表示するために必要です。またID、すなわちショートネームは標準的なPloneの慣習に対応しています。これら2つのス キーマはより大きなスキーマを生成するために合わされます。ArchExampleでは既存のBaseSchemaにコンテンツタイプのスキーマを追加す ることにします。例えば

schema = BaseSchema + Schema((
    StringField('group',
        vocabulary=ARTICLE_GROUPS,
        widget=SelectionWidget(),
        ),
        # 他のフィールドが続く
        )
...

アイテムはスキーマのクエリから、スキーマに追加された順番で返されるということに注意しておくとよいでしょう。これはスキーマ中のフィールドを移 動させることで、ユーザインターフェイスに表示される順番を再配置できることを意味しています。すなわちBaseSchemaを最初に持ってきているの は、IDおよびtitleフィールドがページの先頭に表示されることを意図したものです。

フィールド

StringFieldフィールドを見てきてわかるように、これはコンテンツタイプの文字列を表しています。Archetypesでは下表に示すよ うなたくさんのフィールドを利用できます。これからさらに多くのフィールドが追加されるかもしれませんし、必要であれば自分のフィールドを作成することも できます。

Archetypesで利用できるフィールド:

名前 タイプ
デフォルトウィジェット
説明
BooleanField Boolean values ComputedWidget Simple storage of true or false for a field.
DateTimeField Date and time objects CalendarWidget For storing dates and times.
FileField Files FileWidget Storage for large chunks of data such as plain-text files, Microsoft Word documents, and so on.
FixedPointField Fixed-point numbers DecimalWidget For storing numerical data with fixed points.
FloatField Floats DecimalWidget For storing numerical data with floating points.
ImageField Image ImageWidget Stores an image and allows dynamic resizing of the image.
IntegerField Integer StringWidget For storing numerical data as integers.
LinesField Lists LinesWidget A list of data such as keywords.
PhotoField Image PhotoWidget Same as an image field but has more default image sizes.
ReferenceField Reference ReferenceWidget Contains a reference between this object and another.
StringField String StringWidget A string field optimized for smaller strings-say, fewer than 100 words.
TextField String TextWidget A string field optimized for larger strings say, larger than 100 words. The string can also be transformed into multiple formats.

それぞれのフィールドは、それ指定されない場合に適用されるデフォルトのウィジェットを持っています。先に出たblurbの例では TextAreaWidgetを指定しました(ウィジェットについては時節で言及します)。これらのフィールドはすべてArchetypesの publicモジュールからインポートします。

from Products.Archetypes.public import BooleanField

すべてのフィールドは同じ方法で初期化します。フィールドを作成し、必須パラメータである「name」を渡します。オプションとして他のキーワードパラメータを渡すこともできます。例えば以下のように。

from Products.Archetypes.public import IntegerField
# 年齢を表す簡単なフィールド
age = IntegerField('age')

それぞれのフィールドは、そのフィールドに割り当てることのできるアトリビュートを持っています。このうち少なくとも2つは既に登場しています (nameとwidgetアトリビュートです)。nameアトリビュートはフィールドに唯一必須のパラメータで、ユニークで、小文字で、スペースやピリオ ドを含まないものでなければなりません。このnameアトリビュートは内部でのみ用いられるので、名付けルールは重要です。他の値はオプションで、下表で はこれらのアトリビュートを述べます。

フィールドのアトリビュート:

名前 説明 取り得る値
accessor The name of the method to get the value of the field, so you could change how this field is retrieved Any method name (for example, specialGetMethod)     See the 'Overriding Default Methods” section later in this chapter.
default The default value for the field. Should be appropriate to the field.
default_method A string for obtaining a value for the field; one is created for you by default if you don't define one. Any string (for example, getSpecialDescription). See the 'Overriding Default Methods” section later in this chapter.
edit_accessor The name of a method to get the raw value of a field. Any method name (for example, rawGetMethod). See the 'Overriding Default Methods” section later in this chapter.
enforceVocabulary If enabled, you won't accept anything outside the vocabulary. True or False.
index If you want this field to be placed in its own catalog index, then specify the type of index here as a string. If you append :schema onto the end of the schema, then this will also be added as a metadata column. The name of any index, such as KeywordIndex or KeywordIndex:schema.
name A unique name for this field. Any string, lowercase conforming to standard Python variable rules (for example, description, user_name, or coffee_bag_6).
mode The read and write mode of field, as a string; the default is to be read and write. For read only: r, for write only: w, for read and write: rw.
multiValued If this field can have multiple values, this is useful for things such as drop-down lists. True or False.
mutator The name of the method to alter the value of the field, so you could change how this field is set. Any method name (for example, specialSetMethod). See 'Overriding Default Methods” later in this chapter.
primary If True on a field, then this will be the field that responds to File Transfer Protocol (FTP) and WebDAV requests. There can be only field that does this; if multiple are defined, the first one in the schema will be used. You normally do this for the main body attribute. True or False.
required Specifies that some value for this field required. True or False.
 schemata  Place the field into the grouping of other fields called schematas
 default

 metadata

 user_information

 searchable  A boolean that specifies if this field will be added to the searchable text and can be used in the searches.  True or False.
 validators  The validations that will be performed on the field as a tuple of strings; it starts at first and validates against each validation.  Any validator; see the 'Validations of Input” section later.
 vocabulary  A list of values that a user can choose from, for example, the values to show in a drop-down list.  List of strings (for example, ["Green", "Red", "Blue"]).
 storage  Where to store the value; the default is Attribute Storage, which stores the field as an attribute of the object.  Any valid storage object such as AttributeStorage or SQLStorage. You can find these in the Archetypes Application Programming Interface (API). For more information, see the 'Storing Your Content in a SQL Database” section later in this database.
 widget  The widget that will be used to display this field.  Any widget object.

これでデフォルトのフィールドとアトリビュートについて述べましたので、ウィジェットについて見てみましょう。

ウィジェット

ウィジェットはそのオブジェクトが視覚的にどのように表現されるかについての情報を含んでいます。表示されるアトリビュートのビューはそのアトリ ビュートのタイプに関係していることが多いですが、表示についてのオプションがいくつかあります。ウィジェットはArchetypesのpublicモ ジュールからインポートすることができます。

from Products.Archetypes.public import BooleanWidget

すべてのウィジェットは同じ方法で初期化します。ウィジェットを作成し、必要とするキーワードパラメータを渡します。

from Products.Archetypes.public import IntegerFiled
from Products.Archetypes.public import IntegerWidget
# 年齢を表す簡単なフィールド
age = IntegerField('age',
        widget=IntegerWidget(label="Your age")
        )

ウィジェットはそのウィジェットのタイプによってその他のアトリビュートを持つこともできます。ほとんどの場合、これらのアトリビュートはHTML のアトリビュートに直接対応しています。例えばStringWidgetでは、sizeアトリビュートを設定することができます。これはHTMLの sizeアトリビュートの設定を生成しますので、20文字サイズのinputを設定するためには以下のようなウィジェットにします。

bankAccountNumber = StringField('bank',
    widget=StringWidget(
        label="Bank account number",
        size=20)
    )

下表で示すのはArchetypesで利用可能なウィジェットです。

利用可能なウィジェット値:

名前 説明 他の属性
BooleanWidget Shows two checkboxes for the possible values.

CalendarWidget Returns a set of input boxes with a link to a helper pop-up box so that a user can select a date.     

ComputedWidget Returns the computed value as HTML.
DecimalWidget A simple HTML input box for a string. size
EpozWidget An HTML Epoz widget that shows the Epoz rich-text editor for the content. You can provide format, rows, mode, and cols (for columns)
FileWidget Displays an HTML file element for users to upload files.
IdWidget A simple HTML input box that's used for rendering autogenerated IDs.

ImageWidget Shows and allows the editing of images. You can provide a display_threshold that allows you to set the size of an image; if it's below this size, the image will display in the Web page.
IntegerWidget A simple HTML input box for a string. size
KeywordWidget This displays a list of keywords from the catalog in a complicated widget, such as the one in the Properties tab on a normal object.

LabelWidgets Used to display labels on forms; no values or form elements.
LinesWidget Displays a text area that users can enter values. rows and columns
MultiSelectionWidget A selection widget; by default it's an HTML select widget. format, which can be one of select or checkbox
PasswordWidget An HTML password element.
RichWidget Allows the input of a file in multiple formats that are then transformed. See 'Transforming Data' later in this chapter for more information. You can provide rows, cols *(columns), and *format.
ReferenceWidget Shows an HTML select element of a list of possible references.
SelectionWidget Shows a selection widget. If it's flex (the default), then if the number of choices is more than four, a select element is used; otherwise a radio button is used. format, which can be one of flex, select, or radio.
StringWidget A simple HTML input box for a string size and maxlength.
TextAreaWidget A text area widget that allows the uploading of the content in multiple formats You can provide allowed_content_types, which is a list of strings; each string represents a meta_type of the type of content uploaded.

以上に示したウィジェットそれぞれに対して、下表に示すすべてのアトリビュートを設定可能です。labelアトリビュートは既に見たとおり、そのウィ ジェットの説明を設定します。その他のアトリビュートを組み合わせることで完全なアトリビュートセットを生成することができるでしょう。

アトリビュートの取りうる値:

名前 説明 取り得る値
label The label that will appear in the user interface with this field. Any string, for example, Start Date for a field start_date.
modes The modes that this widget will be shown in; by default there are two modes: view and edit. A list of modes as strings; by default it's ("view", "edit").
populate If this is enabled, the view and edit fields will be populated. Usually this enabled, but for fields such as a password field, this shouldn't be the case. Usually this is true by default. True or False
postback If this is enabled, then when an error is raised, the field is repopulated; for fields such as a password field, this shouldn't be the case. Usually this is true by default. True or False
visible If the attribute should be visible in the user interface. This is a dictionary mapping the view mode to a string, describing the visibility. Choices are visible, hidden (shown in an HTML hidden form value), invisible (not shown at all). For example, {'view': 'visible', 'edit': 'hidden' } means that the view will show, but the edit page will hide the value.

フィールドとウィジェットを組み合わせた例

このセクションではふだんよく使われるような便利な組み合わせの例を紹介します。この例では、好きなフルーツのドロップダウンメニューのリストを作 成します。これを実現するためには、文字列のリストとしてvocabularyアトリビュートを定義することになります。すなわちフィールドのタイプは StringFieldとなります。そしてウィジェットをSelectionWidgetとして定義することでこれがドロップダウンメニューになります。

StringField('fruit',
    vocabulary = ["Apple", "Orange", "Mano", "Banana"],
    widget = SelectionWidget(label = "Favourite Fruit")
    )

ImageFieldはPloneサイトで画像を生成・管理するのに便利です。ユーザが画像をアップロードできるようなシンプルなフィールドを作るには以下のようにします。

ImageField('portrait',
    widget = ImageWidget(label = "My picture"),
    )

次の例は非常に複雑なコンテンツタイプです。ほとんどのコンテンツタイプはデータを格納できる一つのメインフィールドを持つでしょう。基本的な documentタイプを考えてみると、文章を入力したり編集したりできるbodyフィールドがあるのに気づくでしょう。このbodyフィールドはコンテ ンツタイプの主体となる文章です。この標準的なフィールドに対していくつかのアトリビュートを追加できます。

まず、このフィールドを検索対象にしたいのではないでしょうか。これにはsearchableアトリビュートを設定します。次にFTPや WebDAVのリクエストに応答させたいと思うかもしれません。これにはprimaryアトリビュートを設定します。また複数のコンテンツタイプをアップ ロードしたい場合もあります。これにはallowable_content_typesアトリビュートを設定します。そしてもちろん、このフィールドをど のように見せるかということを設定してやります。default_output_typesを設定してやればOKです。ということで以上のような要件を満 たすフィールドは以下のようになります。

TextField('body',
    searchable = 1,
    primary = 1,
    default_output_types = 'text/html',
    allowable_content_types = ('text/plain',
                                'text/structured',
                                'text/restructured',
                                'text/html',
    widget = RichWidget(label = 'Body'),
    )

primaryフィールド:整列化、およびFTPとWebDAVに対する反応

Ploneはオブジェクトが多くのアトリビュートを持ち、さらにプレーンファイルとしては表現できない、オブジェクト指向のシステムです。あいにく FTPやWebDAVといった既存のプロトコルのほとんどはプレーンなファイルとしてコンテンツを扱いますので、これらの間で翻訳をしてくれるような何ら かの方法が必要です。primaryフィールドはまさにこのことをしてくれます。オブジェクトに対してprimaryフィールドを設定すると、このフィー ルドはそういったプロトコルでやりとりされるようになります。

もちろんこれは完璧な解決法ではないのですが、FTPやExternal Editorを使うと、オブジェクトのメタデータについてのキーと値を含んだ数値がページのトップへ埋め込まれているのがわかります。これを使うことに よってやりとりを行うというのも一つのやり方です。

マーシャリング(整列化)を行うためには、スキーマに「marshall=some_marshaller()」などの記述を加える必要がありま す。現状では2つのマーシャラがあります。一つはPrimaryFieldMarshallerで、すべてのコンテンツをオブジェクトへ移します。もう一 つはRFC822Marshallerで、メールで使われるような、フィールド名と値のペアによってコンテンツを処理します。この章の目的を考えると、コ ンテンツをExternal Editorで処理するためにPrimaryFieldMarshallerを使うことにしましょう。

入力のバリデーション

コンテンツを編集したときのコンテンツそのものや基本的なエラー(例えば必須項目が無いなど)はうまく処理されますが、もう少し洗練されたエラー処 理ができればいいなと思うときがあるでしょう。実はコンテンツが正しく入力されているかどうかをテストするためのバリデーションのセットを使うことができ ます。例えば、IntegerFieldがあるときには入力されたデータが適切かどうかを確認したいでしょう。

これにはフィールドにvalidatorsパラメータを追加します。例えばIntegerFieldが本当にIntegerになっているかどうかをテストするには次のようにします:

from Products.Archetypes.public import IntegerField
from Products.Archetypes.public import IntegerWidget
# シンプルな年齢のフィールド
age = IntegerField('age',
    validators=("isInt"),
    widget=IntegerWidget(label="your age")

しかしisIntはどこから来るのでしょう?isIntはバリデーションフレームワークに登録されたバリデータの名前です。数こそ少ないものの、こ れらのバリデータは非常に便利です(下図に示します)。これらの詳細についてはソースコードを読んだり、正規表現を調べたりすることをおすすめします。こ れはProductsディレクトリのvalidation/validators/__init__.pyモジュールにあります。

利用可能なバリデータ:

名前 説明
isDecimal This validates that the string is a decimal, including positive and negative, exponentials, and so on.
isInt This validates that it's an integer.
isPrintable This validates that this is a letter or a number.
isSSN This validates that it's nine numbers (the length of a U.S. Social Security number).
isUSPhoneNumber This validates that it's ten numbers and is optional.
isInternationalPhoneNumber This validates that it's at least one number and is optional.
isZipCode This validates that it's five or nine numbers.
isURL This validates that the input starts with http://, ftp://, or https://.
isEmail This validates that this conforms to the standard e-mail syntax.

また自分でバリデータを登録することも可能です。バリデータはivalidatorインターフェイスを実装したシンプルなクラスになります。既存のものは 2つあって、RegexValidatorは正規表現を確認するものですし、RangeValidatorは値の範囲を確認するものになります。例えば ユーザの年齢が0~150際であることを確認するような新しいバリデータを追加するには、フィールドを作成する前にコンテンツタイプに以下のコードを追加 します。

from validation.validators.validator import RangeValidator
from validation import validation

# RangeValidatorはバリデータの名前、開始値、終了値を取ります
validAge = RangeValidator("validAge", 0, 150)
validation.register(validAge)

そしてフィールドではvalidatorsを以下のように設定します。

validators=("isInt", "validAge")

このようにするとまず最初に整数であることがチェックされ、その後にその整数が年齢の範囲に含まれているかがチェックされます。もし RegexValidatorやRangeValidator以外の全く新しいバリデータを必要としているならば、新規のバリデーションシステムを作成す る必要があります。上記の例ではデータが2つの値の間にあることをチェックするバリデーションを作成しましたが、以下では DateRangeValidatorというバリデータを作って、与えられた日付が指定された日付の間にあるかどうかをbooleanで返してみましょ う。これは例えば学校が休暇中であるかどうかなどを確認するときに使えます。

さてそれではまず最初にvalidatorsモジュールの中にDateRangeValidaorというバリデータを定義しましょう。これによって 日付の範囲を登録することになります。具体的にはZopeのDateTimeオブジェクトを使うことにします。バリデータはシンプルです。nameを持 ち、__call__メソッドに対して反応をするクラスです。以下はvalidatorsモジュールに追加されたDateRangeValidaorで す。

from DateTime import DateTime

class DateRangeValidator:
    __implements__ = (ivalidator,)

    def __init__(self, name):
        selft.name = name

    def __call__(self, value, *args, **kwargs):
        min, max = args[:2]
        if not isinstance(value, DateTime):
            value = DateTime(value)

    return min < value and value < max

Zopeをリスタートすると、この新しいバリデーションを登録することができるようになります。

from validation.validators.validator import DateRangeValidator
from validation import validation
from DateTime import DateTime

christmas = DateRangeValidator("ChristmasHolidays",
    DateTime('12/18/2004'),
    DateTime('01/09/2005'),
    )
validation.register(christmas)

BaseClassのビューとアクションを上書きする

Archetypesは様々な必要性に応じるような標準セットに基づいたビューやアクションをデフォルトで生成します。このアクションはもちろん 「表示」、「編集」、「プロパティ」であり、関連づけもまたこのアクションの一つです。あなたはこのオブジェクトに対するビューや編集ページを探そうとは しないでしょう。これらはArchetypesによって自動的に生成されるものです。しかしこれらを上書きすることはできます。

ほとんどの場合はビューメソッドを上書きして自分なりのビューを作りたいのではないでしょうか。デフォルトで提供されるものはかなり基本的なもの で、特定のページのために作られたわけではないからです(もちろんPloneのページは平均的なCMSのものよりは良いと思いますが)。しかしページのコ ンテンツに応じた必要性があるかもしれません。

Archetypesはそれぞれのコンテンツタイプのクラスを生成します。Archetypesでは、12章で述べたようなソースコードタイプのク ラスを作成するのとほとんど同じように、自分のコンテンツタイプのための基本的なクラスを生成することが可能です。この基本クラスは BaseContentと言って、Archetypesのpublicモジュールからインポートすることができます。このBaseContentクラスは Archetypesが知る必要のあることをすべて定義しています。このクラスを作成することで自分がしたいことのほとんどすべてを上書きすることができ るでしょう。

今示してきたように、これには2つのパーツがあります。まずはfactoryタイプの情報によって利用されるアクションを作成することです。Archetypesではクラスにactionアトリビュートを付与することでこれを行うことができます。例えば:

from Products.Archetypes.public import BaseContent

class Article(BaseContent):
    # 他のコード
    actions = ({'id': 'view',
                'name': 'View',
                'action': 'string:&{object_url}/article_view',
                'permissions': (CMFCorePermissions.View,)
               },)

次に実際のビューのための(上記コード内で指定した)article_viewという名前でページテンプレートを生成する必要があります。この文字 列はこのプロダクトにおいてコンテンツタイプが存在するページテンプレートを定義します。今回の例の場合はArchExampleの 「skins/archexample」ディレクトリにこのページテンプレートのコピーを確認することができます。

変更を確認するためにはPloneをリスタートする必要があります。またportal_typesツールにインストールされたアクションを変更しま すので、このアクションを変更した場合はプロダクトを再インストールする必要があるでしょう。これはPloneのサイト設定から行うことができます。

またfactoryタイプの情報に含まれるすべての要素はアトリビュートを生成することで上書きできます。例えばcontent_iconの設定を上書きするためにはcontent_iconというアトリビュートを作成すればよいわけです。

class SomeProduct(BaseContent):
    """Some product"""
    content_icon = "some_icon.gif"

デフォルトメソッドを上書きする

デフォルトメソッドのいくつかを上書きするオプションについては前述しましたが、ここではフィールドの編集のされ方を操作する、より高度なオプションについて述べます。

アトリビュートやフィールドにはアクセサやミューテータを用いてアクセスすることができます。いくつかのデフォルトメソッドは利用可能な状態になっ ています。例えばblurbというフィールドがあれば、getBlurbメソッドやsetBlurbメソッドを用いることでアクセスすることができます。

しかし、実際にはもう少し別の何かをしたいという場合もあるでしょう。例えば会社の名前のスペルをチェックするためにフィールドの値をフィルタした い、とか、あるフィールドが変更されたときに他のフィールドの値も同時に変更したいなどということがあるかもしれません。こういったことはデフォルトメ ソッドを上書きすることで実現することができます。以下の例ではgetSpecialBlurbメソッドを作成します。これは入力されたblurbを操作 した後にクライアントに返すようにするメソッドです。具体的には「Perl」というテキストを「Python」にします。

class Article(BaseContent):
    def getSpecialBlurb(self):
        """The view for an article"""
        blurb = self.getField('blurb').get(self)
        blurb = blurb.replace('Perl', 'Python')
        return blurb

実際には、フィールドがこのメソッドを使うように変更する必要もあります:

StringField('blurb',
    searchable=1,
    widget=TextAreaWidget(),
    accessor="getSpecialBlurb",
)

この例では「表示」や「編集」ページにおいてblurbフィールドがアクセスされるときは常にgetSpecialBlurbの値が返されるように なります。Archetypesにはaccessorパラメータによってこのメソッド名が渡されているので、Archetypesはこのメソッドにアクセ スすべきであることを知っています。ただしここにはちょっとトリッキーな方法も含まれています。アトリビュートの生の値にアクセスするためにはこのフィー ルドを得る必要があるので、getメソッドを使っています(「blurb = self.getField('blurb').get(self)」の部分)。フィールドをgetしてメソッドを呼ぶのはArchetypesでは非常 によくあるやり方です。