Rails Polymorphic relationship and link_to
Asked Answered
V

4

5

Here's my Schema

class Menu < ActiveRecord::Base
  belongs_to :menuable, :polymorphic => true
end

class Page < ActiveRecord::Base
  has_one :menu, :as => :menuable
end

class Links < ActiveRecord::Base
  has_one :menu, :as => :menuable
end

I want to link to a polymorphic class in the Menu view using link_to, e.g.

<%= link_to menu.name, menu.menuable %>

This works, but this retrieves the menuable object from the database, when all I wanted is to generate a link. You can imagine if my menu is large, this will really bog down my application.

When I decared the menuable field as polymorphic, Rails created menuable_type and menuable_id. What can I use to generate a link to the polymorphic page, short of writing a helper function with a giant switch statement (e.g. if I have a large number of menuable 'subclasses'?)

Visually answered 16/8, 2009 at 9:15 Comment(1)
you could use polymorphic routes to generate the path api.rubyonrails.org/classes/ActionDispatch/Routing/…Minimum
L
7

It's been long since the question was asked but I had the same problem recently and the solution was to use polymorphic_url. You need to find the name of the route you need to create a link to, for example "fast_car_path" and make it out of your *_type and *_id from polymorphic table. For example, you have a list of comments and want to make the link to the cars that they belong to. So if *_type = FastCar we have

@comments.each do |comment|
  link_to polymorphic_url(comment.commentable_type.tableize.singularize, :id => comment.commentable_id) 

which will generate "fast_car_path" without downloading the cars from database.

I am a noob in rails and I dont know how good that advice is, but I hope it will be helpful for somebody.

Lothair answered 26/11, 2013 at 4:48 Comment(1)
you could just write it with =link_to polymorphic_url(comment) there is also polymorphic_path more about the method is here api.rubyonrails.org/classes/ActionDispatch/Routing/…Minimum
Q
1

You could do something like this:

def my_menu_url(menu)
  "/#{menu.menuable_type.tableize}/#{menu.menuable_id}"
end

if you use the rails convention for naming the controllers that correspondent to your models.

But don't do it. You work around the routing mechanism of rails and that's simply bad practice.

You should use the :include option in your finders to eager load your menuables:

Menu.all :include => :menuable

In the case this isn't enough you may use some sort of caching.

Quinquereme answered 16/8, 2009 at 10:4 Comment(3)
Does eager loading work for a polymorphic relationship? How does it know what table to join with, without first retrieving the menu table?Visually
I guess at most you can reduce the loading to the number of polymorphic types that exist. Unfortunately, a quick search shows that this is not the existing behavior in ActiveRecord. :( Maybe it's time to go back to single table inheritance.Visually
I think ActiveRecord first loads all the Menu records. In a second step it dos a query for every menuable_type in the result set of the first query. I think single table inheritance is no solution in this case, because the tow models differ to much.Quinquereme
A
0

Another approach could be to use url_for[menu.menuable, menu]. So, the link tag would look like so: <%= link_to menu.name, url_for([menu.menuable, menu]) %>.

Application answered 18/11, 2021 at 20:13 Comment(0)
M
0

you could use polymorphic routes for this

https://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html

<%= link_to menu.name, polymorphic_path(menu.menuable) %>

it will generate html like this

<a href="/menu.menuable_type/menu.menuable_id">menu.name</a>
Minimum answered 2/12, 2021 at 14:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.