Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / Tips / Dexterity QuickStart

Dexterity QuickStart

Dexterity 是 Plone 的內容型別建置架構,它在 Plone 5 之後,取代 Archetypes 的功能和角色,成為預設的型別建置工具。

從功能角色來看,Dexterity 和 Archetypes 一樣,都是 Plone (CMF 應用程式) 的內容型別建置架構,差別在於 Dexterity 擁有更多擴充彈性,效率更高,而且更加輕量化。從 Plone 4.2 開始,啟用 Dexterity 模組的門檻大幅降低,David Glick 參與早期開發,在 Plone 4.3 之際,Dexterity 2.0.x 成為預設安裝的模組,可以滿足多數的實務應用。在 Plone 5 之際,Archetypes 的地位變成選項模組,Dexterity 成為建置內容型別的預設工具。詳細資訊可參考 Developer Manual 文件。

Plone 版本及預設套件列表:

Plone plone.dexterity plone.app.dexterity plone.supermodel
3.x
4.2.4           1.1.2              1.2.1
4.3.0
4.3.1           2.1.3              2.0.8
4.3.2           2.1.3              2.0.9
4.3.3           2.2.1              2.0.11
4.3.7           2.2.4              2.0.16
4.3.10          2.2.7              2.0.18
5.0.2           2.3.7              2.1.17
5.0.5           2.4.2              2.3.1            1.3.0
5.1.11 2.5.5 2.4.8 1.3.4

plone.dexterity 2.0+ only has a "soft dependency" on zope.app.content, and your build does not have it installed. Your add-on can have the desired interface provided on content type interfaces if you add zope.app.content to your setup.py install_requires and pin a version (3.5.1) in your buildout. Should you do this, all your content type schema/interface classes will provide IContentType.

Plone 4.1Plone 4.2 想要搭配 Dexterity 2.0.x 的話,必須自行維護 KGS 和 backport。

Get Started

Plone 4.3.6 之後的環境,建議利用 mr.bob 來建立程式碼骨架:

$ cd src
$ ../bin/mrbob -O my.pkg bobtemplates:plone_addon

Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered.

Answer with a question mark to display help.
Values in square brackets at the end of the questions show the default value if there is no answer.


--> What kind of package would you like to create? Choose between 'Basic', 'Dexterity', and 'Theme'. [Basic]: Dexterity

--> Content type name [Task]: MyType

--> Author's name [John Smith]:

--> Author's email [jsmith@gmail.com]:

--> Author's github username: jsmith

--> Package description [An add-on for Plone]:

--> Plone version [4.3.6]:


Generated file structure at /home/jsmith/Plone/zinstance/src/my.pkg
$ tree my.pkg
my.pkg/
├── CHANGES.rst
├── CONTRIBUTORS.rst
├── MANIFEST.in
├── README.rst
├── bootstrap-buildout.py
├── buildout.cfg
├── docs
│   ├── LICENSE.GPL
│   ├── LICENSE.rst
│   └── index.rst
├── setup.py
├── src
│   └── my
│       ├── __init__.py
│       └── pkg
│           ├── __init__.py
│           ├── browser
│           │   ├── __init__.py
│           │   ├── configure.zcml
│           │   ├── overrides
│           │   └── static
│           ├── configure.zcml
│           ├── interfaces.py
│           ├── locales
│           │   ├── my.pkg.pot
│           │   └── update.sh
│           ├── profiles
│           │   └── default
│           │       ├── browserlayer.xml
│           │       ├── metadata.xml
│           │       ├── mypkg_default.txt
│           │       ├── types
│           │       │   └── MyType.xml
│           │       └── types.xml
│           ├── setuphandlers.py
│           ├── testing.py
│           └── tests
│               ├── __init__.py
│               ├── robot
│               │   ├── test_example.robot
│               │   └── test_mytype.robot
│               ├── test_mytype.py
│               ├── test_robot.py
│               └── test_setup.py
└── travis.cfg

13 directories, 32 files

Tutorial in 15 Minutes

Todo App Demo

使用 Dexterity 客製系統 Rename Dexterity object (id) after copy (from stackexchange)

examples: collective.polls example.ploneconf09 collective.conference collective.answers dexterity-sample

interfaces.py 裡 def myfunc(): 之類的定義,如果沒加上註釋段落,會造成 IndentationError: expected an indented block 錯誤。

Tool Script to Get All Attributes

Dexterity doesn't really have any built-in support for storage mechanisms other than attribute storage

Item or Container

依照能否包含其他型別的條件,型別分成 Item 與 Container 兩種。在 portal_catalog 的 meta_type 索引值裡,Dexterity 型別會被記錄成 Dexterity Item 或 Dexterity Container

