中文 (Chinese)English

概念

KeystoneJS 需要 MongoDB v2.4 或更高版本。

在KeystoneJS中,数据模式和模型是由列表控制的,并且数据库中的文档经常被称为条目

要定义数据模型,你要创建一个new keystone.List,然后传给它列表参数

然后向列表中add域。在底层,Keystone List会创建一个mongoose模式,并为你添加的域给它定义恰当的路径。

你可以访问schema,插入其它mongoose功能,比如虚拟域、方法和前置/后置钩子。

完成列表的设置后,调用list.register()初始化它,并用Keystone注册。

查询数据用list.model (这是一个mongoose 模型)。

列表条目是mongoose 文档。用new list.model()创建新条目,当准备好保存它时(或者将修改保存到已有条目上)调用item.save()

列表

用法

new keystone.List(key[, options]);

创建Keystone列表的语法跟创建Mongoose模式的语法非常像,只是构造器不一样,它是var MyList = new keystone.List(key, options)

新列表创建好以后,可以用MyList.add(fields)添加域,域是一个包含键(域的路径)和值(域的类型或参数)的对象。

域是由带有type属性的对象定义的,这个type必须是有效的域类型或基本的数据类型。你可以用对象语法为域指定额外的参数。常用的域参数和域类型特定的参数在域文档中有详细介绍。

列表上的所有域和参数都设好之后,调用MyList.register()用Keystone注册列表,并最终确定它的配置。

例子

一个用于博客的简单Post模型可能会像下面这个:

Post.js

var keystone = require('keystone'),
    Types = keystone.Field.Types;
 
var Post = new keystone.List('Post', {
    autokey: { path: 'slug', from: 'title', unique: true },
    map: { name: 'title' },
    defaultSort: '-createdAt'
});
 
Post.add({
    title: { type: String, required: true },
    state: { type: Types.Select, options: 'draft, published, archived', default: 'draft' },
    author: { type: Types.Relationship, ref: 'User' },
    createdAt: { type: Date, default: Date.now },
    publishedAt: Date,
    image: { type: Types.CloudinaryImage },
    content: {
        brief: { type: Types.Html, wysiwyg: true, height: 150 },
        extended: { type: Types.Html, wysiwyg: true, height: 400 }
    }
});
 
Post.defaultColumns = 'title, state|20%, author, publishedAt|15%'
Post.register();

这个例子使用了可选的mapautokeydefaultSort参数,描述见下文。

它还指定titlestateauthorpublishedAt作为管理界面中默认显示的列,state和publishedAt还给定了列宽度。

author域是PostUser模型的关系,在入门指南中介绍过。

参数

列表支持以下参数:

label String 列表在管理界面中用的标签。默认为key的友善格式。
path String 列表在管理界面中的路径。默认为key的缩略名格式。
singular String 列表中条目的单数标签。用在管理界面中,默认为label的单数格式。
plural String 列表中条目的复数标签。用在管理界面中,默认为label的复数格式。
schema String

列表的Mongoose模式的参数。 除了其他方面外,这个参数可以给集合指定一个定制的名称。参见mongoose模式的文档查看可用参数的清单。

警告: 不要修改模式参数id_id;这是Keystone要求的默认行为。

drilldown String 以空格分隔的关系清单,在管理界面中显示为下钻项。
sortable Boolean 给模式添加一个隐藏的sortOrder域,并在管理界面中启用拖拽式的排序。
sortContext String 管理界面中有拖拽式排序时用来控制的List:relationship对。
searchFields String 在管理界面中用于搜索的路径清单,用空格分隔。
defaultSort String 管理界面中用于排序的默认列或路径。
defaultColumns String 在管理界面的列表视图中默认显示的列清单,用逗号分隔。 你可以在管道符|后面用像素或百分比指定宽度。
map Object 将域映射到特定列表路径上的对象。如果添加了带那个键的域,则每个路径都默认为它的键。映射路径包括
  • name - 包含条目名称的域,显示在管理界面中
autokey Object 在列表上添加一个插件,文档保存时自动基于另一个域或路径为它生成一个键。这个参数的值应该是带有如下键的对象:
  • from String - 用于生成键值的域或路径,可以是用空格分开的域清单
  • path String - 存储键的路径
  • unique Boolean - 键是否应该有唯一性
  • fixed Boolean - 如果键存在并且非空,应该保留。默认为false
路径Autokey是自动索引的;你可能还想把它放在复合索引中。
track Boolean or Object

在列表上添加一个插件,追踪谁在什么时候(比如哪个Keystone用户)创建和最后修改了一个条目。

设为true时,所有追踪域都是用它们的默认名称启用的。

你也可以选择性地启用各个域,并且还可以通过将track设为一个带有下面任一或全部域的object来指定一个定制的域名:

  • createdAt Boolean/String - 设为true时,追踪条目何时创建(使用默认的域名createdAt)。要使用定制的域名,用想要的名称设置String。默认为false
  • createdBy Boolean/String -设为true时,追踪哪个用户创建了条目(使用默认的域名createdBy)。要使用定制的域名,用想要的名称设置String。默认为false
  • updatedAt Boolean/String - 设为true时,追踪条目的最后更新时间(使用默认的域名updatedAt)。要使用定制的域名,用想要的名称设置String。默认为false
  • updatedBy Boolean/String - 设为true时,追踪是哪个用户最后更新了条目(使用默认的域名updatedBy)。要使用定制的域名,用想要的名称设置String。默认为false

