Loopbackjs 环回设置一个属性,该属性以某种方式不';不能持久化到数据库sql?
我们正在将api从C#移植到LoopbackLoopbackjs 环回设置一个属性,该属性以某种方式不';不能持久化到数据库sql?,loopbackjs,Loopbackjs,我们正在将api从C#移植到Loopback^v3.19.0,并且遇到了一个拦截器 我们的许多模型都具有共享属性,因此我们创建了一个基础模型“base”,它们从中继承 { "name": "Base", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "mixins": { "Timestamp": {} }, "proper
^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"
}
}
},
...
}
在时间戳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 **************
但是,当环回执行更新SQL时,它会以某种方式忽略/删除由更新的的值?(第二个参数应该是1
,而不是null
)
Postgres中由
更新的可为空,因此不应生成错误。。。但是环回发送的是一个字符串化的函数吗
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)
LoopbackJS | 2018-08-16T18:04:12.522Z环回:连接器:postgresql错误:整数的输入语法无效:“函数(){[本机代码]}”
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)
TLSSocket处的环回JS |。(/home/src/back-end/node_modules/pg/lib/connection.js:119:22)
emitOne上的LoopbackJS(events.js:115:13)
LoopbackJS |位于TLSSocket.emit(events.js:210:7)
LoopbackJS|位于addChunk(_stream_readable.js:264:12)
LoopbackJS | at readableAddChunk(_stream_readable.js:251:11)
LoopbackJS |位于TLSSocket.Readable.push(_stream_Readable.js:209:10)
LoopbackJS |位于TLSWrap.onread(net.js:587:20)
如果我们不点击updated\u by
列,则SQL是正确的,并且会更新
顺便说一句,如果我们软删除并且deleted\u by
列正在运行,同样的事情也会发生
感觉就像我在绕圈子,可能忽略了一些基本的东西。有什么建议吗
编辑
所以它似乎并不局限于一个混合。。。当我们完全删除它并手动设置有效负载中的k:v对(即“创建人”:1)时,我们仍然从Postgres返回相同的错误。这一错误的根本原因是由于不正确的关系
我创建了这个,但也将它粘贴在这里,以防它对其他人有帮助
使用小写名称是PostgreSQL的最佳实践,如果需要,可以使用snakecase。我的名字
另外,由于我使用的是客户机,所以我安装了优秀的来处理反序列化内容。。。但这只会增加额外的复杂性
JSON API调用dasherized属性名。当您以类似于my property name
的内容开始时,默认情况下,环回或PostgreSQL驱动程序(其实并不重要)会将dasherized属性向下折叠为mypropertyname
这很糟糕。。。尤其是当您有一个正在使用的现有模式时
处理关系时情况更糟,因为默认情况下,环回还会附加id
后缀,所以现在您会遇到问题,除非您碰巧有mypropertynameid
列
一个例子
假设我们有一个客户
模型。我需要小写的端点(并且在适用的情况下使用dasherised),所以只需将复数形式更改为与此处匹配即可
{
"name": "Customer",
"plural": "customers",
"base": "PersistedModel",
...
}
在选项.postgresql
中,可以设置表名
。默认情况下,环回将使用名称
值,但请记住PostgreSQL不喜欢CamelCase。除非使用小写的模型名称,否则需要重写此选项
(这是一种宗教偏好,但我喜欢我的桌子是复数的。跟我打吧。)
回到属性,使用postgresql.columnName
属性映射到数据库中正确的列名。如果它不是dasherized属性名(即status
),则可以忽略postgresql.columnName
位
{
...
"properties": {
"is-active": {
"type": "boolean",
"default": false,
"postgresql": {
"columnName": "is_active"
}
}
}
}
人际关系可能令人头痛。
假设我们的客户
有人在那里工作。要做一个基本的一个多个模型之间的关系
{
...
"relations": {
"people": {
"type": "hasMany",
"model": "Person",
"foreignKey": "customer_id"
}
},
...
}
people
是JSON API负载的relationship元素的名称
对我来说,这里的“抓到”是foreignKey
属性。
循环文档说它是可选的,而且是-但是如果您不使用它,它会将id
后缀添加到名称(person
),然后在customers
表中查找该列。这并没有很好地突出显示,但已经足够清楚了
这部分不清楚=>我最初认为foreignKey
值指向Person
模型的属性,因此我在这里使用了dasherised客户id
属性这是不正确的。它实际上是在询问数据库列名,这感觉有点像反模式。。。如果要引用ORM下的db列,则必须在属性中定义一个columnName
另外,请注意,foreignKey
属性在关系中重用,但它对不同的类型
上下文意味着不同的内容。在一个hasMany
中,它询问“此处哪个列映射到主键?”
最终客户
型号:
{
"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
提出了相反的问题。。。“此处的哪个属性映射到此处的主键?”
此外,如果您有不希望公开的属性(特别是如果您继承了一个模型,并且不希望/不需要w的所有这些属性)
{
...
"relations": {
"people": {
"type": "hasMany",
"model": "Person",
"foreignKey": "customer_id"
}
},
...
}
{
"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": {}
}
{
"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": {}
}