Reputation: 763
class Board < ActiveRecord::Base
has_many :applications, dependent: :destroy
end
class Application < ActiveRecord::Base
belongs_to :board
validates_presence_of :board
has_many :interactions, dependent: :destroy
end
class Interaction < ActiveRecord::Base
belongs_to :application
validates_presence_of :application
end
Given the above ActiveRecords, in the show method of boards_controller
, I can call @boards.applications
, and even though I don't explicitly call application.interactions
I still have access to the interactions in the view. However, for this particular view, I only need one interaction, which is gathered through some logic having to do with nil checks and sorting.
I would rather do this logic in the controller and only pass that one interaction in instead of all the extras for every application, but currently it's passing all of them and I can't explicitly add an application.current_interaction
in the controller because it's an unknown attribute.
How can I set one interaction for each application, and what is the proper way to do it in Ruby on Rails?
Here's what I ended up doing:
The application
model should look like this:
class Application < ActiveRecord::Base
belongs_to :board
validates_presence_of :board
has_many :interactions, dependent: :destroy
def current_interaction
#logic here
return interaction
end
end
Then it can be called in the view with <%= application.current_interaction %>
, there shouldn't have to be any changes to the controller at all.
Upvotes: 0
Views: 145
Reputation: 1057
I believe lazy-loading is the default behavior when retrieving the interactions
from an application
, so they shouldn't get fetched from the db until you call application.interactions
. But, you say that you don't explicitly call application.interactions
, so how are you seeing that they're accessible?
You can wrap that "nil checks and sorting" logic in a current_interaction
method on Application
, and the interactions
shouldn't be loaded unless you explicitly call them.
Upvotes: 1
Reputation: 23317
If the model Board has a relationship to applications, then anything with a Board object in @board
can call @board.applications
to get all the applications associated with that board. There is no way to prevent this.
But just because it can call that to get all applications doesn't mean it has to. Don't look at @board.applications unless you want all the applications for that board.
If instead:
However, for this particular view, I only need one interaction, which is gathered through some logic having to do with nil checks and sorting.
I would rather do this logic in the controller and only pass that one interaction in instead of all the extras for every application,
No problem! Just do that. You just won't pass it to the view as an attribute of the @board
, it'll be it's own thing.
# in controller
@current_interaction = find_current_interaction_for(@board)
# Or, instead, use template-parameters instead of using ivars
render "some_template", :locals => {:board => @board, :current_interaction => find_current_interaction_for_board(@board)}
You can't remove "access to the interactions in the view" -- so long as the relationship is defined in the model, they are "accessible". But if no code accesses @board.interactions
, they won't neccesarily be actually loaded, until accessed.
But if all the interactions aren't what you want in this view but only a certain one, and you want to calculate that certain one in the controller -- just calculate it in the controller and pass it in as a separate param. You were getting stuck thinking the view needed to the get the current_interaction from the Board model somehow, but it doesn't. If it makes sense to calculate it in the controller, then the controller has to pass it to the view separately. @board.applications will always be all the applications for the board, if you don't want all the applications don't look at that property, put the thing you want somewhere else.
Upvotes: 0