createdByupdatedBy域只有通过Keystone管理界面添加/修改条目时才能自动更新。然而,如果你想在自己的程序内添加/修改条目,则必须在保存条目之前手工将条目的._req_user属性设为当前登录的用户(req.user),如下例所示。

var item = new List.model();
item.set({ field1: 'value1', field2: 'value2' });
item._req_user = req.user;
item.save();
noedit Boolean 禁止在Keystone管理界面中编辑列表中的条目。
nocreate Boolean 禁止在Keystone管理界面中创建列表的新条目。
nodelete Boolean 禁止在Keystone管理界面中删除列表中的条目。
hidden Boolean 在Keystone管理界面中隐藏这个列表。

如果你想知道如何控制被分类列表在管理界面中的导航区域,请查看KeystoneJS配置文档中的nav参数。

下钻示例

下钻参数是提高管理界面可用性的好办法,为用户当前正在编辑的条目提供上下文。

默认情况下,下钻只会显示该条目所属的列表。

然而你可以将它设为模式中的Relationship域,它就会显示当前存储在那个关系域中的条目。

如果有几个要在下钻清单中显示的相关关系,可以用空格把它们分开。

例子:在帖子的下钻中包含作者

var Post = new keystone.List('Post', {
    autokey: { path: 'slug', from: 'title', unique: true },
    map: { name: 'title' },
    defaultSort: '-createdAt',
    drilldown: 'author' // 在上面的例子中,author被定义为一个Relationship域
});

模式插件

你可以用schema指定列表虚拟域方法静态域前置后置钩子。你也可以用插件网站上的mongoose插件

比如说,在上面的Post列表中,我们可能想在state被改成published后自动设定publishedAt的值(但仅在它还没被设定过时)。

我们可能还想添加个方法用来检查Post是否发布了,而不是直接检查state域的值。

在调用Post.register()之前,我们先加上下面这段代码:

Post.schema.methods.isPublished = function() {
    return this.state == 'published';
}
 
Post.schema.pre('save', function(next) {
    if (this.isModified('state') && this.isPublished() && !this.publishedAt) {
        this.publishedAt = new Date();
    }
    next();
});

查询数据

要查询数据,你可以在list.model上使用任何mongoose查询方法。

比如说:要加载最新5条状态为publishedposts,并组装可链接的author,按发布日期的降序排列:

加载Posts

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.find()
    .where('state', 'published')
    .populate('author')
    .sort('-publishedAt')
    .limit(5)
    .exec(function(err, posts) {
        // 对帖子做些处理
    });

Promises

还有一种处理mongoose查询中的事件的方法。除了传给exec方法一个回调函数,我们还可以用它的返回结果:Promise。对于带有错误传播的整洁事件链,Promise非常实用。

比如说:加载100条 posts,然后异步做些处理,然后再对其结果做些处理:

加载Posts,异步做些处理,然后再做些处理:

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.find()
    .limit(100)
    .exec()
    .then(function (posts) { //第一个promise得到满足
        //返回另一个异步promise
    }, function (err) { //第一个promise被驳回
        throw err;
    }).then(function (result) { //第二个promise得到满足
        //对最终结果做些处理
    }, function (err) { //除了某些问题
        //捕获错误,它可能是链中的任何promise抛出的
        console.log(err);
    });

分页查询

你可以用List.paginate()进行分页查询,它会像List.model.find()一样返回一个查询对象,该方法支持下面这些参数:

  • page - 从哪一页开始
  • perPage - 每页的记录条数
  • maxPages - 可选参数,在计算页面时从开始/中间/末尾处省略页数(如果你有很多页,并且不想让它们显示时占好几行,就可以用这个参数)。

比如说: 要以maxPages 10 和perPage 10加载posts,并且要求其状态为published,同时组装出关联的authorcategories,按发布时间的逆序排列:

分页加载Post

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
 Post.paginate({
		page: req.query.page || 1,
		perPage: 10,
		maxPages: 10
	})
	.where('state', 'published')
	.sort('-publishedDate')
	.populate('author categories')
	.exec(function(err, results) {
		locals.data.posts = results;
		next(err);
	});

当你在分页查询上调用exec时,除了查询结果,它还会返回很多元数据:

  • total: 匹配结果的总数(不仅仅是这一页的)
  • results: 这一页的结果集数组
  • currentPage: 当前页的页码
  • totalPages: 总页数
  • pages: 要显示的页码的数组
  • previous: 前一页的页码,如果当前页是第一页则为false
  • next: 下一页的页码,如果当前页是最后一页则为false
  • first: 包含第一条结果的页码
  • last: 包含最后一条结果的页码

创建条目

