模型 before_save 未使用来自 params/controller 的更新的多选顺序

Model before_save not using updated multiselect order coming from params/controller

在表单中,我有一个可以手动排序的多选下拉列表。当我在编辑时提交表单时,我在参数中得到以下内容:

"article" =>{"tag_ids"=>["", "4", "1", "2"]}

哪个是我想要的,而不是1、2、4。

然后,我在模型中更新序号。我无法从参数中获取 tag_id 的新订单。当我调用 article_tags 时,我得到 table 的顺序,而不是参数中的顺序。我假设这是因为 tag_ids 没有变化(相同的条目,不同的顺序)。有没有一种方法可以在模型本身中访问该订单?

我可以使用隐藏输入或 attr_accessors 做一些变通方法,但我想知道是否有办法从模型中做到这一点。

控制器:

def update
 if @article.update(article_params)
  redirect_to articles_path
 end
end

def article_params
 params.require(:article).permit(:title, tag_ids: [])
end

型号:

class Article
 has_many: :article_tags
 has_many: :tags, through: :article_tags

 before_save: :order

 def order
  self.article_tags.each_with_index do |i, idx|
   i.update_attribute(:ordinal, idx) 
  end
 end
end

class ArticleTag
 belongs_to: :article
 belongs_to: :tag
end

表格

articles
 id  |   title
------------------
  1  |  Testing

tags
 id  |   name
------------------
  1  |  Business
  2  |  Education
  3  |  Health
  4  |  Social

article_tags
 id  |   article_id  |  tag_id  | ordinal
------------------------------------------
  1  |       1       |     1    |   2
  2  |       1       |     2    |   3
  3  |       1       |     4    |   1

您可以通过重写 tag_ids= setter 创建的方法来做到这一点 has_many: :tags, through: :article_tags

class Article
  # ...
  def tags_ids=(ids)
    ids.reject(&:blank?).each_with_index do |id, index|
      article_tag = self.article_tags.find_or_intialize_by(tag_id: id)
      article_tag.update(
        ordinal: index
      )
      article_tag.where.not(tag_id: ids).destroy_all
    end
  end
end