模型中的Rails“自动”作用域

时间:2019-05-06 21:26:15

标签: ruby-on-rails activerecord

我目前面临以下问题。我有以下2种型号:

  1. PageElement

  2. 页面

页面has_many :page_elements,并且在has_many模型中添加了以下page_element关系:

  #PageElement model
  has_many :siblings, ->(pe) { where.not(id: pe.id) }, through: :page, class_name: "PageElement", source: :page_elements

因此,在名为set_position的方法中,我寻找同级的最高位置,并将其设置为高1个数字。


  def set_position
    if position.zero? && siblings.present?
      new_position = (siblings.order("position ASC").pluck(:position).last + 1 rescue 0)
      update(position: new_position) if position != new_position
    end
  end

但是,当我查看由siblings方法生成的查询时,由于某种原因,它会根据“当前page_element”自动进行范围调整:

  PageElement Load (0.6ms)  SELECT  "page_elements".* FROM "page_elements" WHERE "page_elements"."page_id" = $1 AND "page_elements"."element_type" = $2 ORDER BY "page_elements"."position" ASC LIMIT $3  [["page_id", 69], ["element_type", 6], ["LIMIT", 1]]

如您所见,这里的重要部分是它对“当前元素类型”(即6)的过滤。但这不是我期望的行为,因为我想获得所有同级兄弟,而没有任何种类过滤。

为什么rails会根据当前元素自动进行作用域设置?我还发现,它会在其他类型的列(例如字符串)中发生,如果我向该元素添加title,它也会过滤该标题字符串。

我知道一种解决方案是先取消范围的集合,像这样:

  has_many :siblings, ->(pe) { unscoped.where.not(id: pe.id) }, through: :page, class_name: "PageElement", source: :page_elements

但是,我想了解为什么首先要对它进行范围调整,以便我理解我没有做错任何事情。

更新:

我发现只有在以after_commit触发的方法中运行这种关系时,才会发生这种“作用域”。如果使用rails console我尝试做PageElement.first.siblings,则会得到正确的SQL查询:

irb(main):002:0> PageElement.last.siblings
  PageElement Load (0.7ms)  SELECT  "page_elements".* FROM "page_elements" ORDER BY "page_elements"."id" DESC LIMIT $1  [["LIMIT", 1]]
  PageElement Load (0.8ms)  SELECT  "page_elements".* FROM "page_elements" INNER JOIN "pages" ON "page_elements"."page_id" = "pages"."id" WHERE "pages"."id" = $1 AND "page_elements"."id" != $2 ORDER BY "page_elements"."position" ASC LIMIT $3  [["id", 79], ["id", 200], ["LIMIT", 11]]

UPDATE2:

经过进一步的挖掘,我发现问题是我正在使用where(element_type: 'something').first_or_create在种子文件中生成这些实例。通过使用first_or_create,“兄弟姐妹”关系将自动确定范围。我已经找出问题的原因,但没有找到原因的原因。

我们当前正在使用page_instance.page_elements.where(some_condition).first_or_create,以便我们可以一遍又一遍地运行种子文件而不会造成重复。

0 个答案:

没有答案