更新嵌套属性时,Rails作用域唯一性约束会引发错误

时间:2015-05-21 09:07:02

标签: ruby-on-rails ruby nested-attributes unique-constraint updating

我有一个应用程序,可以从一系列可用于指定页面布局的小部件创建称为活动页面的页面。我正在处理加载和编辑活动页面内容,更改内容以及更新该页面相关条目的功能。页面上特定小部件的顺序存储在名为page_display_order的列中。页面显示顺序在整个广告系列页面中是唯一的,因为两个小部件不能位于页面上的同一位置。这是有时在更新页面时会引发错误的列。

我属于广告系列页面的小部件模型如下所示:

class CampaignPagesWidget < ActiveRecord::Base

  belongs_to :campaign_page, inverse_of: :campaign_pages_widgets
  belongs_to :widget_type
  has_one :actionkit_page

  validates_presence_of :content, :page_display_order, :campaign_page_id, :widget_type_id
  # validates that there are not two widgets with exactly same content on the same campaign page
  # and that the page display order integer is unique across the widgets with that campaign page id
  validates_uniqueness_of  :page_display_order, :content, :scope => :campaign_page_id
end

这是在编辑页面时请求更新(在campaign_pages_controller.rb中)从页面读取窗口小部件数据的方式。它首先找出它是哪种类型的小部件(存储在小部件的表和模型中),然后使用页面编辑中指定的小部件的内容填充对象,并将其推送到指定嵌套属性的数组中。更新广告系列页面时更新:

@campaign_page = CampaignPage.find params[:id]
@widgets = @campaign_page.campaign_pages_widgets

permitted_params = CampaignPageParameters.new(params).permit
widget_attributes = []
params[:widgets].each do |widget_type_name, widget_data|
      # widget type id is contained in a field called widget_type:
      widget_type_id = widget_data.delete :widget_type
      widget = @widgets.find_by(widget_type_id: widget_type_id)
      widget_attributes.push({
        id: widget.id,
        widget_type_id: widget_type_id,
        content: widget_data,
        page_display_order: i})
      i += 1
    end

    permitted_params[:campaign_pages_widgets_attributes] = widget_attributes
    @campaign_page.update! permitted_params.to_hash
    redirect_to @campaign_page

在某些情况下,这会按预期更新和重定向,有时会抛出以下错误:

  

CampaignPagesController#update中的ActiveRecord :: RecordInvalid   验证失败:Campaign页面小部件页面显示顺序   已经拍摄,广告系列页面小部件无效

我是Ruby和Rails的新手,非常感谢我可能做错的建议,以及我应该如何做得更好。我不太了解如何做事和Rails方式&#39;而且我知道我必须重构控制器中的代码。

更新:仅当有多个小部件时才会抛出错误。如果我注释掉范围的唯一性约束,事情就可以了。它确实更新现有条目而不是创建新条目,因此不会成为问题。

1 个答案:

答案 0 :(得分:0)

这种情况正在发生,因为小部件更新不会一次全部发生 - 它们一次运行一个。 Rails通过查询具有相同数据的现有行的数据库来检查唯一性,如果找到的ID与您要保存的对象的ID不同,则会停止。例如,假设您有

| id | campaign_page_id | page_display_order |
| 10 | 100              | 1                  |
| 20 | 100              | 2                  |

当您尝试将窗口小部件顺序切换到[10, 2] [20, 1]时,Rails开始保存窗口小部件10并检查其新订单(2)是否唯一。但是,它已经找到了具有该订单的小部件20的行,并引发了您所看到的错误。

查看acts as list,这是一个可以为您管理此内容的宝石。否则,我认为您需要绕过Rails并手动执行位置更新。