#node.js #postgresql #orm #sequelize.js
#node.js #postgresql #orm #sequelize.js
Вопрос:
Я использую sequelize.js с node.js и postgres. Я получил 2 простых таблицы из примера в качестве своего рода POC. Я изменил идентификатор на UUID, и у меня возникла проблема со вставкой во вторую таблицу (с UUID FK ).
Я использую postman для его тестирования. Я создаю строки задач с UUID без проблем, затем я пытаюсь создать элемент задач, который имеет идентификатор задачи в качестве внешнего ключа, и кажется, что он не распознает этот идентификатор!
Я попробовал ручной скрипт в postgres, и он сработал. Вероятно, я что-то упускаю из виду в коде, но я не могу понять, что именно.
вот ошибка, которая возвращается мне в postman —
{
"name": "SequelizeDatabaseError",
"parent": {
"name": "error",
"length": 96,
"severity": "ERROR",
"code": "22P02",
"file": "uuid.c",
"line": "137",
"routine": "string_to_uuid",
"sql": "INSERT INTO "TodoItems" ("id","content","complete","createdAt","updatedAt","todoId") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;"
},
"original": {
"name": "error",
"length": 96,
"severity": "ERROR",
"code": "22P02",
"file": "uuid.c",
"line": "137",
"routine": "string_to_uuid",
"sql": "INSERT INTO "TodoItems" ("id","content","complete","createdAt","updatedAt","todoId") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;"
},
"sql": "INSERT INTO "TodoItems" ("id","content","complete","createdAt","updatedAt","todoId") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;"
}
Вот соответствующие файлы js —
todoItems.js контроллер —
const TodoItem = require('../dal/models').TodoItem;
const uuid = require('uuid/v4');
module.exports = {
create(req, res) {
return TodoItem
.create({
content: req.body.content,
todoId: req.params.todoId,
})
.then(todoItem => res.status(201).send(todoItem))
.catch(error => res.status(400).send(error));
},
update(req, res) {
return TodoItem
.find({
where: {
id: req.params.todoItemId,
todoId: req.params.todoId,
},
})
.then(todoItem => {
if (!todoItem) {
return res.status(404).send({
message: 'TodoItem Not Found',
});
}
return todoItem
.update({
content: req.body.content || todoItem.content,
complete: req.body.complete || todoItem.complete,
})
.then(updatedTodoItem => res.status(200).send(updatedTodoItem))
.catch(error => res.status(400).send(error));
})
.catch(error => res.status(400).send(error));
},
destroy(req, res) {
return TodoItem
.find({
where: {
id: req.params.todoItemId,
todoId: req.params.todoId,
},
})
.then(todoItem => {
if (!todoItem) {
return res.status(404).send({
message: 'TodoItem Not Found',
});
}
return todoItem
.destroy()
.then(() => res.status(204).send())
.catch(error => res.status(400).send(error));
})
.catch(error => res.status(400).send(error));
},
};
todos.js контроллер-
const Todo = require('../dal/models').Todo;
const TodoItem = require('../dal/models').TodoItem;
module.exports = {
create(req, res) {
return Todo
.create({
title: req.body.title,
})
.then((todo) => res.status(201).send(todo))
.catch((error) => res.status(400).send(error));
},
list(req, res) {
return Todo
.findAll({
include: [{
model: TodoItem,
as: 'todoItems',
}],
order: [
['createdAt', 'DESC'],
[{ model: TodoItem, as: 'todoItems' }, 'createdAt', 'ASC'],
],
})
.then((todos) => res.status(200).send(todos))
.catch((error) => res.status(400).send(error));
},
retrieve(req, res) {
return Todo
.findByPk(req.params.todoId, {
include: [{
model: TodoItem,
as: 'todoItems',
}],
})
.then((todo) => {
if (!todo) {
return res.status(404).send({
message: 'Todo Not Found',
});
}
return res.status(200).send(todo);
})
.catch((error) => res.status(400).send(error));
},
update(req, res) {
return Todo
.findByPk(req.params.todoId, {
include: [{
model: TodoItem,
as: 'todoItems',
}],
})
.then(todo => {
if (!todo) {
return res.status(404).send({
message: 'Todo Not Found',
});
}
return todo
.update({
title: req.body.title || todo.title,
})
.then(() => res.status(200).send(todo))
.catch((error) => res.status(400).send(error));
})
.catch((error) => res.status(400).send(error));
},
destroy(req, res) {
return Todo
.findByPk(req.params.todoId)
.then(todo => {
if (!todo) {
return res.status(400).send({
message: 'Todo Not Found',
});
}
return todo
.destroy()
.then(() => res.status(204).send())
.catch((error) => res.status(400).send(error));
})
.catch((error) => res.status(400).send(error));
},
};
todo table create migration —
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('Todos', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
},
title: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
}),
down: (queryInterface /* , Sequelize */) => queryInterface.dropTable('Todos'),
};
todo-item table create migration —
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('TodoItems', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
},
content: {
type: Sequelize.STRING,
allowNull: false,
},
complete: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
todoId: {
type: Sequelize.UUID,
onDelete: 'CASCADE',
references: {
model: 'Todos',
key: 'id',
as: 'todoId',
},
},
}),
down: (queryInterface /* , Sequelize */) =>
queryInterface.dropTable('TodoItems'),
};
модель задач —
const uuid = require('uuid/v4');
'use strict';
module.exports = (sequelize, DataTypes) => {
const Todo = sequelize.define('Todo', {
title: {
type: DataTypes.STRING,
allowNull: false,
}
});
Todo.associate = (models) => {
Todo.hasMany(models.TodoItem, {
foreignKey: 'todoId',
as: 'todoItems',
});
};
Todo.beforeCreate((item, _ ) => {
return item.id = uuid();
});
return Todo;
};
модель элемента задач —
const uuid = require('uuid/v4');
'use strict';
module.exports = (sequelize, DataTypes) => {
const TodoItem = sequelize.define('TodoItem', {
content: {
type: DataTypes.STRING,
allowNull: false,
},
complete: {
type: DataTypes.BOOLEAN,
defaultValue: false,
}
});
TodoItem.associate = (models) => {
TodoItem.belongsTo(models.Todo, {
foreignKey: 'todoId',
onDelete: 'CASCADE',
});
};
TodoItem.beforeCreate((item, _ ) => {
return item.id = uuid();
});
return TodoItem;
};
Ответ №1:
Как выглядит код вашего маршрутизатора? Используете ли вы правильный параметр path для todoId? Если вы используете экспресс, например. это должно выглядеть так app.post("/todos/:todoId/todo_items", todoItemController.create)
. Обратите внимание на todoId camelcase . Это гарантирует, что объект, на который req.params.todoId
вы ссылаетесь в контроллере TodoItems, будет иметь правильное значение.
Кроме того, убедитесь, что у вас есть правильный анализатор тела для правильной обработки req.body.content. В express это было бы сделано с помощью библиотеки body body-parser и app.use(bodyParser.json())
. Добавьте точку останова или инструкцию log в код создания контроллера TodoItem и убедитесь, что у вас действительно есть правильные значения параметров.
Комментарии:
1. К сожалению, я удалил код элементов задач и заменил его своими реальными моделями, поэтому я не могу знать наверняка, но сейчас все работает хорошо. Я почти уверен, что проблема действительно была связана с неправильным присвоением имен в путях маршрутизатора.
Ответ №2:
Если у вас возникла ошибка, описанная выше, это может быть связано с тем, что вы вкладываете другие объекты в тело вашего запроса, и поэтому UUID не преобразуется из string в UUID.
Например, если у вас есть тело запроса, например
{
"Transaction": {
"id" : "f2ec9ecf-31e5-458d-847e-5fcca0a90c3e",
"currency" : "USD",
"type_id" : "bfa944ea-4ce1-4dad-a74e-aaa449212ebf",
"total": 8000.00,
"fees": 43.23,
"description":"Description here"
},
}
и поэтому в вашем контроллере вы создаете свою сущность, например
try {
await Transaction.create(
{
id: req.body.Transaction.id,
currency: req.body.Transaction.currency,
type_id: req.body.Transaction.type_id,
total: req.body.Transaction.total,
fees: req.body.Transaction.fees,
description: req.body.Transaction.description,
}......
Ваши id
и type_id
, скорее всего, не преобразуются из строки в UUID.
Существует несколько способов решения этой проблемы. Самый простой подход — выполнить явное преобразование из string в UUID.
Для этого выполните импорт parse
из uuid npm module
и выполните явное преобразование, как вы можете видеть в примере кода ниже.
const { parse: uuidParse } = require("uuid");
try {
await Transaction.create(
{
id: uuidParse(req.body.Transaction.id),
currency: req.body.Transaction.currency,
type_id: uuidParse(req.body.Transaction.type_id),
total: req.body.Transaction.total,
fees: req.body.Transaction.fees,
description: req.body.Transaction.description,
}.....
Это явное преобразование из строки в UUID в основном решит проблему.