klass: Base Class 通常是 plone.dexterity.content Item 或 Container,可以用 MyContentType 之類的新型別,繼承 Base Class 來新增功能,再更新 klass 設定值。

Item 或 Container 轉換方法

每個內容型別至少含有一個 schema,預設的起始名稱是「未命名」(unnamed schema),它是一個 interface,加上 zope.schema 的 field,可以寫在 Python 程式碼裡,或是從 XML 載入。這個起始 schema 提供 IContentType 介面,如果呼叫 queryContentType() 函式,輸入 dexterity 物件,會傳回「未命名」的 schema,也就是說,這個 dexterity 物件將支援 schema 所定義的屬性值。Dexterity 提供基本類別程式碼,開發者很容易可以沿用。

icon_expr 新版使用 CSS 來顯示 Icon

WARNING Plone Deprecation Warning
An icon for the 'controlpanel/DropdownConfiguration' action is being added to the action icons tool.
The action icons tool has been deprecated and will be removed in Plone 5.
You should register action icons directly on the action now, using the 'icon_expr' setting.

Dexterity 可以透過網頁介面建立內容型別,稱為 TTW (Through The Web) 方式,設定結果可以用 XML 格式儲存,記錄在 Dexterity FTI (Factory Type Information) 物件型別裡。利用 FTI 能夠動態存取 behavior 屬性值的機制,我們可以調整既有的 schema 定義,包括選用新的 widget 設定值,在這樣的情況下,behavior 扮演 adapter 角色,視需要而被啟用。Archetypes 的 schema extender 也應用 adapter 機制,但無法使用 FTI 來動態調整定義值。

collective.ambidexterity: TTW editing of views, defaults, validators, vocabulary

plone.dexterity 2.2.3 security private ?

Learning By Example

example.conference provides sample codes to demo how a dexterity application works. Here are the steps to install what you need for the demo. Assume you start with the Plone 4.0.2 UnifiedInstaller.

First, check out example.conference:

$ cd zinstance/src
$ svn co http://svn.plone.org/svn/collective/example.conference/trunk example.conference

Second, edit your buildout.cfg file:

[buildout]
extends =
    http://good-py.appspot.com/release/dexterity/1.0-next?plone=4.0.2

eggs =
    example.conference

develop =
    src/example.conference

Note that example.conference is not necessary for the zcml directive. The setup.py file of example.conference package includes plone.app.dexterity as a dependency and specify the package as a z3c.autoinclude plug-in. This feature is enabled in Plone 3.3 and later, that ensures no need to load its ZCML separately once the package is configured in buildout.cfg.

其他範例: collective.timeline collective.wfcomment apyb.conference clkss.content collective.pece

如果新增成功,但顯示時出現 This page does not seem to exist... 錯誤,可從 browser 程式碼檢查起。如果 XML 檔案有更動,記得要重新安裝啟用才能生效。

如果遇到 ExpatError: types/MyType.xml: mismatched tag 可從 <element ... /> 檢查是否有設定值少了 / 符號。

Schema Interface

基本上,Dexterity 的 Schema 是繼承自 zope.interface.Interface 的 Interface,附上 Field 定義值,標準的 Field 可由 zope.schema 取得,像 RichText、BlobFile、Relation 之類的 Field,就要另外載入模組來取得。

實務上,我們可能會使用 plone.supermodel.model.Schema 的 Marker Interface,方便日後加上 plone.autoform 的 Form Hint 功能,像 form.fieldset()、form.widget、form.omit 都是常見的應用。

FTI (Factory Type Information)

Type 有了 Schema 定義後,接著要準備註冊資訊,讓系統知道怎樣安裝它,我們利用 GenericSetup 來完成這步驟。

在 profiles/default/types.xml 檔案裡,找得到 FTI 註冊資訊:

<object name="portal_types">
 <object name="my.proj.mytype" meta_type="Dexterity FTI" />
</object>

然後,要在 profiles/default/types 目錄裡新增 Type 的 XML 檔案,它的檔名要和上述名稱一致,例如 my.proj.mytype.xml。在 @@dexterity-types 管理介面,有 Export Type Profiles 和 Export Schema Models 兩個匯出按鈕,前者會下載 dexterity_export-yyyymmddhhmmss.zip 檔案,內含 types.xml 和 types/my-type.xml 定義檔,後者會下載 my-type.xml 欄位定義內容,在 dexterity-types/my-type/@@fields 管理介面,可以複製貼上匯出的欄位定義內容。

condition_expr="not:object/@@plone_lock_info/is_locked_for_current_user|python:True"

刪除 FTI 跟 Class 並不相同。其他注意事項,可以參考 Dexterity Developer Manual

Plone uses only utf-8; dexterity content types don't have getCharset Dexterity Object as Field of Another Dexterity Object