要创建新条目,还是用mongoose模型

创建Post

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
var newPost = new Post.model({
    title: 'New Post'
});
 
if (shouldBePublished) {
    newPost.state = 'published';
}
 
newPost.save(function(err) {
    // post已保存	
});

自动键

因为我们在Post列表中设定了autokey参数,它会在post被保存到数据库中之前基于title生成一个唯一的键。

newPost.slug == 'new-post';

删除条目

要删除条目,首先加载那项数据,然后用它的remove方法:

删除一个Post

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.findById(postId)
    .remove(function(err) {
        // post已删除
    });

标题

定义标题以区分文档流程。标题可以定义为 String 或者 Object 以及 依赖于 其他域的值。

Person.add(
   'User', 
   { name: { type: Types.Name, required: true, index: true, initial: true } }, 
   'Permissions', 
   { isAdmin: { type: Boolean, label: 'Can access Keystone', index: true } },
   // header object
   { heading: 'Activities' }, 
   { place: { type: Types.Select, options: ['GT', 'UGA'] } },
   // header with dependsOn
   { heading: "GT Activities", dependsOn: { place: 'GT' } },
   { type: { type: Types.Select, options: ['ZC', 'MP'], dependsOn: { place: 'GT'} }
);
参数

heading String - 标题显示的文字

dependsOn Object - 标题只会在当前对象指定路径和数据项匹配时候显示。 dependsOn

在向列表中添加时,你既可以指定基本数据类型,也可以用Keystone域类型。

概述

你可以用Keystone域轻松地向程序的模型中添加丰富的、功能化的域。它们不仅可以描述数据的结构,还可以描述数据的意图。它们提供了:

  • Keystone管理界面中的丰富控件
  • 复杂的数据类型;比如存储几个字符串和GeoJSON经纬度的location
  • 格式化和验证方法
  • 额外的虚拟属性;比如name提供了一个虚拟的name.full,将实际存储的name.firstname.last合并起来。
  • Underscore方法;比如password域提供了一个password.compare方法, 用于比较加密的哈希值
  • 域之间彼此关系如何的元数据;比如哪个域依赖于其它域中的某个值

基本数据类型跟Keystone的域类型是一一对应的:

数据类型 域类型
String Text
Number Number
Date DateTime
Boolean Boolean

域参数

所有域类型都支持几个通用的参数,可以指定数据库设置(比如indexdefault),或者为Keystone的管理界面提供信息(比如 label)。

域在对象内部可以嵌入,就像在mongoose模式中一样。

所有的mongoose模式类型参数 都是传给mongoose模式的,所以你也可以用mongoose支持的任何参数。

常用的域参数有:

label String 每个域的标签都是由域路径生成的;设置这个参数会覆盖默认值。
required Boolean 在条目保存前验证这个域是有值的(还会传给mongoose并强制使用数据库索引)。
initial Boolean 让这个域出现在管理界面中的创建条目表单中。
noedit Boolean 在管理界面中将这个域渲染为只读域。
note String 在管理界面中跟着域显示。
hidden Boolean 如果设为true,则该域在管理界面中一直是隐藏域。

条件域

为了提高管理界面的可用性,可以在某些域没有值时隐藏它们,或者根据其它域的取值隐藏。

collapse Boolean 该域没有值时在管理界面中显示一个+ 添加链接。当noedit也被设为true时,该域没值时则完全隐藏。
dependsOn Object

只有对象中指定的路径跟条目的当前数据匹配时才会显示

你可以在每个路径上用数组对准多个值。

示例

first: { type: String },
// 如果first === "value1", "1" 或 "2" 时将会显示
second: { type: String, dependsOn: { first: ['value1', '1', 2] } },
// 如果 first == "value1" 时会显示
third: { type: String, dependsOn: { first: 'value1' } }

生成的值及观测域

Keystone的域可以用简单的语法配置动态更新的域。你可以将一个域设置为在下面这些情况下更新它的值:

  • 条目保存时
  • 任何其它域的值发生变化时
  • 任何其它域的值变成特定值时

要使用观测功能,设置下面两个参数:

watch BooleanStringObjectFunction

true时,每次保存条目都会重新计算这个域的值。

提供一个用空格分隔的路径清单,其中任何一个发生变化时就重新计算这个域的值。
比如: 'author title state'

提供一个键/值对的对象,其中任何一个路径变成指定的值时就重新计算这个域的值。
比如: {'state': 'published', 'mainPost': true}

提供一个可以在需要时返回true/false的函数。
比如: function() { return this.author === this.editor; }

value Function

当所观测的路径发生变化时生成这个域的值的函数。必须返回新的值。

这个函数的this上下文是被保存的那个条目。

比如:

function () {
    return this.total<=this.totalreceived ? true:false;
}

Underscore方法

有些域类型包含辅助的underscore方法,在条目的域路径前面放一个下划线。

比如: 像下面这样用上面那个Posts列表中的createdAt DateTime域的underscore方法format

var keystone = require('keystone'),
    Post = keystone.list('Post');
 
Post.model.findById(postId).exec(function(err, post) {
   console.log(post._.createdAt.format('Do MMMM YYYY')); // 2013年8月25号
});

关系

Keystone加强了MongoDB在域中存储相关文档的ObjectID的能力 (或者在数组中很多的ObjectID),支持Relationship域和模型中的定义。

关系域

ObjectIdArray — 在管理界面中显示为带有自动提示功能的输入域

将对另一个模型的ObjectID引用存到一个ObjectID域或数组中创建一对多或多对多关系。

ref参数指定相关模型。对于多对多关系而言,将many参数设为true

比如说,如果你想将Post模型链到一个Author和多个PostCategories上时,可以这样做:

Post.add({
    author: { type: Types.Relationship, ref: 'User' },
    categories: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
关系过滤器

你可以用filters参数过滤关系域。

filters参数是一个键/值对对象,其中键对应要过滤的相关模型的域,其中的值或者是字面值,或者是当前模型的域名,值会用来过滤关系。

在下面的例子中,author域只允许选择group域等于'admin'的User

Post.add({
    title: { type: String, required: true },
    category: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
    author: { type: Types.Relationship, ref: 'User', filters: { group: 'admin' } }
});

你也可以用模型中其它域的值过滤。将过滤器的值设为那个域的名称,前面加上分号(:)。

在下例中,author域只能选择group域跟Post模型的category域的值相等的User

Post.add({
    title: { type: String, required: true },
    category: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
    author: { type: Types.Relationship, ref: 'User', filters: { group: ':category' } }
});

最后,你还可以用当前模型的_id域过滤。

在下例中,bestPost域只能选择author域等于当前文档的_idPost

User.add({
    name: { type: String, required: true },
    group: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
    bestPost: { type: Types.Relationship, ref: 'Post', filters: { author: ':_id' } }
});

你只能在一对多关系(即当many参数不为true时)中设置过滤器。

在查询中组装相关数据

感谢Mongoose的组装功能,你可以为关系域组装相关数据。想在加载上面例子中的Post时组装author和category文档,应该这样做:

Post.model.findOne().populate('author categories').exec(function(err, post) {
    // author是组装完整的User文档
    console.log(post.author.name);
});

注意,如果没存ObjectId,或者保存的ObjectId无效(比如文档被删了),上例中的author将变为undefined

关系定义

在上面的例子中,如果你想看到每位Author的Post清单该怎么做?因为关系域在Post上,你需要告诉Author(和PostCategory)模型它是被引用的。这样管理界面就可以从两个方向呈现这一关系。

你可以像下面这样在Model上调用relationship

User.relationship({ path: 'posts', ref: 'Post', refPath: 'author' });
参数

path String - 模型上的关系引用路径

ref String - 引用方模型的键 (有关系域的那一方)

refPath String - 引用方模型上的关系引用路径

如你所见,提供给relationship方法的这些参数是对关系域的镜像。

关系定义是可选的;如果你不定义,只是关系的另一侧不会在管理界面中显示这一关系。关系域仍能如期工作。

加载相关条目

过滤一对多的相关条目很容易;只需要像其它任何值一样指定你希望过滤的条目的ID:

Post.model.find().where('author', author.id).exec(function(err, posts) {
    // ...
});

要过滤多对多的相关条目,用条件in,并用数组指定一个(或多个)ID:

Post.model.find().where('categories').in([category.id]).exec(function(err, posts) {
    // ...
});

域类型

Boolean

Boolean — 在管理界面中显示为一个检查框

{ type: Types.Boolean }

Text

String — 在管理界面中显示为一个文本控件

{ type: Types.Text }

Textarea

String — 在管理界面中显示为一个文本框

{ type: Types.Textarea }
参数

height Number - 文本框的高度(以像素为单位)

Email

String — 在管理界面中显示为一个文本控件

输入看起来必须像一个有效的email地址(如果不是必填项,可以为空)

{ type: Types.Email, displayGravatar: true }
参数

displayGravatar Boolean - 是否在管理界面中显示gravatar图片

Underscore方法:

gravatarUrl(input, size, defaultImage, rating) - 生成一个gravatar图片的请求url

item.email = "demo@keystonejs.com";
item._.email.gravatarUrl(); // "//www.gravatar.com/avatar/74a0071e5f3a7107b570b7d4a1a7619d?s=80&d=identicon&r=g"
item._.email.gravatarUrl(200,'mm','r'); // "//www.gravatar.com/avatar/74a0071e5f3a7107b570b7d4a1a7619d?s=200&d=mm&r=r"

Url

String — 在管理界面中显示为一个文本控件

{ type: Types.Url }
Underscore方法:

format() - 去掉存储的值前面的协议(如果有的话)

item.url = "http://keystonejs.com";
item._.url.format(); // "keystonejs.com"

Html

String — 在管理界面中显示为一个文本控件或WYSIWYG编辑器。

{ type: Types.Html, wysiwyg: true }
参数

wysiwyg Boolean - 在管理界面中是否显示WYSIWYG编辑器 - 要定制编辑器请参见管理界面参数

height Number - 输入域的高度(以像素为单位)

参见管理界面参数了解用于定制WYSIWYG编辑器的全局配置参数。

Color

Color — 显示为带有取色器的文本控件

{ type: Types.Color }

Date

Date — 在管理界面中显示为日期选择控件

输入或者是有效的Date,或者是格式为YYYY-MM-DD的字符串 (除非为必填项,否则可以为空)

要将Date域设为当前时间,需将default参数设为Date.now

{ type: Types.Date }
参数

format string - 默认的格式模式,默认为Do MMM YYYY

参见momentjs格式文档了解所支持的格式和参数。

Underscore方法

format(string) - 用momentjs格式化存储的值

moment() - 返回一个用该域的值初始化的momentjs对象

parse(input, format, ...) - 用momentjs解析input,将该域的值设为返回的moment对象

参见momentjs解析文档了解所支持的格式和parse方法的参数。

item.createdDate = Date.now();
item._.createdDate.format(); // 用默认的格式字符串返回当天的date
item._.createdDate.parse('2013-12-04'); // 用被解析的日期返回一个moment对象
item._.createdDate.format('YYYY-MM-DD'); // 返回'2013-12-04'

Datetime

Datetime — 在管理界面中显示为日期时间选择控件

输入或者是有效的Date,或者是格式为YYYY-MM-DD的字符串 (除非为必填项,否则可以为空)

要将Date域设为当前时间,需将default参数设为Date.now

{ type: Types.Datetime, default: Date.now }
参数:

format string - 默认的格式模式,默认为Do MMM YYYY hh:mm:ss a

参见momentjs格式文档了解所支持的格式和参数。

Underscore方法:

format(string) - 用momentjs格式化存储的值

moment() - 返回一个用该域的值初始化的momentjs对象

parse(input, format, ...) - 用momentjs解析input,将该域的值设为返回的moment对象

参见momentjs解析文档了解所支持的格式和parse方法的参数。

Key

String — 在管理界面中显示为一个文本输入域

自动将输入转为有效的键(没有空格或特殊字符)。用分隔符替换空格。

{ type: Types.Key }
参数

separator String - 用来替换输入中空格的分隔符;默认为-

Number

Number — 在管理界面中显示为一个数字输入控件

输入或者是有效的Number,或者是可以转换为数字的字符串(除非为必填项,否则可以为空)

{ type: Types.Number }
Underscore方法:

format(string) - 用numeraljs格式化存储的值。设为false可以禁用自动格式化。

格式化字符串默认为0,0[.][000000000000]

Money

Number — 在管理界面中显示为一个数字输入控件

输入或者是有效的Number,或者是可以转换为数字的字符串(可以有前置符号;除非为必填项,否则可以为空)。金额控件不能识别币种。

{ type: Types.Money }
Underscore方法:

format(string) - 用numeraljs格式化存储的值。设为false可以禁用自动格式化。

格式化字符串默认为$0,0.00

Select

StringNumber — 在管理界面中显示为选择输入控件

类似于其他框架中的 Enum.

{ type: Types.Select, options: 'first, second, third' }
参数

numeric Booleantrue时,会将该域的值存为Number而不是String

{ type: Types.Select, numeric: true, options: [{ value: 1, label: 'One' }, { value: 2, label: 'Two' }] }

emptyOption Booleanundefined || true时,会添加一个空白的选择项<select>作为输入控件中的第一项

{ type: Types.Select, required: true, options: 'first, second', emptyOption: false }

options StringArray - 选择输入控件的选择项

选择项的值可以是用逗号分隔的String值列表,其中的字符串会被分割为Array

对于选择项Array,每个选择项或者是

  • 表示选择项的valueStringlabel是自动生成的
  • 带有valuelabel String属性的Object

你可以在options Array中混合StringObject条目:

{ type: Types.Select, options: ['first', 'second', { value: 'third', label: 'The third one' }] }

Object选择项可以有额外的属性,可以在获取当前选择项的数据,或域参数时访问到。

{ type: Types.Select, options: [
    { value: 'first', label: 'The first option', custom: 'value' },
    { value: 'second', label: 'Second' }
]}
属性

ops Array - 输入控件的options数组

values Array - 全部option.value属性

labels Object - 全部option.label属性,以option.value为键

map Object - options的映射,以option.value为键

模式

当前选择项的值会存在{path}上。此外还会提供这些虚拟域:

pathLabel String - 当前选择的option的标签

pathData Object - 当前选择的option,包含所有定制的属性

pathOptions Array - 输入域的options数组

pathOptionsMap Object - options的映射,以option.value为键

Underscore方法:

pluck(property, default) - 返回当前选择的optionproperty 值,或者default。用于跟options的定制属性合并时。

MyList.add({ state: { type: Types.Select, options: 'draft, published, archived', default: 'draft' });
 
MyList.fields.state.values == 'draft,published,archived';
MyList.fields.state.labels == { draft: 'Draft', published: 'Published', archived: 'Archived' };
MyList.fields.state.ops == [
    { value: 'draft', label: 'Draft' },
    { value: 'published', label: 'Published' },
    { value: 'archived', label: 'Archived' }
];
MyList.fields.state.map == {
    draft: { value: 'draft', label: 'Draft' },
    published: { value: 'published', label: 'Published' },
    archived: { value: 'archived', label: 'Archived' }
};
 
var item = new MyList.model();
item.state == 'draft';
item.stateLabel == 'Draft';
item.stateData == { value: 'draft', label: 'Draft' };
item.stateOptions == MyList.fields.state.ops;
item.stateOptionsMap == MyList.fields.state.map;

Markdown

Object — 在管理界面中显示为一个文本框

{ type: Types.Markdown }
参数

height Number - 定义 markdown 编辑器高度;默认高度为 90.

{ type: Types.Markdown, height: 200 }

toolbarOptions Object - 可以定制工具栏。

toolbarOptions.hiddenButtons String - 要隐藏的按钮清单,用逗号分隔。

{ type: Types.Markdown, toolbarOptions: { hiddenButtons: 'H1,H6,Code' } }
模式

md属性发生变化时,markdown输入域会自动将markdown转换为html,这是通过md路径上的设定器完成的。

md String - markdown源文本

html String - 生成的html代码

Page.add({ content: Types.Markdown });
 
var page = new Page.model();
page.content.md = "# Hello World";
page.content.html == "<h1>Hello World</h1>";
 
// 或者...
 
Page.fields.content.updateItem(page, "* list item");
page.fields.content.format(page) == "<ul><li>list item</ul></li>";

Name

Object — 在管理界面中显示为firstname lastname输入控件

{ type: Types.Name }
模式

name域在模式上添加了firstlast String路径,还有虚拟域full的getter和setter。

first String - 姓氏

last String - 名字

虚拟域

full String - 姓和名,中间加空格合并(如果两个都有值)。

name.full setter在第一个空格处分割输入值。

Password

String — 在管理界面中显示为一个密码域,带有一个'修改'按钮。

密码是自动用bcrypt加密的,并且会输出一个方法用于把一个字符串跟加密的哈希值进行比较。

加密是添加在模式上的一个保存前钩子完成的,所以密码直到条目保存到数据库中时才会加密。

{ type: Types.Password }
参数

workFactor Number - 生成哈希值时用的加密因子,数值越大越慢,但也更安全(默认为10)

Underscore方法:

compare(candidate, callback) - 对候选值加密,并跟已加密的哈希值进行比较

  • candidate 要比较的String
  • callback(err, result) - 如果候选值跟保存的密码相配,则resulttrue,否则为false
特殊路径

{path}_compare - 提供给updateHandler时,它会对照{path}进行检查,如果不匹配则验证会失败。

Location

Object — 在管理界面中显示为一组输入控件的组合

包含一组标准的字符串,用于存储地址,带有2dsphere索引的经纬度。

还用谷歌的Places API提供输入自动补充功能 (需要提供谷歌地图API键,并且只能用于相应的谷歌服务条款)。

参见谷歌配置文档了解如何在KeystoneJS中设置谷歌地图的详细介绍。

{ type: Types.Location }

注意:模式的路径是基于澳大利亚的地址格式,并且应该更新为其它更加国际化的格式。如果你有如何进行国际化结构的反馈,请开一个工单。

模式

name String - 建筑的名称

number String - 单位或店铺号

street1 String - 街道地址

street2 String - 街道地址第2行

suburb String

state String

postcode String

country String

geo Array longitude, latitude

重要提示: 按照MongoDB的传统,geo数组的顺序必须是lng, lat,跟谷歌API所用的顺序相反。

Underscore方法:

googleLookup(region, update, callback) - 从存储的值中自动检测完整的地址和经纬度。

  • region 为了进行区域性偏移和过滤而传给Places API的String
  • update String传给"overwrite"会自动用结果覆盖已有数据。true会在这个域上用结果设定空白属性。
  • callback(err, location, result) - 传给解析过的location对象,以及来自谷歌的原始result

内部状态码模仿谷歌API的状态码。参见https://developers.google.com/maps/documentation/geocoding/了解详情。

使用谷歌Geocoding API要受到每天不超过2,500 geolocation请求的限制,除非有企业许可。

Geocoding API只能用于谷歌地图;geocoding结果必须在地图上显示。请确保你的Keystone程序遵守谷歌地图API的许可。

CloudinaryImage

Object — 在管理界面中显示为一个图片上传控件。

自动管理存在Cloudinary中的图片,包括上传、缩放和删除。

参见Cloudinary配置文档了解如何在KeystoneJS中设置Cloudinary的细节。

{ type: Types.CloudinaryImage }
参数

publicID String 用作Cloudinary图片 public_id域的名称。

{ type: Types.CloudinaryImage, publicID: 'slug' }

folder Stringcloudinary folders设为true时,用来为Cloudinary图片public_id指定文件夹/前缀

{ type: Types.CloudinaryImage, folder: 'path/to/image' }

如果你希望 Cloudinary 在 cloudinary folders 设置为 true 的时候自动创建文件夹, 确认已经在你的 Cloudinary 账户中启用 "Auto-create folders"。

autoCleanup Booleantrue时,会将Keystone的默认行为从remove (只是从数据库中去掉Cloudinary 图片)变成delete (会同时从数据库和Cloudinary存储中去掉图片)。此外,这个参数还会在上传时替换掉已有图片(如果图片已存在的话)。

{ type: Types.CloudinaryImage, autoCleanup : true }

select Booleantrue 时,会显示一个下拉列表域,其中是当前保存在Cloudinary存储中的图片。当指定selectPrefix时,只有ID以selectPrefix开头的图片才会显示。否则,显示ID以folder开头的图片。如果既没有配置selectPrefix,也没配置folder,则只显示ID以[{prefix}]/{list.path}/{field.path}/开头的图片。

{ type: Types.CloudinaryImage, select : true }

selectPrefix Stringselect参数为true时指定可供选择的图片的前缀。

{ type: Types.CloudinaryImage, select: true, selectPrefix: 'path/to/images' }
模式

public_id String

version Number

signature String

format String

resource_type String

url String

width Number

height Number

secure_url String

虚拟域
exists Boolean - 是否保存有图片
特殊路径

{path}_upload - 当给updateHandler提供了file时,它会被上传到cloudinary,并且详细信息会存在该域中。

Underscore方法:

src(options) String - 返回图片的url,接受cloudinary支持的所有参数

tag(options) String - 返回一个<img>标签

scale(width, height, options) String - 将图片缩放到恰当的宽度和高度,比例保持不变。

fit(width, height, options) String - 在指定的宽度和高度内缩放图片,比例保持不变。

lfit(width, height, options) String - 缩放图片以适应指定的宽度和高度,比例保持不变(不超出原始维度)

limit(width, height, options) String - 缩放图片(仅向下)以适应指定的宽度和高度,比例保持不变

fill(width, height, options) String - 缩放图片以填充指定的宽度和高度

crop(width, height, options) String - 裁剪图片以填充指定的宽度和高度

pad(width, height, options) String - 补白图片以填充指定的宽度和高度

lpad(width, height, options) String - 补白图片以填充指定的宽度和高度(不超出原始维度)

thumbnail(width, height, options) String - 裁剪图片以填充指定的宽度和高度

所有方法中的options都是可选的Object。 参见Cloudinary的转换文档了解所支持的参数和转换。

记住,如果用HTML表单上传图片到CloudinaryImage域,需要在form标签中指定enctype="multipart/form-data"

CloudinaryImages

Array — 在管理界面中显示为一系列图片,以及一个上传控件。

将数组中的多个图片存为嵌套的Schema,每个都会输出跟cloudinaryimage域一样的方法。

{ type: Types.CloudinaryImages }
参数

folder Stringcloudinary folders设为true时,用来为Cloudinary图片public_id指定文件夹/前缀。

{ type: Types.CloudinaryImages, folder: 'path/to/image' }

如果你希望 Cloudinary 在 cloudinary folders 设置为 true 的时候自动创建文件夹, 确认已经在你的 Cloudinary 账户中启用 "Auto-create folders"。

LocalFile

这个域类型跟Heroku之类的PAAS Hosts不兼容,因为它要依赖本地文件系统。

Object — 在管理界面中显示为一个文件上传控件。

在本地文件系统中存放文件。

{ type: Types.LocalFile }
参数

dest String - 必填项,存放上传文件的路径。

prefix String - 浏览器中的路径前缀,如果跟dest不同的话

datePrefix String - 如果设了,以这种格式的当前日期作为文件名的前缀(参见 moment.js了解格式参数)

allowedTypes 包含StringArray - 可以上传的文件mime类型白名单

filename Function - 以当前模型和客户端文件名为参数的函数,返回要上传文件的新文件名。

format Function - 带两个参数的函数:当前模型和文件对象,返回这个文件在管理界面中的表示。

{
	type: Types.LocalFile,
	dest: '/data/files',
	prefix: '/files/',
	format: function(item, file){
		return '<img src="/files/'+file.filename+'" style="max-width: 300px">'
	}
}

模式

filename String

path String

size Number

filetype String

虚拟域
exists Boolean - 是否保存了这样一个文件
Underscore方法:

uploadFile(file, update, callback) - 将文件上传到本地存储,在该域中保存文件细节信息,并将文件数据提供给回调函数。

  • file File 文件上传时应该是由express提供的文件,即req.files.path
  • update Boolean 文件上传完后是否用文件的细节信息更新该域。
  • callback(err, fileData) - 传递将会存储在该域中的对象(见上面的模式)

S3 File

Object — 在管理界面中显示为一个文件上传控件。

自动管理存在亚马逊S3上的文件,包括上传和删除。

{ type: Types.S3File }
参数

s3path String - 在S3的桶中存放上传文件的路径。

datePrefix String - 如果设了,以这种格式的当前日期作为文件名的前缀(参见 moment.js了解格式参数)

allowedTypes 包含StringArray - 可以上传的文件mime类型白名单

filename Function - 以当前模型和客户端文件名为参数的函数,返回要上传文件的新文件名。

{
	type: Types.S3File,
	filename: function(item, filename){
		// 用object id作为文件名的前缀
		return item._id + '-' + filename;
	}
}

headers ObjectArray 或者 Function - 设置 S3 对象请求头

请求头可以提供 Object 类型, 键值对的键被用来定义请求头名称,值用来设置请求头的值。

{
	type: Types.S3File, 
		headers: {
		'x-amz-meta-Cache-Control' : 'max-age=' + (60 * 15),
		'x-amz-meta-X-Custom-Header' : 'Object Option'
	} 
}

当用 Array 设置请求头, 数组内的每一个请求头元素应该是一个 Object 类型包含 namevalue String 属性。

{ 
	type: Types.S3File, 
		headers: [
		{ name: 'x-amz-meta-Cache-Control', value: 'max-age=' + (60 * 15) },
		{ name: 'x-amz-meta-X-Custom-Header', value: 'Array Option' }
	]
}

当用 Function 设置请求头,以当前模型和客户端文件名为参数的函数;该函数返回一个有效的请求头对象或者一个简单 Object 类型对象。

{ 
	type: Types.S3File, 
		headers: function (item, file){
		var headers = [];
		headers.push({ name: 'x-amz-meta-Cache-Control', value: 'max-age=' + item.maxAge });
		headers.push({ name: 'x-amz-meta-X-Custom-Header', value: 'Computed Option (Array)' });
		return headers;
	}
}
// 或 
{
	type: Types.S3File,
		headers: function (item, file){
		var headers = {};
		headers['x-amz-meta-Cache-Control'] = 'max-age=' + item.maxAge;
		headers['x-amz-meta-X-Custom-Header'] = 'Computed Option (Object)';
		return headers;
	}
}	

format Function - 以当前模型和客户端文件名为参数的函数,返回要上传文件的新文件名。

{
	type: Types.S3File,
	format: function(item, file){
		return '<pre>'+JSON.stringify(file, false, 2)+'</pre>'+
					'<img src="'+file.url+'" style="max-width: 300px">'
	}
}

模式

filename String

type String

filesize Number

url String

虚拟域
exists Boolean - 是否保存了这样一个文件
特殊路径

{path}_upload - 当提供给updateHandler一个file时,这个文件将会被上传到s3上,并且其细节信息会保存在该域中。

Underscore方法:

uploadFile(file, update, callback) - 将文件上传到s3的桶中,在该域中保存文件细节信息,并将文件数据提供给回调函数。

  • file File 文件上传时应该是由express提供的文件,即req.files.path
  • update Boolean 文件上传完后是否用文件的细节信息更新该域。
  • callback(err, fileData) - 传递将会存储在该域中的对象(见上面的模式)

AzureFile

Object — 在管理界面中显示为一个文件上传控件。

自动管理存在Windows Azure Storage中的文件,包括上传和删除。

{ type: Types.AzureFile }
参数

filenameFormatter Callback - 以当前模型和客户端文件名为参数的函数,返回要上传文件的新文件名。

{ type: Types.AzureFile, filenameFormatter: function(item, filename) {
	return item._id + require('path').extname(filename);
} }

containerFormatter Callback - 以当前模型和客户端文件名为参数的函数,返回新的容器名(容器是Azure存储账号中的根文件夹)。

{ type: Types.AzureFile, containerFormatter: containerFormatter: function(item, filename) {
	return item.modelProperty;
} }
模式

filename String

type String

filesize Number

url String

etag String

虚拟域
exists Boolean - 是否保存了这样一个文件
Underscore方法:

uploadFile(file, update, callback) - 将文件上传到Azure存储账号,在该域中保存文件细节信息,并将文件数据提供给回调函数。

  • file File 文件上传时应该是由express提供的文件,即req.files.path
  • update Boolean 文件上传完后是否用文件的细节信息更新该域。
  • callback(err, fileData) - 传递将会存储在该域中的对象(见上面的模式)

Embedly

Object — 在管理界面中显示为只读数据。

自动从Embedly API获取跟另一个域的值相关的信息(由from 参数指定)。

它存储获取的数据(包括提供者、媒体类型、完整的URL、HTML嵌入代码、宽度、高度、缩略图及更多信息)。

获取数据的api调用是作为保存前置钩子实现的,并且只在from路径的值发生变化时才会触发。

参见Embed.ly配置文档了解如何在KeystoneJS中设置Embed.ly的详细介绍。

{ type: Types.Embedly, from: 'path' }
参数

from String - 模式中传给Embedly API的另一个域的路径。另一个域中必须包含一个String值。

options Object (optional) - 跟from域的值一起作为参数传给embedly API。

参见Embedly的oEmbed API 文档了解参数及返回数据的更多信息。

模式

exists Boolean

type String

title String

url String

width Number

height Number

version String

description String

html String

authorName String

authorUrl String

providerName String

providerUrl String

thumbnailUrl String

thumbnailWidth Number

thumbnailHeight Number

更多范例

参见项目的范例页面,了解各种列表参数和域类型的真实用法。