Skip to main content

模型

🌐 Models

Page summary:

模型通过内容类型和可重用组件定义 Strapi 的内容结构。本指南将介绍如何在内容类型构建器或 CLI 中创建这些模型,以及如何使用可选的生命周期钩子管理模式文件。

由于 Strapi 是一个无头内容管理系统(CMS),为内容创建内容结构是使用该软件时最重要的方面之一。模型定义了内容结构的表示。

🌐 As Strapi is a headless Content Management System (CMS), creating a content structure for the content is one of the most important aspects of using the software. Models define a representation of the content structure.

Strapi 有 2 种不同类型的模型:

🌐 There are 2 different types of models in Strapi:

  • 内容类型,可以是集合类型或单一类型,具体取决于它们管理的条目数量,
  • 以及可在多种内容类型中重复使用的内容结构组件。

如果你刚刚开始,可以直接在管理面板中使用 Content-type Builder 生成一些模型。这种用户界面承担了许多验证任务,并展示了创建内容结构的所有可用选项。然后可以使用此文档在代码层面上查看生成的模型映射。

🌐 If you are just starting out, it is convenient to generate some models with the Content-type Builder directly in the admin panel. The user interface takes over a lot of validation tasks and showcases all the options available to create the content's content structure. The generated model mappings can then be reviewed at the code level using this documentation.

模型创建

🌐 Model creation

内容类型和组件模型的创建和存储方式不同。

🌐 Content-types and components models are created and stored differently.

内容类型

🌐 Content-types

可以在 Strapi 中创建内容类型:

🌐 Content-types in Strapi can be created:

内容类型使用以下文件:

🌐 The content-types use the following files:

  • schema.json 用于模型的 schema 定义。(自动生成,在使用任一方法创建内容类型时)
  • lifecycles.js 用于 生命周期钩子。此文件必须手动创建。

这些模型文件存储在 ./src/api/[api-name]/content-types/[content-type-name]/ 中,任何在这些文件夹中找到的 JavaScript 或 JSON 文件都将被加载为内容类型的模型(参见 项目结构)。

🌐 These models files are stored in ./src/api/[api-name]/content-types/[content-type-name]/, and any JavaScript or JSON file found in these folders will be loaded as a content-type's model (see project structure).

Note

在启用了 TypeScript 的项目中,可以使用 ts:generate-types 命令生成模式类型定义。

🌐 In TypeScript-enabled projects, schema typings can be generated using the ts:generate-types command.

组件

🌐 Components

组件模型无法使用 CLI 工具创建。请使用 内容类型构建器 或手动创建它们。

🌐 Component models can't be created with CLI tools. Use the Content-type Builder or create them manually.

组件模型存储在 ./src/components 文件夹中。每个组件必须位于一个子文件夹内,该子文件夹的名称应与组件所属的类别相同(参见 项目结构)。

🌐 Components models are stored in the ./src/components folder. Every component has to be inside a subfolder, named after the category the component belongs to (see project structure).

模型架构

🌐 Model schema

一个模型的 schema.json 文件包含:

🌐 The schema.json file of a model consists of:

  • 设置,例如模型表示的内容类型或应存储数据的表名,
  • 信息,主要用于在管理面板中显示模型并通过 REST 和 GraphQL API 访问它,
  • 属性,描述模型的内容结构,
  • 以及 options 用于定义模型上的特定行为。

模型设置

🌐 Model settings

模型的常规设置可以使用以下参数进行配置:

🌐 General settings for the model can be configured with the following parameters: | 参数 | 类型 | 描述 || --- | --- | --- || collectionName | 字符串 | 数据应存储的数据库表名称 || kind