plone.dexterity (CMF) defines the FTI and content classes, provides basic views (with forms based on z3c.form), handles security and so on. It also provides components to orchestrate the various functionality provided by the packages above in order to bring the Dexterity system together.

plone.directives.dexterity (CMF) adds convention-over-configuration support for Dexterity content and add/edit forms.

plone.app.dexterity (Plone) contains all Plone-specific aspects of Dexterity, including Ploneish UI components, behaviours and defaults.

IField: The default z3c.form data manager is z3c.form.datamanager.AttributeField which just does setattr and getattr on the (possibly adapted) context. If you want the field's get/set to be used, you'll either have to write a custom data manager for your field, or use a custom content item class that uses property descriptors.

Dexterity's getattr knows how to look up field defaults, but it doesn't know anything about widgets

Asko Soukka: Dexterity content import did not work outside site-root or Archetypes-container due to a few missing adapters agains GenericSetup content import framework. That should now be fixed for plone.app.contenttypes in c.themesitesetup 0.13.0 with extras.zcml enabled (described in README).

How did you create your export? You can create working export by:

1. Create a new site, with p.a.contenttypes and your example content
2. Upload your theme
3. Go to /Plone/++theme++example/@@export-site-setup
    where "Plone" is your site and "++theme++example" your theme
4. Click export (it defaults to content export)

That was enough to export importable default Plone site content (including Folders, Documents and Collections).

Archetypes Folder Comparison

表單設定值

Form Configuration with Schema Hints using Directives

Model

XML

MultiSelect Field

<field name="links" type="zope.schema.List">
  <title>Related Items</title>
  <value_type type="zope.schema.Choice">
    <title>Related</title>
    <source>plone.supermodel.tests.dummy_binder</source>
  </value_type>
</field>

Fields and Widgets

plone.autoform 和 plone.supermodel 都有 Directive 參數值,用來控制 z3c.form 的細節,要留意是否繼承 plone.supermodel.model.Schema 定義:

from plone.supermodel import model

class ISampleSchema(model.Schema):

plone.dexterity/browser/view.py 定義了 Dexterity 預設 DefaultView 類別,它繼承了 plone.autoform/view.py 裡的 WidgetsView 類別,會主動顯示所有的 Widget,執行過程的 ++widget++ 會呼叫 update() 而不是 __call__()。參考 plone.dexterity/tests/test_views.py 的 TestDefaultView。

DefaultView 提供 Dexterity 的專有屬性值,像是 view.w 顯示所有 Widget,view.widgets 包含預設 FieldSet,view.groups 包含所有 FieldSet。

plone.app.blocks/layoutbehavior.py 的 ContentLayoutView 是使用 DefaultView 的範例。

Content Tree Popup Conflicts with Autocomplete

自訂 Custom View 和 Display Form 應用技巧 trouble viewform add CSS

text index

Multi-Valued Attributes

Form Field Multiple-Choice Created from Parent Not Initialized on Object Creation

欄位移動位置 欄位值複製 Copy / Duplicate field values 移動到不同 Schema

SuperModel

SuperModel XML based Configuration Support: collective.themefragments

Relation

使用 IIntIds 來取代 Archetypes 的 getRelatedItems index

Rendering Related Items

template display RelationList ObjPathSourceBinder MultiContentTreeFieldWidget

單一關聯的處理範例

避免關聯關鍵字被移除

named relations

Supermodel, Dexterity Content Types, Named Relations between Content Types: title reference

Replace Field in plone.supermodel Schema 優先利用 plone.app.widgets 才是正解

從 ZMI 手動刪除被關聯的項目會造成 KeyError 透過 catalog.clear() 可以處理

ComponentLookupError: InterfaceClass zc.relation.interfaces.ICatalog

Compute Field Relation Behavior

Schemata vs Groups / Fieldset

FIELDSETS_KEY 是舊方法而且無法應付超過一組 Fieldset 情況 Moving Fields between Fieldsets

from plone.supermodel import model
organizer = schema.TextLine()
model.fieldset('ownership', label=_('label_schema_ownership', default=u'Ownership'), fields=['organizer'],)

tabbed fieldset, also known as groups

SchemaEditor and XML Definition plone.schemaeditor 是搭配 zope.schema 的編輯器,目前是 dexterity 編輯 schema 的核心模組。

Combine Model-driven Types with Schema-driven Types

Fieldset Order Behavior Fieldset Order

Hiding Description Field Computed Field

Permission

The Dexterity control panel is protected by a "Manage schemata" permission. It defaults to Managers only, but you can easily assign it to other roles.  In fact I should probably change it to include Site Administrators by default.

欄位權限

Reference

Comparison with Archetypes. 移植結果的比較 More examples includes:

Set the Local Site Manager for Debug Session

http://www.martinaspeli.net/articles/dexterity

Metadata Migration

