rails模型创建 - 如果无效则跳过创建?

时间:2014-01-04 01:38:59

标签: ruby-on-rails ruby activerecord

这似乎很容易。我正在分批创建具有唯一ID的东西。在数据库上验证了唯一性,并在模型级别进行了验证。如果它存在,是否有快速轨道技巧才能跳过记录?

Box控件

def create
  @box = Box.find(params[:box_id])
  start = params[:start].scan(/\d/).join('').to_i
  params[:quantity].to_i.downto(1) do
    UniqueNumber.create(uin: 'ERB' + "%07d" % start, box_id: @box.id, active: true)
    start += 1
  end
  redirect_to @box
end

所以......如果数据库中存在框561 ......我告诉系统,从500开始并添加100个框,我希望它跳过561而不是对我大喊大叫。

我正在考虑在控制器中执行此操作:

params[:quantity].to_i.downto(1) do
  if Box.where(uin: 'ERB' + "%07d" % start).blank?
    UniqueNumber.create(uin: 'ERB' + "%07d" % start, box_id: @box.id, active: true)
    start += 1
  end
end

但是我不禁想到模型级别内置了什么东西?

2 个答案:

答案 0 :(得分:1)

您建议的方法确实已经可以使用。请参阅rails docs中的find_or_create_by

更好的方法,因为您知道要创建的框的范围,将对范围内的所有框执行单个查询,然后从创建循环中排除这些框。这样,在开始插入之前,您只需要进行一次查找。

答案 1 :(得分:1)

由于UniqueNumber个实例同时包含uinactivebox_id属性,但您想要对{{{}进行验证1}} - 您可以使用create_with链接find_or_create_by以生成所需的查询:

uin

为了说明,假设UniqueNumber.create_with(box_id: @box.id, active: true).find_or_create_by(uin: 'ERB' + "%07d" % start) UniqueNumber uin已存在:

ERB0000005

假设它是UniqueNumber.first #=> #<UniqueNumber id: 1, uin: "ERB0000005", box_id: 1, active: true> 表中的唯一条目,则以下情况属实:

unique_numbers

无论您是否对返回的记录执行任何操作(在您的情况下,您不应该这样做),只有在# Creates a NEW record UniqueNumber.create_with(box_id: 2, active: true).find_or_create_by(uin: "ERB0000004") #=> #<UniqueNumber id: 2, uin: "ERB0000004", box_id: 2, active: true> # Returns an EXISTING record UniqueNumber.create_with(box_id: 2, active: true).find_or_create_by(uin: "ERB0000005") #=> #<UniqueNumber id: 1, uin: "ERB0000005", box_id: 1, active: true> UniqueNumber查询时,您才会创建新记录还没有存在。因此,在代码中,您可以实现以下内容:

uin

<强>更新

回复OP's follow-up comment回答:

  

[W]这应该与UniqueNumber.where不同(uin:'ERB'+“%07d”%start,box_id:@ box.id,active:true).first_or_create

答案是,这显然是不同的。请看以下示例:

params[:quantity].to_i.downto(1) do
  UniqueNumber.create_with(box_id: @box.id, active: true).find_or_create_by(uin: 'ERB' + "%07d" % start)
  start += 1
end

您希望第二个查询返回现有记录,因为# Returns an EXISTING record UniqueNumber.where(uin: "ERB0000005", box_id: 1, active: true).find_or_create #=> #<UniqueNumber id: 1, uin: "ERB0000005", box_id: 1, active: true> # Creates a NEW record – even though it should not UniqueNumber.where(uin: "ERB0000005", box_id: 2, active: true).find_or_create #=> #<UniqueNumber id: 3, uin: "ERB0000005", box_id: 2, active: true> 已存在,但它实际上是在创建记录。为什么?由于您的uin子句必须与所有三个属性匹配,但您希望与where匹配。因此,对于uin,您可能会无意中创建具有相同first_or_create值的多个UniqueNumber对象。

但是,如果您要将uin子句限制为where属性,则可以将其余属性传递给{{1创作:

uin

在上面的例子中,将first_or_create# Returns an EXISTING record UniqueNumber.where(uin: "ERB0000005").find_or_create(box_id: 3, active: true) #=> #<UniqueNumber id: 1, uin: "ERB0000005", box_id: 1, active: true> # Creates a NEW record – as it should UniqueNumber.where(uin: "ERB0000006").find_or_create(box_id: 3, active: true) #=> #<UniqueNumber id: 4, uin: "ERB0000006", box_id: 3, active: true> 链接起来与将wherefind_or_create链接起来的效果相同。我更喜欢后者,因为它更清楚/更明确地发生了什么,IMO,但两者之间的偏好可能归结为学术上的区别。