环回设置一个 属性 以某种方式不会持久保存到数据库 sql?
Loopback setting a property that somehow doesn't persist to the database sql?
我们正在将 api 从 C# 移植到 Loopback ^v3.19.0
并将 运行 放入拦截器中。
我们的许多模型都具有共享属性,因此我们创建了一个基础模型 "Base",它们从中继承。
{
"name": "Base",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"mixins": {
"Timestamp": {}
},
"properties": {
"created-by": {
"type": "number",
"postgresql": {
"columnName": "created_by"
}
},
"created-date": {
"type": "date",
"postgresql": {
"columnName": "created_on_utc"
}
},
"updated-by": {
"type": "number",
"postgresql": {
"columnName": "updated_by"
}
},
"updated-date": {
"type": "date",
"postgresql": {
"columnName": "updated_on_utc"
}
},
"soft-deleted": {
"type": "boolean",
"postgresql": {
"columnName": "is_deleted"
}
},
"deleted-by": {
"type": "number",
"postgresql": {
"columnName": "deleted_by"
}
},
"deleted-date": {
"type": "date",
"postgresql": {
"columnName": "deleted_on_utc"
}
},
"tenant-id": {
"type": "number",
"postgresql": {
"columnName": "tenant_id"
}
}
},
...
}
在 Timestamp mixin(我们自己的)中,相应地设置了这些属性
module.exports = function(Model, options) {
Model.observe('before save', function event(ctx, next) {
const token = ctx.options && ctx.options.accessToken;
const userId = token && token.userId;
const now = new Date().toISOString();
if (ctx.instance) {
ctx.instance['created-by'] = userId;
ctx.instance['created-date'] = now;
ctx.instance['updated-by'] = userId;
ctx.instance['updated-date'] = now;
} else {
if (ctx.data['soft-deleted'] &&
ctx.data['soft-deleted'] === true) {
ctx.data['deleted-by'] = userId;
ctx.data['deleted-date'] = now;
ctx.data['is-active'] = false;
}
ctx.data['updated-by'] = userId;
ctx.data['updated-date'] = now;
}
next();
});
};
这在创建新模型时效果很好。它 非常适合更新 (PATCH /modelname/:id)
,但意外损坏,我们无法弄清楚原因。 (这在继承自此 Base
模型的所有模型中都是一致的。)
mixin 正确地看到模型并像这样添加更新的属性
LoopbackJS | ************* 'before save' ctx.data **************
LoopbackJS | { 'is-active': false,
LoopbackJS | 'updated-by': 1,
LoopbackJS | 'updated-date': '2018-08-16T17:57:23.660Z' }
LoopbackJS | ************* END 'before save' ctx.data **************
但是当 loopback 执行更新 SQL 时,它不知何故 omits/removes updated-by
的值? (第二个参数应该是 1
,而不是 null
)
LoopbackJS | 2018-08-16T17:57:23.666Z loopback:connector:postgresql SQL: UPDATE "public"."asset_types" SET "is_active"=,"updated_by"=,"updated_on_utc"=::TIMESTAMP WITH TIME ZONE,"tenant_id"= WHERE "id"=
LoopbackJS | Parameters: [false,null,"2018-08-16T17:57:23.660Z",1,5]
updated_by
在 Postgres 中是可以为 null 的,因此不应产生错误...但是 Loopback 正在发送一个字符串化函数?
LoopbackJS | 2018-08-16T18:04:12.522Z loopback:connector:postgresql error: invalid input syntax for integer: "function () { [native code] }"
LoopbackJS | at Connection.parseE (/home/src/back-end/node_modules/pg/lib/connection.js:553:11)
LoopbackJS | at Connection.parseMessage (/home/src/back-end/node_modules/pg/lib/connection.js:378:19)
LoopbackJS | at TLSSocket.<anonymous> (/home/src/back-end/node_modules/pg/lib/connection.js:119:22)
LoopbackJS | at emitOne (events.js:115:13)
LoopbackJS | at TLSSocket.emit (events.js:210:7)
LoopbackJS | at addChunk (_stream_readable.js:264:12)
LoopbackJS | at readableAddChunk (_stream_readable.js:251:11)
LoopbackJS | at TLSSocket.Readable.push (_stream_readable.js:209:10)
LoopbackJS | at TLSWrap.onread (net.js:587:20)
如果我们不触及 updated_by
列,则 SQL 是正确的并会更新。
顺便说一句,如果我们软删除并且 deleted_by
列正在运行,同样的事情也会发生在那里。
感觉我在这里兜圈子,可能忽略了一些基本的东西。有什么建议吗?
编辑
所以看起来它并不局限于mixin...当我们完全删除它并在有效负载中手动设置k:v对时(即'created-by': 1
)我们仍然得到相同的错误从 Postgres 回来。
根本原因是关系不正确。
我创建了这个 as a gist,但也将其粘贴在这里以防它对其他人有帮助。
使用小写名称是 PostgreSQL
最佳做法,如果需要,请使用蛇形字母。即,my_column_name。
另外,因为我使用 JSON API client, I've installed the excellent loopback-component-jsonapi 来处理 de/serialization 的东西...但这只是增加了额外的复杂性。
JSON API 要求破折号 属性 名称。当您从 my-property-name
之类的内容开始时,Loopback 或 PostgreSQL 驱动程序(并不重要)默认情况下会将破折号 属性 折叠为 mypropertyname
。
这很糟糕……尤其是当您有一个正在使用的现有模式时。
处理关系时情况更糟,因为 Loopback 还 默认附加 id
后缀,所以现在您遇到问题,除非您碰巧有mypropertynameid
列。
一个例子
假设我们有一个 Customer
模型。我需要小写的端点(和破折号,如果适用),所以只需更改复数以匹配此处。
{
"name": "Customer",
"plural": "customers",
"base": "PersistedModel",
...
}
在options.postgresql
里面,可以设置一个tableName
。默认情况下,Loopback 将使用 name
值,但请记住 PostgreSQL 不喜欢 CamelCase。除非您使用小写模型名称,否则您需要覆盖它。
(这是一种宗教偏好,但我喜欢我的 table 是复数。打我吧。)
{
...
"options": {
"validateUpsert": true,
"postgresql": {
"tableName": "customers"
}
}
...
}
返回属性,使用 postgresql.columnName
属性 映射到数据库中正确的列名。如果它不是破折号 属性 名称(即 status
),那么您可以忽略 postgresql.columnName
位。
{
...
"properties": {
"is-active": {
"type": "boolean",
"default": false,
"postgresql": {
"columnName": "is_active"
}
}
}
}
人际关系令人头疼。
假设我们的 Customer
有员工在那里工作。要在模型之间建立基本的一对多关系...
{
...
"relations": {
"people": {
"type": "hasMany",
"model": "Person",
"foreignKey": "customer_id"
}
},
...
}
people
是 JSON API 负载的关系元素的名称。
A "gotcha" 对我来说是 foreignKey
属性.
Loopback 文档说它是可选的 - 它是 - 但如果您将其省略,则会将 id
后缀添加到名称 (people
) 中,然后在您的customers
table。这一点没有很好地突出显示,但已经足够清楚了。
这部分没看清楚=>我本来以为foreignKey
值指向属性 的 Person
模型,所以我在这里使用了破折号 customer-id
属性。 这是不正确的。它实际上是在询问您数据库列名,这感觉有点反模式...如果您需要,您必须在属性中定义一个 columnName
引用 ORM 下的数据库列。
此外,请注意 foreignKey
属性 在关系中重复使用,但它对不同的 type
上下文意味着不同的事情。在 hasMany
中,它询问 "Which column there maps to the primary key here?"
最终 Customer
型号:
{
"name": "Customer",
"plural": "customers",
"base": "PersistedModel",
"options": {
"validateUpsert": true,
"postgresql": {
"tableName": "customers"
}
},
"properties": {
"name": {
"type": "string"
},
"is-active": {
"type": "boolean",
"default": false,
"postgresql": {
"columnName": "is_active"
}
}
},
"validations": [],
"relations": {
"people": {
"type": "hasMany",
"model": "Person",
"foreignKey": "customer_id"
}
},
"acls": [],
"methods": {}
}
关系另一端的 Person
模型。
belongsTo
关系的 foreignKey
问的是相反的问题... "Which property here maps to the primary key there?"
此外,如果您有不想公开的属性(特别是如果您继承了一个模型并且出于某种原因没有 want/need 所有这些属性),那么您可以使用 hidden
元素。见下文。
{
"name": "Person",
"plural": "people",
"base": "User",
"idInjection": false,
"options": {
"validateUpsert": true,
"postgresql": {
"tableName": "people"
}
},
"hidden": [
"emailVerified",
"realm",
"username",
],
"properties": {
"first-name": {
"type": "string",
"postgresql": {
"columnName": "first_name"
}
},
"last-name": {
"type": "string",
"postgresql": {
"columnName": "last_name"
}
},
"email": {
"type": "string"
},
...
},
"validations": [],
"relations": {
"customer": {
"type": "belongsTo",
"model": "Customer",
"foreignKey": "customer_id"
}
},
"acls": [],
"methods": {}
}
我们正在将 api 从 C# 移植到 Loopback ^v3.19.0
并将 运行 放入拦截器中。
我们的许多模型都具有共享属性,因此我们创建了一个基础模型 "Base",它们从中继承。
{
"name": "Base",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"mixins": {
"Timestamp": {}
},
"properties": {
"created-by": {
"type": "number",
"postgresql": {
"columnName": "created_by"
}
},
"created-date": {
"type": "date",
"postgresql": {
"columnName": "created_on_utc"
}
},
"updated-by": {
"type": "number",
"postgresql": {
"columnName": "updated_by"
}
},
"updated-date": {
"type": "date",
"postgresql": {
"columnName": "updated_on_utc"
}
},
"soft-deleted": {
"type": "boolean",
"postgresql": {
"columnName": "is_deleted"
}
},
"deleted-by": {
"type": "number",
"postgresql": {
"columnName": "deleted_by"
}
},
"deleted-date": {
"type": "date",
"postgresql": {
"columnName": "deleted_on_utc"
}
},
"tenant-id": {
"type": "number",
"postgresql": {
"columnName": "tenant_id"
}
}
},
...
}
在 Timestamp mixin(我们自己的)中,相应地设置了这些属性
module.exports = function(Model, options) {
Model.observe('before save', function event(ctx, next) {
const token = ctx.options && ctx.options.accessToken;
const userId = token && token.userId;
const now = new Date().toISOString();
if (ctx.instance) {
ctx.instance['created-by'] = userId;
ctx.instance['created-date'] = now;
ctx.instance['updated-by'] = userId;
ctx.instance['updated-date'] = now;
} else {
if (ctx.data['soft-deleted'] &&
ctx.data['soft-deleted'] === true) {
ctx.data['deleted-by'] = userId;
ctx.data['deleted-date'] = now;
ctx.data['is-active'] = false;
}
ctx.data['updated-by'] = userId;
ctx.data['updated-date'] = now;
}
next();
});
};
这在创建新模型时效果很好。它 非常适合更新 (PATCH /modelname/:id)
,但意外损坏,我们无法弄清楚原因。 (这在继承自此 Base
模型的所有模型中都是一致的。)
mixin 正确地看到模型并像这样添加更新的属性
LoopbackJS | ************* 'before save' ctx.data **************
LoopbackJS | { 'is-active': false,
LoopbackJS | 'updated-by': 1,
LoopbackJS | 'updated-date': '2018-08-16T17:57:23.660Z' }
LoopbackJS | ************* END 'before save' ctx.data **************
但是当 loopback 执行更新 SQL 时,它不知何故 omits/removes updated-by
的值? (第二个参数应该是 1
,而不是 null
)
LoopbackJS | 2018-08-16T17:57:23.666Z loopback:connector:postgresql SQL: UPDATE "public"."asset_types" SET "is_active"=,"updated_by"=,"updated_on_utc"=::TIMESTAMP WITH TIME ZONE,"tenant_id"= WHERE "id"=
LoopbackJS | Parameters: [false,null,"2018-08-16T17:57:23.660Z",1,5]
updated_by
在 Postgres 中是可以为 null 的,因此不应产生错误...但是 Loopback 正在发送一个字符串化函数?
LoopbackJS | 2018-08-16T18:04:12.522Z loopback:connector:postgresql error: invalid input syntax for integer: "function () { [native code] }"
LoopbackJS | at Connection.parseE (/home/src/back-end/node_modules/pg/lib/connection.js:553:11)
LoopbackJS | at Connection.parseMessage (/home/src/back-end/node_modules/pg/lib/connection.js:378:19)
LoopbackJS | at TLSSocket.<anonymous> (/home/src/back-end/node_modules/pg/lib/connection.js:119:22)
LoopbackJS | at emitOne (events.js:115:13)
LoopbackJS | at TLSSocket.emit (events.js:210:7)
LoopbackJS | at addChunk (_stream_readable.js:264:12)
LoopbackJS | at readableAddChunk (_stream_readable.js:251:11)
LoopbackJS | at TLSSocket.Readable.push (_stream_readable.js:209:10)
LoopbackJS | at TLSWrap.onread (net.js:587:20)
如果我们不触及 updated_by
列,则 SQL 是正确的并会更新。
顺便说一句,如果我们软删除并且 deleted_by
列正在运行,同样的事情也会发生在那里。
感觉我在这里兜圈子,可能忽略了一些基本的东西。有什么建议吗?
编辑
所以看起来它并不局限于mixin...当我们完全删除它并在有效负载中手动设置k:v对时(即'created-by': 1
)我们仍然得到相同的错误从 Postgres 回来。
根本原因是关系不正确。
我创建了这个 as a gist,但也将其粘贴在这里以防它对其他人有帮助。
使用小写名称是 PostgreSQL
最佳做法,如果需要,请使用蛇形字母。即,my_column_name。
另外,因为我使用 JSON API client, I've installed the excellent loopback-component-jsonapi 来处理 de/serialization 的东西...但这只是增加了额外的复杂性。
JSON API 要求破折号 属性 名称。当您从 my-property-name
之类的内容开始时,Loopback 或 PostgreSQL 驱动程序(并不重要)默认情况下会将破折号 属性 折叠为 mypropertyname
。
这很糟糕……尤其是当您有一个正在使用的现有模式时。
处理关系时情况更糟,因为 Loopback 还 默认附加 id
后缀,所以现在您遇到问题,除非您碰巧有mypropertynameid
列。
一个例子
假设我们有一个 Customer
模型。我需要小写的端点(和破折号,如果适用),所以只需更改复数以匹配此处。
{
"name": "Customer",
"plural": "customers",
"base": "PersistedModel",
...
}
在options.postgresql
里面,可以设置一个tableName
。默认情况下,Loopback 将使用 name
值,但请记住 PostgreSQL 不喜欢 CamelCase。除非您使用小写模型名称,否则您需要覆盖它。
(这是一种宗教偏好,但我喜欢我的 table 是复数。打我吧。)
{
...
"options": {
"validateUpsert": true,
"postgresql": {
"tableName": "customers"
}
}
...
}
返回属性,使用 postgresql.columnName
属性 映射到数据库中正确的列名。如果它不是破折号 属性 名称(即 status
),那么您可以忽略 postgresql.columnName
位。
{
...
"properties": {
"is-active": {
"type": "boolean",
"default": false,
"postgresql": {
"columnName": "is_active"
}
}
}
}
人际关系令人头疼。
假设我们的 Customer
有员工在那里工作。要在模型之间建立基本的一对多关系...
{
...
"relations": {
"people": {
"type": "hasMany",
"model": "Person",
"foreignKey": "customer_id"
}
},
...
}
people
是 JSON API 负载的关系元素的名称。
A "gotcha" 对我来说是 foreignKey
属性.
Loopback 文档说它是可选的 - 它是 - 但如果您将其省略,则会将 id
后缀添加到名称 (people
) 中,然后在您的customers
table。这一点没有很好地突出显示,但已经足够清楚了。
这部分没看清楚=>我本来以为foreignKey
值指向属性 的 Person
模型,所以我在这里使用了破折号 customer-id
属性。 这是不正确的。它实际上是在询问您数据库列名,这感觉有点反模式...如果您需要,您必须在属性中定义一个 columnName
引用 ORM 下的数据库列。
此外,请注意 foreignKey
属性 在关系中重复使用,但它对不同的 type
上下文意味着不同的事情。在 hasMany
中,它询问 "Which column there maps to the primary key here?"
Customer
型号:
{
"name": "Customer",
"plural": "customers",
"base": "PersistedModel",
"options": {
"validateUpsert": true,
"postgresql": {
"tableName": "customers"
}
},
"properties": {
"name": {
"type": "string"
},
"is-active": {
"type": "boolean",
"default": false,
"postgresql": {
"columnName": "is_active"
}
}
},
"validations": [],
"relations": {
"people": {
"type": "hasMany",
"model": "Person",
"foreignKey": "customer_id"
}
},
"acls": [],
"methods": {}
}
关系另一端的 Person
模型。
belongsTo
关系的 foreignKey
问的是相反的问题... "Which property here maps to the primary key there?"
此外,如果您有不想公开的属性(特别是如果您继承了一个模型并且出于某种原因没有 want/need 所有这些属性),那么您可以使用 hidden
元素。见下文。
{
"name": "Person",
"plural": "people",
"base": "User",
"idInjection": false,
"options": {
"validateUpsert": true,
"postgresql": {
"tableName": "people"
}
},
"hidden": [
"emailVerified",
"realm",
"username",
],
"properties": {
"first-name": {
"type": "string",
"postgresql": {
"columnName": "first_name"
}
},
"last-name": {
"type": "string",
"postgresql": {
"columnName": "last_name"
}
},
"email": {
"type": "string"
},
...
},
"validations": [],
"relations": {
"customer": {
"type": "belongsTo",
"model": "Customer",
"foreignKey": "customer_id"
}
},
"acls": [],
"methods": {}
}