可选,
仅用于内容类型
| 字符串 | 定义内容类型是否为:

  • 集合类型(collectionType
  • 或单一类型(singleType
|

// ./src/api/[api-name]/content-types/restaurant/schema.json

{
"kind": "collectionType",
"collectionName": "Restaurants_v1",
}

模型信息

🌐 Model information

模型模式中的 info 键描述了用于在管理面板中显示模型以及通过内容 API 访问模型的信息。它包括以下参数:

🌐 The info key in the model's schema describes information used to display the model in the admin panel and access it through the Content API. It includes the following parameters:

| 参数 | 类型 | 描述 || --- | --- | --- || displayName | 字符串 | 在管理面板中使用的默认名称 || singularName | 字符串 | 内容类型名称的单数形式。
用于生成 API 路由和数据库/表集合。

应使用 kebab-case 格式。 || pluralName | 字符串 | 内容类型名称的复数形式。
用于生成 API 路由和数据库/表集合。

应使用 kebab-case 格式。 || description | 字符串 | 模型的描述 |

./src/api/[api-name]/content-types/restaurant/schema.json

"info": {
"displayName": "Restaurant",
"singularName": "restaurant",
"pluralName": "restaurants",
"description": ""
},

模型属性

🌐 Model attributes

模型的内容结构由一系列属性组成。每个属性都有一个 type 参数,用于描述其性质,并将该属性定义为简单的数据片段或 Strapi 使用的更复杂结构。

🌐 The content structure of a model consists of a list of attributes. Each attribute has a type parameter, which describes its nature and defines the attribute as a simple piece of data or a more complex structure used by Strapi.

有多种类型的属性可用:

🌐 Many types of attributes are available:

  • 标量类型(例如字符串、日期、数字、布尔值等),
  • Strapi 特有的类型,例如:
    • media 用于通过 媒体库 上传的文件
    • relation 用于描述内容类型之间的 关系
    • customField 用于描述 自定义字段 及其特定键
    • component 用于定义一个 组件(即可在多种内容类型中使用的内容结构)
    • dynamiczone 用于定义一个 动态区域(即基于组件列表的灵活空间)
    • 以及 localelocalizations 类型,仅由 国际化 (i18n) 插件 使用

属性的 type 参数应为以下值之一:

🌐 The type parameter of an attribute should be one of the following values: | 类型类别 | 可用类型 ||------|-------|| 字符串类型 |

  • string
  • text
  • richtext
  • enumeration
  • email
  • password
  • uid
|| 日期类型 |
  • date
  • time
  • datetime
  • timestamp
|| 数字类型 |
  • integer
  • biginteger
  • float
  • decimal
|| 其他通用类型 |
  • boolean
  • json
|| Strapi 独有的特殊类型 | || 国际化 (i18n) 相关类型

仅在内容类型启用了 i18n 时可用|
  • locale
  • localizations
|

验证

🌐 Validations

可以使用以下参数将基本验证应用于属性:

🌐 Basic validations can be applied to attributes using the following parameters: | 参数 | 类型 | 描述 | 默认值 || --- | --- | --- | --- || required | 布尔值 | 如果 true,为此属性添加必填验证器 | false || max | 整数 | 检查值是否大于或等于给定的最大值 | - || min | 整数 | 检查值是否小于或等于给定的最小值 | - || minLength | 整数 | 字段输入值的最小字符数 | - || maxLength | 整数 | 字段输入值的最大字符数 | - || private | 布尔值 | 如果 true,该属性将从服务器响应中移除。

💡 这对于隐藏敏感数据很有用。 | false || configurable | 布尔值 | 如果 false,该属性无法通过内容类型构建器插件进行配置。 | true |

./src/api/[api-name]/content-types/restaurant/schema.json

{
// ...
"attributes": {
"title": {
"type": "string",
"minLength": 3,
"maxLength": 99,
"unique": true
},
"description": {
"default": "My description",
"type": "text",
"required": true
},
"slug": {
"type": "uid",
"targetField": "title"
}
// ...
}
}

数据库验证和设置

🌐 Database validations and settings

🚧 This API is considered experimental.

这些设置应该保留给高级使用,因为它们可能会导致某些功能失效。目前没有计划让这些设置稳定。

🌐 These settings should be reserved to an advanced usage, as they might break some features. There are no plans to make these settings stable.

数据库验证和设置是直接传递给 tableBuilder Knex.js 函数的自定义选项,用于架构迁移期间。数据库验证允许对设置自定义列设置进行高级控制。以下选项在每个属性的 column: {} 对象中设置:

🌐 Database validations and settings are custom options passed directly onto the tableBuilder Knex.js function during schema migrations. Database validations allow for an advanced degree of control for setting custom column settings. The following options are set in a column: {} object per attribute: | 参数 | 类型 | 描述 | 默认值 || --- | --- | --- | --- || name | 字符串 | 更改数据库中列的名称 | - || defaultTo | 字符串 | 设置数据库的 defaultTo,通常与 notNullable 一起使用 | - || notNullable | 布尔值 | 设置数据库的 notNullable,确保列不能为空 | false || unsigned | 布尔值 | 仅适用于数字列,移除允许负数的功能,但将最大长度加倍 | false || unique | 布尔值 | 强制对已发布条目执行数据库级唯一性检查。当启用“草稿与发布”功能时,草稿保存会跳过检查,因此仅在发布时重复才会失败 | false || type | 字符串 | 更改数据库类型,如果 type 有参数,应在 args 中传入 | - || args | 数组 | 传递给 Knex.js 函数的参数,可更改如 type 的内容 | [] |

Caution
Draft & Publish and unique

当启用 Draft & Publish 时,Strapi 会在条目被保存为草稿时故意跳过 unique 验证。因此,重复内容在发布之前不会被检测到,此时数据库约束会触发错误,即使 UI 之前对于草稿显示了“已保存文档”。

🌐 When Draft & Publish is enabled, Strapi intentionally skips unique validations while an entry is saved as a draft. Duplicates therefore remain undetected until publication, at which point the database constraint triggers an error even though the UI previously displayed “Saved document” for the drafts.

为避免意外发布失败:

🌐 To avoid unexpected publication failures:

  • 禁用必须保持全局唯一的内容类型的“草稿和发布”功能,
  • 或添加自定义验证(例如生命周期钩子或中间件),在保存前检查草稿是否重复,
  • 或者依赖自动生成的唯一标识符,例如 uid 字段和文档编辑规范。
./src/api/[api-name]/content-types/restaurant/schema.json

{
// ...
"attributes": {
"title": {
"type": "string",
"minLength": 3,
"maxLength": 99,
"unique": true,
"column": {
"unique": true // enforce database unique also
}
},
"description": {
"default": "My description",
"type": "text",
"required": true,
"column": {
"defaultTo": "My description", // set database level default
"notNullable": true // enforce required at database level, even for drafts
}
},
"rating": {
"type": "decimal",
"default": 0,
"column": {
"defaultTo": 0,
"type": "decimal", // using the native decimal type but allowing for custom precision
"args": [
6,1 // using custom precision and scale
]
}
}
// ...
}
}

uid 类型

🌐 uid type

uid 类型用于在管理面板中自动预填字段值,使用唯一标识符(UID)(例如文章的 slug),基于两个可选参数:

🌐 The uid type is used to automatically prefill the field value in the admin panel with a unique identifier (UID) (e.g. slugs for articles) based on 2 optional parameters:

  • targetField(字符串):如果使用,定义为目标的字段的值将用于自动生成 UID。
  • options(字符串):如果使用,UID 将基于传递给底层 `uid` 生成器的一组选项生成。生成的 uid 必须符合以下正则表达式模式:/^[A-Za-z0-9-_.~]*$

关系

🌐 Relations

关系将内容类型连接在一起。Strapi 支持单条条目关系(单向和一对一)以及多条关系,其中至少一方可以指向多个条目(一对多、多对一、多对多和多向)。多条关系在数据库层中以数组形式保存,并在内容 API 响应中以数组形式返回。

🌐 Relations link content-types together. Strapi supports both single-entry relations (one-way and one-to-one) and multi relations where at least one side can point to several entries (one-to-many, many-to-one, many-to-many, and many-way). Multi relations are persisted as arrays in the database layer and are returned as arrays in the Content API responses.

关系在模型的属性中用type: 'relation'明确定义,并接受以下附加参数:

🌐 Relations are explicitly defined in the attributes of a model with type: 'relation' and accept the following additional parameters: | 参数 | 描述 || --- | --- || relation | 这些值之间的关系类型:

  • oneToOne
  • oneToMany
  • manyToOne
  • manyToMany
|| target | 接受一个字符串值作为目标内容类型的名称 || mappedByinversedBy

可选 | 在双向关系中,拥有方声明 inversedBy 键,而被引用方声明 mappedBy 键 |

当一个条目只能链接到另一个条目时,一对一关系非常有用。

🌐 One-to-One relationships are useful when one entry can be linked to only one other entry.

它们可以是单向或双向的。在单向关系中,只有其中一个模型可以通过其关联项进行查询。

🌐 They can be unidirectional or bidirectional. In unidirectional relationships, only one of the models can be queried with its linked item.

单向使用案例示例:
  • 博客文章属于一个类别。
  • 查询一篇文章可以检索它的类别,
  • 但查询类别不会检索拥有的文章。
./src/api/[api-name]/content-types/article/schema.json

// …
attributes: {
category: {
type: 'relation',
relation: 'oneToOne',
target: 'category',
},
},
// …
双向使用案例示例:
  • 博客文章属于一个类别。
  • 查询一篇文章可以检索它的类别,
  • 查询类别也会检索其拥有的文章。
./src/api/[api-name]/content-types/article/schema.json

// …
attributes: {
category: {
type: 'relation',
relation: 'oneToOne',
target: 'category',
inversedBy: 'article',
},
},
// …

./src/api/[api-name]/content-types/category/schema.json

// …
attributes: {
article: {
type: 'relation',
relation: 'oneToOne',
target: 'article',
mappedBy: 'category',
},
},
// …

自定义字段

🌐 Custom fields

自定义字段 通过向内容类型添加新类型的字段来扩展 Strapi 的功能。自定义字段在模型的 属性 中使用 type: customField 明确定义。

自定义字段的属性还显示以下特性:

🌐 Custom fields' attributes also show the following specificities:

  • 一个 customField 属性,其值作为唯一标识符,用于指示应使用哪个已注册的自定义字段。其值如下:
    • 如果插件创建了自定义字段,则使用 plugin::plugin-name.field-name 格式
    • 或者用于当前 Strapi 应用特定自定义字段的 global::field-name 格式
  • 以及额外的参数,这取决于在注册自定义字段时定义的内容(参见自定义字段文档)。
./src/api/[apiName]/[content-type-name]/content-types/schema.json

{
// …
"attributes": {
"attributeName": { // attributeName would be replaced by the actual attribute name
"type": "customField",
"customField": "plugin::color-picker.color",
"options": {
"format": "hex"
}
}
}
// …
}

组件

🌐 Components

组件字段在内容类型和组件结构之间创建关系。组件在模型的 attributes 中通过 type: 'component' 明确定义,并接受以下附加参数:

🌐 Component fields create a relation between a content-type and a component structure. Components are explicitly defined in the attributes of a model with type: 'component' and accept the following additional parameters: | 参数 | 类型 | 描述 || --- | --- | --- || repeatable | 布尔值 | 根据组件是否可重复,可能是 truefalse || component | 字符串 | 定义相应的组件,遵循此格式:
<category>.<componentName> |

./src/api/[apiName]/restaurant/content-types/schema.json

{
"attributes": {
"openinghours": {
"type": "component",
"repeatable": true,
"component": "restaurant.openinghours"
}
}
}

动态区域

🌐 Dynamic zones

动态区域创建了一个灵活的空间,用于根据混合的组件列表来编排内容。

🌐 Dynamic zones create a flexible space in which to compose content, based on a mixed list of components.

动态区域在模型的 attributes 中用 type: 'dynamiczone' 明确定义。它们还接受一个 components 数组,其中每个组件的名称应遵循以下格式:<category>.<componentName>

🌐 Dynamic zones are explicitly defined in the attributes of a model with type: 'dynamiczone'. They also accept a components array, where each component should be named following this format: <category>.<componentName>.

./src/api/[api-name]/content-types/article/schema.json

{
"attributes": {
"body": {
"type": "dynamiczone",
"components": ["article.slider", "article.content"]
}
}
}

模型选项

🌐 Model options

options 键用于定义特定行为,并接受以下参数:

🌐 The options key is used to define specific behaviors and accepts the following parameter:

参数类型描述
privateAttributes字符串数组允许将一组属性视为私有,即使它们实际上并未在模型中定义为属性。它可以用于从 API 响应中移除它们的时间戳。

模型中定义的 privateAttributes 会与全局 Strapi 配置中定义的 privateAttributes 合并。
draftAndPublish布尔值启用草稿和发布功能。

默认值:true(如果内容类型是通过交互式 CLI 创建,则为 false)。
populateCreatorFields布尔值在 REST API 返回的响应中填充 createdByupdatedBy 字段(更多详情参见指南)。

默认值:false
./src/api/[api-name]/content-types/restaurant/schema.json

{
"options": {
"privateAttributes": ["id", "createdAt"],
"draftAndPublish": true
}
}

插件选项

🌐 Plugin options

pluginOptions 是一个可选对象,允许插件为模型或特定属性存储配置。 | 键 | 值 | 描述 ||---------------------------|-------------------------------|--------------------------------------------------------|| i18n | localized: true | 启用本地化。 || content-manager | visible: false | 在管理员面板中从内容管理器隐藏。 || content-type-builder | visible: false | 在管理员面板中从内容类型构建器隐藏。 |

./src/api/[api-name]/content-types/[content-type-name]/schema.json

{
"attributes": {
"name": {
"pluginOptions": {
"i18n": {
"localized": true
}
},
"type": "string",
"required": true
},
"slug": {
"pluginOptions": {
"i18n": {
"localized": true
}
},
"type": "uid",
"targetField": "name",
"required": true
}
// …additional attributes
}
}

生命周期钩子

🌐 Lifecycle hooks

生命周期钩子是当 Strapi 查询被调用时触发的函数。当通过管理面板管理内容或使用 queries 开发自定义代码时,它们会自动触发。

🌐 Lifecycle hooks are functions that get triggered when Strapi queries are called. They are triggered automatically when managing content through the administration panel or when developing custom code using queries·

生命周期钩子可以通过声明或编程方式自定义。

🌐 Lifecycle hooks can be customized declaratively or programmatically.

Caution

当直接使用 knex 库而不是Strapi函数时,生命周期钩子不会被触发。

Document Service API: lifecycles and middlewares

文档服务 API 会根据调用的方法触发各种数据库生命周期钩子。完整参考请参见 文档服务 API:生命周期钩子。批量操作生命周期(createManyupdateManydeleteMany)绝不会被文档服务 API 方法触发。也可以实现 文档服务中间件

🌐 The Document Service API triggers various database lifecycle hooks based on which method is called. For a complete reference, see Document Service API: Lifecycle hooks. Bulk actions lifecycles (createMany, updateMany, deleteMany) will never be triggered by a Document Service API method. Document Service middlewares can be implemented too.

可用的生命周期事件

🌐 Available lifecycle events

以下生命周期事件可用:

🌐 The following lifecycle events are available:

  • beforeCreate
  • beforeCreateMany
  • afterCreate
  • afterCreateMany
  • beforeUpdate
  • beforeUpdateMany
  • afterUpdate
  • afterUpdateMany
  • beforeDelete
  • beforeDeleteMany
  • afterDelete
  • afterDeleteMany
  • beforeCount
  • afterCount
  • beforeFindOne
  • afterFindOne
  • beforeFindMany
  • afterFindMany

钩子 event 对象

🌐 Hook event object

生命周期钩子是接收一个 event 参数的函数,该参数是一个包含以下键的对象:

🌐 Lifecycle hooks are functions that take an event parameter, an object with the following keys: | 键 | 类型 | 描述 || --- | --- | --- || action | 字符串 | 已触发的生命周期事件(参见 列表) || model | 字符串数组(uid) | 一个包含要监听事件的内容类型 uid 的数组。
如果未提供此参数,则会监听所有内容类型的事件。 || params | 对象 | 接受以下参数:

  • data
  • select
  • where
  • orderBy
  • limit
  • offset
  • populate
|| result | 对象 | 可选,仅在 afterXXX 事件中可用

包含操作的结果。 || state | 对象 | 查询状态,可用于在查询的 beforeXXXafterXXX 事件之间共享状态。 |

声明式和程序化使用

🌐 Declarative and programmatic usage

要配置内容类型生命周期钩子,请在 ./src/api/[api-name]/content-types/[content-type-name]/ 文件夹中创建一个 lifecycles.js 文件。

🌐 To configure a content-type lifecycle hook, create a lifecycles.js file in the ./src/api/[api-name]/content-types/[content-type-name]/ folder.

每个事件监听器按顺序调用。它们可以是同步的也可以是异步的。

🌐 Each event listener is called sequentially. They can be synchronous or asynchronous.

./src/api/[api-name]/content-types/[content-type-name]/lifecycles.js

module.exports = {
beforeCreate(event) {
const { data, where, select, populate } = event.params;

// let's do a 20% discount everytime
event.params.data.price = event.params.data.price * 0.8;
},

afterCreate(event) {
const { result, params } = event;

// do something to the result;
},
};

使用数据库层 API,还可以注册订阅者并以编程方式监听事件:

🌐 Using the database layer API, it's also possible to register a subscriber and listen to events programmatically:

./src/index.js
module.exports = {
async bootstrap({ strapi }) {
// registering a subscriber
strapi.db.lifecycles.subscribe({
models: [], // optional;

beforeCreate(event) {
const { data, where, select, populate } = event.params;

event.state = 'doStuffAfterWards';
},

afterCreate(event) {
if (event.state === 'doStuffAfterWards') {
}

const { result, params } = event;

// do something to the result
},
});

// generic subscribe for generic handling
strapi.db.lifecycles.subscribe((event) => {
if (event.action === 'beforeCreate') {
// do something
}
});
}
}