我有一个需要发送活动用户邀请的应用。当用户邀请朋友(用户)参加活动时,如果尚不存在将用户连接到该事件的新记录,则创建该记录。我的模型由user,event和events_user组成。
class Event
def invite(user_id, *args)
user_id.each do |u|
e = EventsUser.find_or_create_by_event_id_and_user_id(self.id, u)
e.save!
end
end
end
用法
Event.first.invite([1,2,3])
我不认为以上是完成任务的最有效方法。我设想了像
这样的方法 Model.find_or_create_all_by_event_id_and_user_id
但其中一个不存在。
没有验证的模型
class User
has_many :events_users
has_many :events
end
class EventsUser
belongs_to :events
belongs_to :users
end
class Event
has_many :events_users
has_many :users, :through => :events_users
end
答案 0 :(得分:7)
首先获取所有现有记录然后创建所有缺失记录可能会更快:
class Event
def invite(user_ids, *args)
existing_user_ids = event_users.where(user_id: user_ids).map(&:user_id)
(user_ids - existing_user_ids).each do |u|
event_users.create(user_id: u)
end
end
end
这样,如果所有event_users
都已存在,则只进行1次查询。但是,如果不存在event_users
,则此方法会执行额外查询 - 与每个EventUser
创建所需的查询数相比较。
答案 1 :(得分:1)
最高效的是什么意思?我将假设,通过高效,你的意思是高性能,而不是优雅,干燥,可维护的代码等。
从数据库的角度来看,如果要在数据库中插入100条记录,这将转换为100“INSERT INTO events_models VALUES(x,x)”sql查询(可能是100“SELECT COUNT(*)..”查询是否还有唯一性验证)。因此,即使您想要的方法将在AR中实现,它仍然会在属性数组上有一个循环,并在每个event_id,user_id对上保存。)
从Ruby / Rails的角度来看,如果你想在你的模型上有验证/回调/等,那么你必须在一个循环中逐个创建一个ActiveRecord实例。现在,如果要超级优化方法(删除ActiveRecord类的实例化),可以手动编写sql查询(从而节省一些时间和内存)。然而,与风险相比,收益微乎其微。
顺便说一下,
e.save!
不是必需的:
相同的动态查找器样式可以 用于创建对象,如果它 尚不存在。这种动态 发现者被称为 find_or_create_by_并将返回 对象,如果它已经存在和 否则创建它,然后返回它。
答案 2 :(得分:1)
如果在连接模型中没有任何验证或回调,最快的方法是原始sql:
self.connection.execute(%Q{insert into events_users (event_id,user_id)
select distinct events.id,users.id from events,users
where events.id = #{self.id}
and users.id in ( #{user_ids.join(",")} )
}
)
在进行上述调用之前,您可以编写一个查询来消除user_ids中的现有记录。我有一个previous question about this,特别是关于没有模型的连接表。
由于您有连接模型,因此可以使用ar-import gem:
books = []
10.times do |i|
books << Book.new(:name => "book #{i}")
end
Book.import books