Я создаю приложение Rails с двумя наборами вложенных моделей:
Цель -> Этап -> Шаблон
Контакты -> Трекер -> Электронная почта
На странице показа моей цели я могу перечислить все трекеры, связанные с этой целью, и отобразить информацию, такую как название, но я не могу понять, как связать с трекером.
Я пробовал много вариантов link_to, но ничего не работает, поэтому я был бы очень признателен за помощь. Спасибо!
Конфиг Routes.rb:
Rails.application.routes.draw do
get 'pages/inbox'
get 'pages/trackers'
devise_for :users
resources :contacts do
resources :trackers do
resources :emails
end
end
resources :goals do
resources :stages do
resources :templates
end
end
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
root "contacts#index"
end
Мой целевой контроллер:
def show
@trackers = Tracker.all
@tracker = Tracker.all
end
Моя целевая модель:
class Goal < ApplicationRecord
has_many :stages , dependent: :destroy
has_many :trackers
end
Моя модель трекера:
class Tracker < ApplicationRecord
belongs_to :contact
belongs_to :goal
belongs_to :stage
has_many :emails , dependent: :destroy
end
Вот страница Show.html.erb для цели с неправильным кодом для ссылки на трекер:
<% for tracker in @goal.trackers %>
<hr></hr>
<%= link_to goal_tracker_path(@goal, tracker) do %> #Doesn't work
<p><%= tracker.title %> | <%= tracker.contact.first_name %></p>
<% end %>
<% end %>





Ресурс tracker вложен не в goal, а в contacts. Путь goal_tracker_path (eg: /goals/1/trackers/1) не существует. Существует только contact_tracker_path. Вы можете увидеть это, если побежите rails routes.
Есть несколько способов сделать это.
/trackers/id. Ссылка на будет выглядеть примерно такlink_to 'Title', tracker
goal_tracker_path, и contact_tracker_path будут доступны, хотя оба будут использовать один и тот же TrackersController. Вы всегда можете написать другой контроллер и указать его в маршрутах, если это необходимо. Маршруты будут выглядеть примерно такresources :contacts do
resources :trackers do
resources :emails
end
end
resources :goals do
resources :trackers
resources :stages do
resources :templates
end
end
contact_tracker_path.Лучше всегда смотреть на rails routes, чтобы убедиться, что путь существует.
Спасибо @sajinmp - так что я попробую второй вариант, добавив ресурсы :templates в ресурсы :goals do. Каким будет правильный link_to в этом случае?
@CatMorley Было бы goal_tracker_path(goal, tracker). В качестве альтернативы [goal, tracker] должен помочь, если я не ошибаюсь. Вы можете запустить rails routes и убедиться, проверив список всех маршрутов.
О, хорошо, это работает, но выдает ошибку на странице трекера: ActiveRecord::RecordNotFound in TrackersController#show Не удалось найти контакт без идентификатора — есть предложения? Спасибо
Контакт в этом случае не передается. Кажется, вы уже настроили контроллер трекера в отношении контактов. Трекер можно получить с помощью Tracker.find(id), а цель и контакт, если необходимо, можно получить с помощью tracker.goal и tracker.contact. Это помогло бы избежать необходимости в двух контроллерах, но это зависит от предполагаемой функциональности. Другой вариант - иметь отдельный контроллер трекера. Макс объяснил это в своем ответе.
Большое спасибо @sajinmp - теперь мне удалось заставить это работать, и я намного лучше понимаю, как работает маршрутизация!
Извините, да, я принял это сейчас - я не знал об этом до сих пор! Еще раз спасибо!
Трекеры вложены ниже контрактов. Поэтому нужно передать контракт, к которому принадлежит трекер, тоже в метод link_to:
<% @goal.trackers.each do |tracker| %>
<hr />
<%= link_to [tracker.contact, tracker] do %>
<p><%= tracker.title %> | <%= tracker.contact.first_name %></p>
<% end %>
<% end %>
Или вы можете подумать о том, чтобы не использовать вложенность в своих маршрутах так часто. Официальные руководства Rails предлагают Shallow Nesting вместо вложенных маршрутов.
goal_tracker_path нет, и это вызывает выборку contact записей, которые, по-видимому, используются только для создания ссылки.
@sajinmp: я исправляю маршрутизацию, чтобы избежать неизвестной ошибки goal_tracker_path. Но если вы хотите избежать извлечения контакта, вам нужно изменить маршрутизацию и использовать неглубокую вложенность, как я описал в своем ответе.
Основная проблема в том, что вы на самом деле не вкладываете ресурс trackers в goals.
Для этого вам понадобится:
resources :contacts do
resources :trackers
# ...
end
resources :goals do
resources :trackers
# ...
end
Здесь и /contacts/1/trackers, и /goals/1/trackers перейдут к методу TrackersContoller#index. Один из распространенных способов справиться с этим - это то, что я называю "методом прослушивания параметров":
class TrackersContoller
# GET /contacts/1/trackers
# GET /goals/1/trackers
# GET /trackers
def index
if params[:contact_id].present?
@trackers = Contact.find(params[:contact_id]).trackers
elsif params[:goal_id].present?
@trackers = Goal.find(params[:contact_id]).trackers
else
@trackers = Tracker.all
end
end
# ...
end
Что чертовски вонюче с точки зрения принципа единой ответственности, поскольку контроллер теперь обрабатывает три разных варианта ресурса RESTful.
Обычно я предпочитаю направлять вложенные представления ресурса на их собственный контроллер:
resources :trackers # TrackerController
resources :contacts do
# routes to Contacts::TrackerContoller
resources :trackers, only: [:new, :create, :index],
module: :contacts
# ...
end
resources :goals do
# routes to Goals::TrackerContoller
resources :trackers, only: [:new, :create, :index],
module: :contacts
# ...
end
module Contacts
# Creates and lists trackers belonging to a Contact
class TrackersContoller < ApplicationController
# GET /contacts/1/trackers
def index
Contact.find(params[:contact_id]).trackers
end
end
end
module Goals
# Creates and lists trackers belonging to a Goal
class TrackersContoller < ApplicationController
# GET /goals/1/trackers
def index
Goal.find(params[:goal_id]).trackers
end
end
end
Кроме того, прежде чем вы сойдете с ума, создавая схему русской куклы, вам, вероятно, следует прочитать классический пост Джеймиса Бака о том, почему глубокая вложенность — это плохо:
Эмпирическое правило: ресурсы никогда не должны быть вложены более чем на 1 уровень. глубокий. Коллекция может нуждаться в том, чтобы ее область действия была определена ее родителем, но конкретная член всегда может быть доступен непосредственно по идентификатору и не должен нуждаться область видимости (если только идентификатор по какой-то причине не уникален).
Кодекс может быть сильно устаревшим, но основные принципы так же актуальны, как и 15 лет назад. Это также рекомендуется руководствами Rails.
Большое спасибо @max - я сожалею о том, что вложил еще один уровень, но мне удалось заставить это работать и понять, как маршрутизация работает намного лучше!
Не могли бы вы поделиться файлом маршрутов? Или запустите
rails routesи подтвердите правильность пути.