如果记录不存在,并且 - 如果记录确实存在 - 更新其属性,我正在寻找一种干净的方法来创建具有一组属性的记录。我喜欢find_or_create_by_id调用中块的语法。这是我的代码:
@categories = Highrise::DealCategory.find(:all)
@categories.each do |category|
puts "Category: #{category.name}"
Category.find_or_create_by_id(category.id) do |c|
c.name = category.name
end
end
这里的问题是,如果记录存在但名称已更改,则不会更新。
寻找这个问题的干净解决方案......
答案 0 :(得分:4)
您可以编写自己的方法:
class ActiveRecord::Base
def self.find_by_id_or_create(id, &block)
obj = self.find_by_id( id ) || self.new
yield obj
obj.save
end
end
使用
Category.find_by_id_or_create(10) do |c|
c.name = "My new name"
end
当然,通过这种方式,您应该扩展method missing
方法,并以与其他find_by_something
方法相同的方式实现此方法。但是,为了做空,这就足够了。
答案 1 :(得分:1)
我编写了这个可用于不同场景的查找器。
最重要的是,它会在创建和更新时删除参数:id
。
使用:id
创建模型可能会导致MySql
或PostgreSQL
出现问题,因为Rails使用数据库的自动序列号。如果您使用:id
创建新模型实例,则可以获得UniqueViolation: ERROR: duplicate key value violates unique constraint
。
# config/initializers/model_finders.rb
class ActiveRecord::Base
def self.find_by_or_create(attributes, &block)
self.find_by(attributes) || self.create(attributes.except(:id), &block)
end
def self.find_by_or_create!(attributes, &block)
self.find_by(attributes) || self.create!(attributes.except(:id), &block)
end
def self.find_or_create_update_by(attributes, &block)
self.find_by(attributes).try(:update, attributes.except(:id), &block) || self.create(attributes.except(:id), &block)
end
def self.find_or_create_update_by!(attributes, &block)
self.find_by(attributes).try(:update!, attributes.except(:id), &block) || self.create!(attributes.except(:id), &block)
end
def self.find_by_or_initialize(attributes, &block)
self.find_by(attributes) || new(attributes.except(:id), &block)
end
end
答案 2 :(得分:1)
我一直在使用这个模式种子:
Category.find_or_initialize_by(id: category.id).update |c|
c.name = category.name
end
它的工作原理与Dale Wijnand和Teoulas的答案相同(仅保存实例一次),但使用的是您问题中的一个块。
答案 3 :(得分:0)
我昨天做了这个,希望有一种方法可以用一个班轮做。
结束(使用你的代码):
c = Category.find_or_initialize_by_id(category.id)
c.name = category.name
c.save
也许有一种更好的方式,但这就是我使用的方式。
[编辑:使用初始化而不是创建,以避免两次击中数据库)
答案 4 :(得分:0)
试试这个:
c = Category.find_or_initialize_by_id(category.id)
c.name = category.name
c.save!
这样,如果您调用find_or_create_by_id
(假设它是新记录),则只保存一次实例,而不是两次。
答案 5 :(得分:0)
我喜欢fl00r的回答。但为什么我们每次都要保存对象呢? 我们可以检查它是否已存在于记录中或者保存它
def self.find_or_create_by_id(id, &block)
obj = self.find_by_id(id)
unless obj
obj = self.create(id: id)
end
obj
end
答案 6 :(得分:0)
我认为最简单的方法是使用Ruby的tap方法,如下所示:
SELECT orderNumber FROM orders EXCEPT
SELECT DISTINCT orderdetails.orderNumber FROM orderdetails JOIN products
ON orderdetails.productCode == products.productCode
WHERE products.productLine == "Planes";
此外,我将Category.find_or_initialize_by(id: category.id).tap do |c|
c.name = category.name
c.save
end
更改为find_or_create_by_id
,否则它将两次访问数据库。
find_or_initialize_by
查找或初始化记录,然后将其返回。然后,我们点击它,进行更新并将其保存到数据库中。