JCQuintas
JCQuintas

Reputation: 1138

Sequelize create include existing object

I'm having an issue with sequelize on create with associated model with an "unique" value. It works when I create a "book" with a new "category", but if the "category" already exists it throws an error because it tried to duplicate an "unique" value. Is there a way to make sequelize.create try to use findAndCreate on a associated model?


My setup

Book:

const Book = sequelize.define('book', {
  title: Sequelize.STRING,
})

Category:

const Category = sequelize.define('category', {
  id: Sequelize.BIGINT,
  name: {
    type: Sequelize.STRING,
    unique: true,
  },
})

and the relationship:

Book.belongsTo(Category, { foreignKey: 'category_id' })

So, when i create a book i can write:

Book.create(
  {
    title: 'Book',
    category: {
      name: 'Foo'
    }
  }, {
    include: [
      {
        model: Category
      }
    ]
  }
)

if after that I create another book:

Book.create(
  {
    title: 'Another Book',
    category: {
      name: 'Foo'
    }
  }, {
    include: [
      {
        model: Category
      }
    ]
  }
)

it throws the error saying category.name must be unique

Upvotes: 2

Views: 3120

Answers (1)

JCQuintas
JCQuintas

Reputation: 1138

For anyone with the same question, this was my final solution:

On my services.js I created the function create that receives a request(req) and all my models already initialized.

create: (req, models) => {
  const { category, ...rest } = req.body
  if (category && category.name && !rest.category_id) {
    return models.Category
      .findOrCreate({ 
        where: { 
          name: category.name 
        }
      })
      .then(([returnedCategory]) => {
        return models.Book.create(
          {
            ...rest, 
            category_id: returnedCategory.id 
          }, 
          { ...defaultOptions(models) }
        )
      })
    } else {
      return models.Book.create(rest, { ...defaultOptions(models) })
    }
},

defaultOptions looks like this:

const defaultOptions = models => {
  return {
    attributes: {
      exclude: ['category_id', 'created_at', 'updated_at'],
    },
    include: [
      { model: models.Category, attributes: ['id', 'name'] },
    ],
  }
}

Upvotes: 1

Related Questions