Using Delegate With has_many In Rails?

跟風遠走 提交于 2020-01-11 05:13:25

问题


We've got 2 models & a join model:

#app/models/message.rb
Class Message < ActiveRecord::Base
    has_many :image_messages
    has_many :images, through: :image_messages
end

#app/models/image.rb
Class Image < ActiveRecord::Base
    has_many :image_messages
    has_many :messages, through: :image_messages
end

#app/models/image_message.rb
Class ImageMessage < ActiveRecord::Base
    belongs_to :image
    belongs_to :message
end

Extra Attributes

We're looking to extract the extra attributes from the join model (ImageMessage) and have them accessible in the Message model:

@message.image_messages.first.caption # -> what happens now    
@message.images.first.caption #-> we want

We've already achieved this using the select method when declaring the association:

#app/models/message.rb
has_many :images, -> { select("#{Image.table_name}.*", "#{ImageMessage.table_name}.caption AS caption") }, class_name: 'Image', through: :image_messages, dependent: :destroy

Delegate

We've just found the delegate method, which does exactly what this needs. However, it only seems to work for has_one and belongs_to associations

We just got this working with a single association, but it seems it does not work for collections (just takes you to a public method)


Question

Do you know any way we could return the .caption attribute from the ImageMessage join model through the Image model?

We have this currently:

#app/models/image.rb
Class Message < ActiveRecord::Base
    has_many :image_messages
    has_many :messages, through: :image_messages

    delegate :caption, to: :image_messages, allow_nil: true
end

#app/models/image_message.rb
Class ImageMessage < ActiveRecord::Base
    belongs_to :image
    belongs_to :message

    def self.caption # -> only works with class method
        #what do we put here?
    end
end

Update

Thanks to Billy Chan (for the instance method idea), we have got it working very tentatively:

#app/models/image.rb
Class Image < ActiveRecord::Base
    #Caption
    def caption
        self.image_messages.to_a
    end
end

#app/views/messages/show.html.erb
<%= @message.images.each_with_index do |i, index| %>
    <%= i.caption[index][:caption] %> #-> works, but super sketchy
<% end %>

Any way to refactor, specifically to get it so that each time .caption is called, it returns the image_message.caption value for that particular record?


回答1:


delegate is just a shorthand as equivalent instance method. It's not a solution for all, and there are even some debate that it's not so explicit.

You can use an instance method when simple delegate can't fit.

def image_message_caption image_messages.class.caption end

I reviewed and found any association is unnecessary is this case. The ImageMessage's class method caption is more like a constant, you can refer it directly.

def image_message_caption
  ImageMessage.caption
end


来源:https://stackoverflow.com/questions/20816020/using-delegate-with-has-many-in-rails

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!