环回设置某个属性,该属性以某种方式无法持久化到数据库sql?

时间:2018-08-16 18:38:22

标签: loopbackjs

我们正在将我们的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 混合(我们自己的)中,这些属性会相应地设置

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时,它是否以某种方式忽略/删除了updated-by的值? (第二个参数应该是1,而不是null

LoopbackJS  | 2018-08-16T17:57:23.666Z loopback:connector:postgresql SQL: UPDATE "public"."asset_types" SET "is_active"=$1,"updated_by"=$2,"updated_on_utc"=$3::TIMESTAMP WITH TIME ZONE,"tenant_id"=$4 WHERE "id"=$5
LoopbackJS  | Parameters: [false,null,"2018-08-16T17:57:23.660Z",1,5]

updated_by在Postgres中可以为空,因此不应该产生错误...但是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返回相同的错误。

1 个答案:

答案 0 :(得分:0)

其根本原因是由于不正确的关系。

我创建了此as a gist,但也将其粘贴到此处以防其他人使用。

使用小写名称是一种PostgreSQL的最佳实践,如果需要,可以使用蛇形。即 my_column_name

此外,由于我使用的是JSON API客户端,因此我安装了 excellent (优秀) loopback-component-jsonapi来处理反序列化的事情……但这只是添加其他复杂性。

JSON API要求使用反斜线的属性名称。当您以my-property-name之类的名称开始时,默认情况下,Loopback或PostgreSQL驱动程序(没关系)会将破折号属性折叠为mypropertyname

这很糟糕……尤其是当您拥有正在使用的现有架构时。

处理关系时,情况会更糟,因为默认情况下,回送会附加id后缀,因此,除非碰巧有一个mypropertynameid,否则您现在就遇到了问题。列。

一个例子

假设我们有一个Customer模型。我需要使用小写的端点(在适用的情况下也应使用反斜线表示),因此只需在此处更改复数即可。

{ 
  "name": "Customer",
  "plural": "customers",
  "base": "PersistedModel",
   ...
 }

options.postgresql内,您可以设置tableName。默认情况下,环回将使用name值,但请记住PostgreSQL不喜欢CamelCase。除非使用小写型号名称,否则您需要覆盖它。

(这是一种宗教偏爱,但是我希望我的桌子可以是复数形式。与我战斗。)

{ 
  ...
  "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有效负载的关系元素的名称。

对我来说,这里的“陷阱”是foreignKey属性。

Loopback文档说它是可选的-是的-但是,如果省略它,则会将id后缀添加到名称(people)中,然后在{{ 1}}表。突出显示的不是很好,但是很清楚。

这部分内容不清楚=> 我本来以为customers值指向foreignKey模型的属性,所以我在此处具有经过虚线处理的Person属性。 这是不正确的。这实际上是在询问您数据库列名,这听起来有点像反样式...在属性中,如果要引用,则必须定义一个customer-id在ORM下的db列。

此外,请注意,columnName属性在关系中被重用,但对于不同的foreignKey上下文而言,意味着不同的含义。在type中,询问“哪个列映射到主键这里?”

最终的hasMany模型:
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关系的foreignKey提出了相反的问题……“哪个属性此处映射到主键? “

此外,如果您有不想公开的属性(尤其是继承了模型并且出于某种原因不希望/需要所有这些属性),则可以使用belongsTo隐藏它们元件。见下文。

hidden