Несколько вложенных маршрутов к одному и тому же контроллеру. Не удалось найти XYZ без идентификатора

Я реорганизую свое приложение, чтобы везде использовать глубоко вложенные ресурсы 1 уровня, это API только для JSON. Вот урезанная версия моего 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

И мой урезанный 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.:

Что работает:

  • GET /experiments/4/measurements.json работает нормально

Что не работает: (все остальное ;))

  • GET /measurements/2/experiment.json

Сообщение об ошибке:

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

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

Это должно быть легко исправить. Более важным является:

2.:

GET "/measurements/2/tag_node"

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

Как я могу заставить рельсы называть его tag_node_id вместо measurement_id?

Решение:

После долгой беседы с 'dmoss18' стало ясно, что нет смысла ставить tag_nodes и anchor_nodes дочерними элементами physical_nodes, так как они существуют только в таблице измерений.

Итак, теперь мой routes.rb выглядит так:

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

Я также удалил все эти only childs, потому что база данных была разработана не так.


person Benjamin M    schedule 01.03.2013    source источник
comment
# 2: Ваш родительский ресурс: измерения. Rails автоматически предполагает, что параметр id называется [parent]_id или, в данном случае, Measurement_id. Это также, я считаю, почему № 1 терпит неудачу. Действие вашего контроллера (ExperiementsController#show), вероятно, ищет params[:id], когда маршрутизатор передает params[:measurement_id]. Вы можете прочитать больше на guides.rubyonrails.org/routing.html#nested-resources   -  person dmoss18    schedule 01.03.2013
comment
Проблема №1 немного другая. Если я вызову GET /measurements/2/experiment, а затем вызову параметр id (вместо measurement_id) при переходе к ExperiementsController#show, я получу эксперимент с id=2. Но я, конечно, хочу: дайте мне эксперимент, в котором Measurement_id=2. Похоже, мне нужно построить: if(params[:measurement_id]) then @experiement = Experiment.where("measurement_id = ?", params[:measurement_id]). Или есть более простое решение?   -  person Benjamin M    schedule 01.03.2013
comment
правильный. Предполагается, что вы не используете Experiment_id для поиска экспериментов, а используете Measurement_id. я опубликую ответ   -  person dmoss18    schedule 01.03.2013
comment
Ой, извини. Ты прав. Мне пришлось бы провести JOIN измерения, чтобы найти правильный эксперимент, потому что Experiment_id хранится в таблице измерений. Это немного усложняет задачу, но все же может быть решено с помощью блока if. Но я ищу более простое/элегантное/СУХОЕ решение, если это возможно;)   -  person Benjamin M    schedule 01.03.2013


Ответы (1)


1: Ваше действие ExperimentsController#show, скорее всего, ищет params[:id], чтобы найти эксперимент, когда рельсы проходят в идентификаторе измерения. Вам нужно будет сделать что-то вроде следующего:

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

Однако это не сработает, так как в вашей экспериментальной таблице нет столбца Measurement_id. Я бы не предлагал вкладывать эксперименты в качестве источника измерений. Это не то, как устроена ваша база данных. Если вы все еще хотите вложить его, вот что вам нужно сделать:

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

Ваши «has_many» и «belongs_to» не нуждаются в атрибуте «foreign_key». Rails позаботится об этом сам.

2: Поскольку вашим родительским ресурсом этого маршрута является Measurement, рельсы предполагают, что параметр id называется :measurement_id. Обновите свои ассоциации следующим образом:

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

Я бы не стал ничего вкладывать в ресурс измерений, так как это дочерний ресурс, а не родительский ресурс. Поскольку ваш /measurements/1/[anythingelse] является маршрутом measurements, rails предполагает, что идентификатор называется :measurement_id. Вы вкладываете вещи в ресурс/объект измерения с идентификатором 1. Другими словами, вы говорите, что измерение x ИМЕЕТ tag_nodes и HAS anchor_nodes, что на самом деле не так.

Если вы все еще хотите, вы можете создать отдельные действия в своем контроллере для каждого ресурса, например:

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

Создайте действие show_tag и show_anchor в контроллере Physical_nodes. Затем эти действия будут искать params[:measurement_id)

person dmoss18    schedule 01.03.2013
comment
Вот пример проекта с использованием вложенных ресурсов. Обратите внимание, что вложенный контроллер ресурсов (комментариев) предполагает, что параметр всегда будет называться :article_id, поскольку комментарий является ресурсом статьи. Этот подход предполагает, что пользователь никогда не попадет в маршрут комментариев напрямую (например, /comments/1), и в этом случае параметр будет просто :id github.com/railscasts/139-nested-resources/blob/master/blog/app/ - person dmoss18; 02.03.2013
comment
#1 можно было бы сделать проще, я думаю: @e = Measurement.where(params[:measurement_id]).experiment. Это должно работать, так как оно связано с помощью отношений модели, верно? - person Benjamin M; 02.03.2013
comment
Да, я просто хотел сделать подход очень ясным. Вы правы, хотя - person dmoss18; 02.03.2013
comment
Проблема с #2 заключается в следующем: Measurement имеет два разных подключения к PhysicalNode, одно через tag_node_id и одно через anchor_node_id. Мне нужен способ различить их... - person Benjamin M; 02.03.2013
comment
Ваши принадлежности_to и has_many нуждаются в обновлении. принадлежит_к :tag_node, :class_name =› 'PhysicalNode', :foreign_key =› :tag_node_id принадлежит_к :anchor_node, :class_name =› 'PhysicalNode', :foreign_key =› :anchor_node_id, и измените has_many :measurements на :tag_node и :anchor_node. Я бы не стал вкладывать tag_node и anchor_node в ваш ресурс измерений, так как они не являются дочерними ресурсами измерений. Это родительские ресурсы. - person dmoss18; 02.03.2013
comment
Я постараюсь и скоро отчитаюсь :) ... кстати: я ненавижу этот олдскульный => - person Benjamin M; 02.03.2013
comment
Я сделал это, но rake routes выглядит все так же, и вызов http://127.0.0.1:3000/measurements/2/tag_node все еще говорит мне, что он получил Parameters: {"measurement_id"=>"2"} :( ... и GET "/physical_nodes/2/tag_nodes.json" все еще передает Parameters: {"physical_node_id"=>"2"} - person Benjamin M; 02.03.2013
comment
Посмотрите здесь (robots.thoughtbot.com/post/159809070/), это похоже на решение, но установка key: :tag_node_id просто приводит к: Parameters: {"key"=>:tag_node_id, "physical_node_id"=>"2"} - person Benjamin M; 02.03.2013
comment
давайте продолжим это обсуждение в чате - person dmoss18; 02.03.2013
comment
Дерьмо. Я наткнулся на еще один маленький вопрос. И я не знаю, как с вами связаться. Итак, вот оно: Как мы уже говорили, /tag_nodes/2/measurements имеет смысл. А как же /tag_nodes? Это выполнит что-то вроде Measurement.select("DISTINCT tag_node_id").tag_nodes. Или это опять против структуры базы данных? - person Benjamin M; 02.03.2013