ActiveRecord的更新方法失败,因为UNIQUE约束失败:items.id

时间:2015-11-07 17:22:39

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord

在这种情况下,

product = <Item id: 4, url: nil, memo: nil, created_at: "2015-11-07 09:48:36", updated_at: "2015-11-07 09:48:36", Totalweight: 390.0, Perweight: nil, price: 1000>
attr = {"id"=>4, "tag_list"=>"peanuts", "price"=>1000, "url"=>nil, "Totalweight"=>390, "memo"=>nil}

我确实更新了Item的记录。

product.update!(attr)

但错误说,

SQLite3::ConstraintException UNIQUE constraint failed: items.id
!! #<ActiveRecord::RecordNotUnique: SQLite3::ConstraintException: UNIQUE constraint failed: items.id: INSERT INTO "items" ("id", "price", "Totalweight", "created_at", "updated_at", "url") VALUES (?, ?, ?, ?, ?, ?)>

当然id是一样的,因为我想更新记录。 我试过了

product.update_attributes(attr)

也显示相同的错误。

问题是如何更新此Item对象?

如果未设置id,则ActiveRecord的保存方法正常工作。

此外,更多信息, 我使用导入Excel,CSV文件并可以解析的gem'roo'。 https://github.com/roo-rb/roo 参数行来自gem'roo'

这是代码,

  COLUMN = ["id","tag_list","price","url","Perweight","Totalweight", "memo", "id", "created_at", "updated_at"]

  def self.import(file)
    spreadsheet = open_spreadsheet(file)
    header = spreadsheet.row(1)
    (2..spreadsheet.last_row).each do |i|
      row = Hash[[header, spreadsheet.row(i)].transpose]
      if Item.find(row["id"]) then
        product = Item.new
        attr = row.slice(*COLUMN)
        # product.attributes = attr
        product.update(attr)
      else
        product = Item.new
        attr = row.slice(*COLUMN)
        product.attributes = attr
        product.save!
      end
    end
  end

  def self.open_spreadsheet(file)
    case File.extname(file.original_filename)
    when ".csv" then
      Roo::Spreadsheet.open(file.path, extension: :csv)
    when ".xls" then
      Roo::Spreadsheet.open(file.path, extension: :xls)
    when ".xlsx" then
      Roo::Spreadsheet.open(file.path, extension: :xlsx)
    else raise "Unknown file type: #{file.original_filename}"
    end
  end

1 个答案:

答案 0 :(得分:1)

您已经检查过物品是否存在 - 这很好。 但是,您并未升级现有的商品,即使已存在具有相同ID的商品,您也会创建一个新商品,这就是您收到错误的原因。

有两种方法可以解决这个问题 - 第一种方法有点冗长,但更接近你已有的方法:

product = Item.find(row["id"])
if product then
  attr = row.slice(*COLUMN)
  # Update the existing product
  product.update(attr)
else
...
end

但是,既然你对产品对象做了同样的事情,无论它是否存在,你也可以这样做:

# This will either find an existing product, or initialize a new one with the ID
product = Item.find_or_initialize_by(id: row["id"])
attr = row.slice(*COLUMN)
product.attributes = attr
product.save!