Rails与表单collection_select的多态关联,没有嵌套

zec*_*htz 4 polymorphic-associations ruby-on-rails-4

我的应用程序中有以下模型

class User < ActiveRecord::Base
has_many :articles
has_many :tour_companies
has_many :accomodations
end

class Articles < ActiveRecord::Base
belongs_to :user
belongs_to :bloggable, :polymorphic => true
end

class TourCompany < ActiveRecord::Base
belongs_to :user
has_many :articles, :as => :bloggable
end

class Accommodation < ActiveRecord::Base
belongs_to :user
has_many :articles, :as => :bloggable
end
Run Code Online (Sandbox Code Playgroud)

现在我的问题是我想要一个登录用户能够写一篇文章并使用表单collection_select来选择他/她的旅游公司或文章应该与之相关的住宿,我如何在rails 4中做到这一点?如何从表单集选择中选择bloggable类型和id?我不想要嵌套资源

Ans*_*son 12

从 Rails 4.2 开始,这可以通过rails/globalid处理。这个较新的选项被 Rails 的 ActiveJob 使用。它使解析和查找设置起来非常简单。

首先,检查您Gemfile.lockglobalid. 对于 Rails 5,它包括在内。

神奇的一切都发生在模型中......

文章型号:

# Use :to_global_id to populate the form
def bloggable_gid
  bloggable&.to_global_id
end

# Set the :bloggable from a Global ID (handles the form submission)
def bloggable_gid=(gid)
  self.bloggable = GlobalID::Locator.locate gid
end
Run Code Online (Sandbox Code Playgroud)

要了解它的作用,请打开一个rails console. 玩弄gid = TourCompany.first.to_global_idGlobalID::Locator.locate gid

现在你的其余代码是股票 Rails 的东西......

文章控制器:

# consider building the collection in the controller.
# For Rails 5, this would be a `before_action`.
def set_bloggables
  @bloggables = TourCompany.all + Accomodation.all
end

# permit :bloggable_gid if you're using strong parameters...
def article_params
  params.require(:article).permit(:bloggable_gid)
end
Run Code Online (Sandbox Code Playgroud)

文章形式:

<%= f.collection_select(:bloggable_gid, @bloggables, :to_global_id, :to_s) %>
Run Code Online (Sandbox Code Playgroud)

对于更多的演练Simple Polymorphic Selects with Global IDs博客文章很有帮助。

  • 在 2020 年(以及 Rails 6),这绝对是正确的答案。谢谢! (2认同)

zec*_*htz 5

所以我设法最终做到了.这是我在views/articles/_form.html.erb中的表现

<div class="row">
<% bloggable_collection = TourCompany.all.map{|x| [x.title, "TourCompany:#{x.id}"]} +
                          Accomodation.all.map{|x| [x.title, "Accomodation:#{x.id}]}
%>
<p>Select one of your listing this article is associated with</p>
<%= f.select(:bloggable, bloggable_collection,:selected =>"#{f.object.bloggable_type}:#  {f.object.bloggable_id}" ) %>
</div>
Run Code Online (Sandbox Code Playgroud)

然后在文章控制器中

#use regular expression to match the submitted values
def create
bloggable_params = params[:article][:bloggable].match(/^(?<type>\w+):(?<id>\d+)$/)
params[:article].delete(:bloggable)

@article = current_user.articles.build(article_params)
@article.bloggable_id         =  bloggable_params[:id]
@article.bloggable_type       =  bloggable_params[:type]
if @article.save
  redirect_to admin_root_url, :notice => "Successfully created article"
else
  render 'new', :alert => "There was an error"
end
end
Run Code Online (Sandbox Code Playgroud)

它应该工作!