问题
I'm trying to learn how to use polymorphic associations in my Rails 5 app.
I have models called Organisation, Proposal and Package::Bip.
The associations are:
Organisation
has_many :bips, as: :ipable, class_name: Package::Bip
accepts_nested_attributes_for :bips, reject_if: :all_blank, allow_destroy: true
Proposal
has_many :bips, as: :ipable, class_name: Package::Bip
accepts_nested_attributes_for :bips, reject_if: :all_blank, allow_destroy: true
Package::Bip
belongs_to :ipable, :polymorphic => true, optional: true, inverse_of: :bip
Package::Bip can be associated with either of Organisation or Proposal. I'm struggling to figure out how to show the Package::Bips that only belong to proposal in my proposal show and the same for organisation.
My package::bip table has these 2 columns:
# ipable_id :integer
# ipable_type :string
The type gets set to either Proposal or Organisation.
In my proposal show, I have:
<% if @proposal.bips.present? %>
<%= link_to package_bips_path(@proposal) do %>
<% end %>
I think the (@proposal) should be the constraining factor in deciding which package_bip instances to return. But, it isn't. I'm not sure what it's doing because all package_bips get rendered (including those that are associated with an organisation).
In my proposal controller, I have:
def show
@images = @proposal.images
@bips = @proposal.bips.where(ipable_type: 'Proposal')
end
I added the above where scope to try and force the show to use only those that have Proposal set in the ipable_type attribute - but that doesnt do any thing to help.
I'm not sure if I'm supposed to make an if statement in the Package::Bips controller to see whether the ipable_type is proposal. I don't understand how to exctract associated instances of the polymorphic object so that I can just show the relevant ones in an index on the proposal show page (with the index being populated only with the instances that belong to the proposal).
I tried writing the index action of the Package::Bips controller as:
def index
if params[:ipable_type]
@bips = Package::Bip.where(ipable_type: 'Proposal')
elsif params[:ipable_type]
@bips = Package::Bip.where(ipable_type: 'Organisation')
else
@bips = Package::Bip.all
end
# authorize @bips
end
That doesnt do anything at all. So I'm not sure why it at least doesnt give an error message.
Rendering the right view
In my proposals/show - I have:
<%= link_to package_bips_path(@proposal) do %>
That link goes to my views/package/bips/index.html. That view has:
<% @bips.each do |ip| %>.
That is currently listing out all of the bips (for both organisation and proposal). I thought that the @proposal in the link in proposal/show might constrain things, or the index action method in my bips controller might(above) might help. But neither of these things filter the instances.
If I write:
<%= @proposal.bips.first.title %>
in my proposals show view, it works to render the title.
Now my challenge is how to let the bip index view render the correct set of instances (only proposal or only organisation) depending on which page sent the request to render that view. Maybe I could write request referer to see if its an organisation controller or proposals controller request?
TRYING TO RENDER THE VIEW
views/proposals/show.html.erb
<% if @proposal.bips.present? %>
<%= link_to package_bips_path(@proposal) do %>
<h6 style="margin:2%; "> Intellectual Property </h6>
<% end %>
<% end %>
views/bips/index.html.erb
<% @bips.each do |ip| %>
<%= ip.title.titleize %>
<%= ip.classification.humanize %>
<% end %>
views/bips/show.html.erb
<%= @bip.title.titleize %></h5><p><%= @bip.status %>
proposals controller
def index
@proposals = Proposal.all
# @bips = @proposal.bips
# authorize @proposals
end
def show
@images = @proposal.images
@bips = @proposal.bips#.where(ipable_type: 'Proposal')
end
bips controller
def index
# if params[:status]
# @bips = Package::Bip.where(:status => params[:status])
# else
# @bips = Package::Bip.all
# end
if params[:ipable_type]
@bips = Package::Bip.where(ipable_type: 'Proposal')
elsif params[:ipable_type]
@bips = Package::Bip.where(ipable_type: 'Organisation')
else
@bips = Package::Bip.all
end
# authorize @bips
end
New attempt
I found this:
http://rails-bestpractices.com/posts/2010/09/23/generate-polymorphic-url/
I don't understand whether 'parent' where used in the article is some kind of keyword, or if i need to define it somewhere.
Im trying to nest my routes, so now:
resources :proposals do
namespace :package do
resources :materials
resources :insights
resources :facilities
resources :participants
resources :fundings
resources :facts
resources :bips
end
... and the same for organisations.
But, I am getting errors. I think maybe the article has jumped forward a few steps. Are there conventions for using polymorphic paths?
I've now found this resource:
http://www.codequizzes.com/rails/learn-rails/polymorphism
The create action looks unusual to me. Can anyone explain the create action concept used in this approach?
NEXT ATTEMPT
new index action in the Package::Bips controller:
def index
if Package::Bip.ipable_type: 'Proposal'
@bips = Package::Bip.where(ipable_type: 'Proposal')
elsif Package::Bip.ipable_type: 'Organisation'
@bips = Package::Bip.where(ipable_type: 'Organisation')
else
@bips = Package::Bip.all
end
But now, that still isn't letting me try to see if it works.
When I try to render a show view for a proposal, I get an error that says:
undefined method `package_bips_path' for #<#<Class:0x007fa72ea9b730>:0x007fa72ea93ee0>
This is the link that the show page contains:
<% if @proposal.bips.present? %>
<%= link_to package_bips_path(@proposal) do %>
<h6 style="margin:2%; "> Intellectual Property </h6>
<% end %>
ANOTHER GO AT IT
bips controller, index action:
if params[:ipable_type] == 'Proposal'
@bips = Package::Bip.where(ipable_type: 'Proposal')
else params[:ipable_type] == 'Organisation'
@bips = Package::Bip.where(ipable_type: 'Organisation')
# else
# @bips = Package::Bip.all
end
proposals show:
<% if @proposal.bips.present? %>
<%= link_to proposal_package_bips_path(@proposal) do %>
<% end %>
<% end %>
views/bips/index
<div class="container-fluid" style="margin-bottom: 50px">
<h2 style="text-align: center">Intellectual Property Assets</h2>
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<div class="row" style="margin-top:50px; margin-bottom:50px">
<div class="col-md-8 col-md-offset-2">
<span style="margin-right:50px; padding-left:50px">
<%= link_to "All", action: :index %>
</span>
<span style="color:grey">·</span>
<span style="margin-right:50px; padding-left:50px">
<%#= link_to "Intellectual Property we can offer", package_bips_path(:status => "Offered") %>
</span>
<span style="color:grey">·</span>
<span style="margin-right:50px; padding-left:50px">
<%#= link_to "Intellectual Property sought", package_bips_path(:status => "Sought") %>
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<div class="table-responsive" style="margin-left:30px; margin-top:15px">
<table class="table table-bordered">
<tr>
<td> <h5>Title</h5> </td>
<td> <h5>Asset</h5> </td>
<td> <h5>Added</h5> </td>
<%# if policy(@package_ips).update? %>
<td> <h5>Manage this asset</h5></td>
<%# end %>
</tr>
<% @bips.each do |ip| %>
<tr>
<td>
<%= ip.title.titleize %>
</td>
<td>
<%= ip.classification.humanize %>
</td>
<td>
<%= ip.created_at.try(:strftime, '%e %B %Y') %>
</td>
<%# if policy(@package_ips).update? %>
<td>
<%#= link_to 'More details', package_bip_path(ip) %> <span style="color:grey">·</span>
<%#= link_to "Edit", edit_package_ip_organistion_path(@organisation) %>
<!-- <span style="color:grey">·</span> -->
<%#= link_to 'Destroy', ip, method: :delete, data: { confirm: 'Are you sure?' } %>
</td>
<%# end %>
</tr>
<% end %>
</table>
</div>
</div>
</div>
</div>
This doesnt work (still renders wrong bips), BUT besides being incorrect, I can't have any links on the show page. If i want to link to :status params on the bips show view, it starts from a path that is not prefixed by proposal/organisation - so it doesnt work.
NEXT ATTEMPT
The bips controller index action now has:
if params[:ipable_type] == "Proposal"
@bips = Package::Bip.where(:ipable_type == 'Proposal')
else params[:ipable_type] == 'Organisation'
@bips = Package::Bip.where(:ipable_type == 'Organisation')
This now renders bips, but it renders proposal bips in the organisation index (as well as the proposal index).
I'm stuck!
In the console, I can do:
p = Proposal.last
Proposal Load (6.1ms) SELECT "proposals".* FROM "proposals" ORDER BY "proposals"."id" DESC LIMIT $1 [["LIMIT", 1]]
=> #<Proposal id: 15, user_id: 4, title: "testing filter", created_at: "2016-11-08 00:59:09", updated_at: "2016-11-08 00:59:09">
2.3.1p112 :123 > p.bips
Package::Bip Load (0.5ms) SELECT "package_bips".* FROM "package_bips" WHERE "package_bips"."ipable_id" = $1 AND "package_bips"."ipable_type" = $2 [["ipable_id", 15], ["ipable_type", "Proposal"]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Package::Bip id: 17, identifier: "testing filter", ipable_id: 15, ipable_type: "Proposal", created_at: "2016-11-08 00:59:09", updated_at: "2016-11-08 00:59:09"]>
This is correct. I can also do:
o = Organisation.first
Organisation Load (1.1ms) SELECT "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Organisation id: 1, title: "bhjhghdddd", created_at: "2016-10-21 05:20:39", updated_at: "2016-11-06 21:36:30">
2.3.1p112 :125 > o.bips
Package::Bip Load (0.4ms) SELECT "package_bips".* FROM "package_bips" WHERE "package_bips"."ipable_id" = $1 AND "package_bips"."ipable_type" = $2 [["ipable_id", 1], ["ipable_type", "Organisation"]]
=> #<ActiveRecord::Associations::CollectionProxy []>
This is also correct.
I just can't find a way to get the index populated with the right bips in the code.
ANOTHER ATTEMPT
I'm trying to use the advice in this tutorial: http://www.codequizzes.com/rails/learn-rails/polymorphism
It suggests:
# views/articles/show.html.erb
<%= render @article.comments %>
# views/comments/_comment.html.erb
<%= comment.body %><br />
The render @article.comments in the show page automatically knows to load a file called views/comments/_comment.html.erb. This is Rails magic, so just memorize this.
Taking that, I made a partial in my views/package/bips folder called _bips.html.erb.
In my proposals show view, I tried:
<%= render @proposal.bips %>
I get an error that says:
Missing partial package/bips/_bip with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :coffee, :jbuilder]}. Searched in:
* "/Users/d/cv/cflive/app/views"
That is the right location. I'm wondering if this problem might have something to do with the routes being nested. My routes file now has:
resources :proposals do
namespace :package do
resources :bips
end
Can anyone see why this approach isn't working?
来源:https://stackoverflow.com/questions/40455139/rails-polymorphic-association-rendering-associated-instances-only