Question

I'm refactoring my application to use 1 level deep nested resources everywhere, it's a JSON-only API. Here's a trimmed version of my routes.rb:

resources :measurements, only: [:index, :show] do
  resource :tag_node, controller: :physical_nodes, only: [:show]
  resource :anchor_node, controller: :physical_nodes, only: [:show]
  resource :experiment, only: [:show]
end

resources :physical_nodes, only: [:index, :show] do
  resources :tag_nodes, controller: :measurements, only: [:index]
  resources :anchor_nodes, controller: :measurements, only: [:index]
end

resources :experiments, only: [:index, :show] do
  resources :measurements, only: [:index]
end

And my trimmed down models:

class Measurement < ActiveRecord::Base
  self.table_name = 'measurement'
  self.primary_key = 'id'

  belongs_to :physical_node, foreign_key: :tag_node_id
  belongs_to :physical_node, foreign_key: :anchor_node_id
  belongs_to :experiment, foreign_key: :experiment_id
end

class PhysicalNode < ActiveRecord::Base
  self.table_name = 'physical_node'
  self.primary_key = 'id'

  has_many :measurements, foreign_key: :tag_node_id
  has_many :measurements, foreign_key: :anchor_node_id
end

class Experiment < ActiveRecord::Base
  self.table_name = 'experiment'
  self.primary_key = 'id'

  has_many :measurements, foreign_key: :experiment_id
end

1.:

What works:

  • GET /experiments/4/measurements.json works fine

What doesn't work: (everything else ;) )

  • GET /measurements/2/experiment.json

Error message:

Processing by ExperimentsController#show as HTML
Parameters: {"measurement_id"=>"2"}

ActiveRecord::RecordNotFound (Couldn't find Experiment without an ID)

This should be easy to fix. More important is:

2.:

GET "/measurements/2/tag_node"

Processing by PhysicalNodesController#show as HTML
Parameters: {"measurement_id"=>"2"}

How can I get rails to call it tag_node_id instead of measurement_id?

Solution:

After a long chat with 'dmoss18', it became clear that it makes no sense to put the tag_nodes and anchor_nodes as child elements of physical_nodes, as they only exist in the measurements table.

So now my routes.rb looks like this:

resources :measurements, only: [:index, :show, :create]

resources :physical_nodes, only: [:index, :show]

resources :tag_nodes, only: [] do
  resources :measurements, only: [:index]
end

resources :anchor_nodes, only: [] do
  resources :measurements, only: [:index]
end

resources :experiments, only: [:index, :show] do
  resources :measurements, only: [:index]
end

I've also removed all those only childs, because this is not the way the database was designed.

Was it helpful?

Solution

1: Your ExperimentsController#show action is likely looking for params[:id] to find the experiment when rails is passing in the measurement id. You will need to do something like the following:

@experiment = Experiment.find(params[:measurement_id])

However, this will not work since your experiment table doesn't have a measurement_id column. I wouldn't suggest nesting experiments as a resource of measurements. That is not how your database is laid out. If you still want to nest it though, here is what you need to do:

@experiment = Measurement.find(measurement.experiment_id).experiment

Your "has_many" and "belongs_to" don't need the "foreign_key" attribute on them. Rails will take care of this itself.

2: Since your parent resource of this route is Measurement, rails will assume the id parameter is called :measurement_id. Update your associations like this:

class Measurement < ActiveRecord::Base
  self.table_name = 'measurement'
  self.primary_key = 'id'

  belongs_to :tag_node, :class_name => 'PhysicalNode', :foreign_key => :tag_node_id
  belongs_to :anchor_node, :class_name => 'PhysicalNode', :foreign_key => :anchor_node_id
  belongs_to :experiment
end

class PhysicalNode < ActiveRecord::Base
  self.table_name = 'physical_node'
  self.primary_key = 'id'

  has_many :tag_node, :class_name => 'Measurement', :foreign_key => :tag_node_id
  has_many :anchor_node, :class_name => 'Measurement', :foreign_key => :anchor_node_id
end

class Experiment < ActiveRecord::Base
  self.table_name = 'experiment'
  self.primary_key = 'id'

  has_many :measurements
end

I would not nest anything under the measurements resource since it is a child resource, and not a parent resource. Since your /measurements/1/[anythingelse] is a measurements route, rails assumes the id is called :measurement_id. You are nesting things under the measurement resource/object whose id is 1. In other words, you are saying that measurement x HAS tag_nodes and HAS anchor_nodes, which isn't really true.

If you still wanted to, you could create individual actions in your controller for each resource, like this:

resources :measurements, only: [:index, :show] do
  resource :tag_node, controller: :physical_nodes, only: [:show_tag]
  resource :anchor_node, controller: :physical_nodes, only: [:show_anchor]
  resource :experiment, only: [:show]
end

Create a show_tag and show_anchor action in your physical_nodes controller. These actions would then look for params[:measurement_id)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top