transmogrify.dexterity: transmogrifier blueprint for updating dexterity objects

Related Item Back Reference Dexterity References

AddForm EditForm DisplayForm

Custom Add / Edit Form: DefaultAddForm DefaultAddView

reorder the form widgets via update() method Custom Behavior on Save

getMultiadapter Error with Custom EditForm

note: Dexterity views and __init__ method

In general you shouldn't do anything in the __init__ of a view, but instead do everything in the __call__ method. This applies to all views, not just Dexterity views. When the __init__ is called, you don't have a security context set up yet, so there's certain things that won't work. It's much easier to just not do anything there, instead of debugging such problems.

從 Plone 4.1 (Dexterity 1.1) Dexterity 應該都有 UUID

理論上,讓 Content Type 都具備 Folderish 特性,會是好主意,但實務上有 Versioning 和效能的問題要處理,目前有移植方案

Can Not Be Imported via GenericSetup: collective.cover

Dexterity 預設不需要 Archetypes 的 Accessor 或 Mutator

Plone 4.1 併用 Archetypes 和 Dexterity 的注意事項

利用 Behavior 可以建立 Field 並提供再利用效果

Dexterity's custom __providedBy__ implementation caches the behavior marker interfaces by portal_type

註冊 Viewlet

註冊多個 View

notes on Dexterity development

custom Edit Form

must not acquisition-wrap an objects before calling addContentToContainer

Round-tripping with TTW

View Can Not Be Re-registered After Enabling Marker Interface

Ploneboard + TinyMCE JavaScript Code is not included

欄位數量未定的表單

API Proposal Discussion

Custom Form

Custom Add Form Needs No Context

Edit Form Button

Example: imio.urbdial.notarydivision

storage strategy (attribute storage only)

Testing

Test Custom Item Constructor

Creation with File Image RichText

Validation

Detect Type On Adding Item MultiSelection / MultiValue Field with Input from Vocabulary

if INewsItem.providedBy(self.context) or (IAddForm.providedBy(self.view) and self.view.portal_type == 'News Item'):

Validating Data before Programmatic Creation

newInstance = createContentInContainer(folder, id, **schemavalues)
errors = getValidationErrors(IMyType, newInstance)
if errors:
    # Schema not validated; errors is a sequence of (field, exception) tuples
    # field is None if the error is for an invariant.

Invariant Between Behaviors

# ZCML
<adapter factory=".behaviors.MyValidator" />

# Py
from z3c.form import validator
from zope.interface import Invalid

class MyValidator(validator.SimpleFieldValidator):

    def validate(self, value):
        fieldsets = dict([(group.__name__, group)
                          for group in self.widget.form.parentForm.groups])
        other_value = fieldsets['myfieldset'].widgets['otherfield'].extract()
        # ...
        raise Invalid('Validation error')

validator.WidgetValidatorDiscriminators(MyValidator, field=IMyBehavior['myfield'])

更新 RichText 內容

from Testing import makerequest
root = makerequest.makerequest(app)
site = root.mysite
folder = site.myfolder['subfolder']

admin = root.acl_users.getUserById('admin')
admin = admin.__of__(site.acl_users)

from AccessControl.SecurityManagement import newSecurityManager
newSecurityManager(None, admin)

from zope.site.hooks import setHooks
from zope.site.hooks import setSite
setHooks()
setSite(site)
site.setupCurrentSkin(site.REQUEST)

import csv
from plone.app.textfield.value import RichTextValue
import transaction

with open('myfile.csv', 'rb') as f:
    dialect = csv.Sniffer().sniff(f.read(), delimiters=';') # f.read(1024) may fail
    f.seek(0)
    reader = csv.reader(f, dialect)
    for row in reader:
        item = folder[row[0]]
        item.text = RichTextValue(row[1].decode('utf-8'), 'text/html', 'text/x-html-safe', 'utf-8')
# UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 68: ordinal not in range(128)
 item.reindexObject() transaction.commit()

programmatic creation: CSV example Setting Field Defaults Unauthorized, checkConstraints DateTime

folder = mysite.events
import datetime
from plone.dexterity.utils import createContentInContainer
item = createContentInContainer(folder, 'Event', title=u"Bar")
item.start = datetime.datetime(2013, 10, 10, 10, 0)
item.end = datetime.datetime(2013, 10, 15, 12, 0)

使用 bin/plonectl run myscript.py 方式新增型別時,要注意 Id 決定方式,如果 Title 決定 Id 而且 Title 有重覆時,就會產生 The id "some-title" is invalid - it is already in use. 錯誤訊息。

readonly field

edit short name

plone.alterego: 動態衍生新的型別

Event Handler IObjectAddedEvent

Moving Dexterity Fields to Different Schema

Related content
Behaviors
Versioning