Rails 4 [Best practices] Вложенные ресурсы и мелкие: true
Следующая публикация основана на Rails 4.
Я действительно ищу хорошие передовые практики в отношении нескольких вложенных ресурсов (более 1), а опция small: true.
Сначала в моих маршрутах было следующее:
resources :projects do
resources :collections
end
Связанные маршруты:
project_collections GET /projects/:project_id/collections(.:format) collections#index
POST /projects/:project_id/collections(.:format) collections#create
new_project_collection GET /projects/:project_id/collections/new(.:format) collections#new
edit_project_collection GET /projects/:project_id/collections/:id/edit(.:format) collections#edit
project_collection GET /projects/:project_id/collections/:id(.:format) collections#show
PATCH /projects/:project_id/collections/:id(.:format) collections#update
PUT /projects/:project_id/collections/:id(.:format) collections#update
DELETE /projects/:project_id/collections/:id(.:format) collections#destroy
projects GET /projects(.:format) projects#index
POST /projects(.:format) projects#create
new_project GET /projects/new(.:format) projects#new
edit_project GET /projects/:id/edit(.:format) projects#edit
project GET /projects/:id(.:format) projects#show
PATCH /projects/:id(.:format) projects#update
PUT /projects/:id(.:format) projects#update
DELETE /projects/:id(.:format) projects#destroy
Я прочитал в документации об ограничении вложенных ресурсов:
Ресурсы никогда не должны вставляться в глубину более 1 уровня.
Источник: http://guides.rubyonrails.org/routing.html#limits-to-nesting
ОК. Затем, как и в документации, я буду использовать "мелкие" на своих маршрутах.
shallow do
resources :projects do
resources :collections
end
end
Связанные маршруты:
project_collections GET /projects/:project_id/collections(.:format) collections#index
POST /projects/:project_id/collections(.:format) collections#create
new_project_collection GET /projects/:project_id/collections/new(.:format) collections#new
edit_collection GET /collections/:id/edit(.:format) collections#edit
collection GET /collections/:id(.:format) collections#show
PATCH /collections/:id(.:format) collections#update
PUT /collections/:id(.:format) collections#update
DELETE /collections/:id(.:format) collections#destroy
projects GET /projects(.:format) projects#index
POST /projects(.:format) projects#create
new_project GET /projects/new(.:format) projects#new
edit_project GET /projects/:id/edit(.:format) projects#edit
project GET /projects/:id(.:format) projects#show
PATCH /projects/:id(.:format) projects#update
PUT /projects/:id(.:format) projects#update
DELETE /projects/:id(.:format) projects#destroy
Основное отличие, которое я вижу, это "показ" коллекций, этот:
collection GET /collections/:id(.:format) collections#show
Итак, если я прав, ссылка для действия show для коллекции:
<%= link_to 'Show", collection_path(collection)%>
и должен возвращать что-то вроде этого: " http://example.com/collections/1"
НО! 2 вещи:
- Это не работает. Я получаю вместо этого http://example.com/projects/1". WTF?
- Даже если он работает, это действительно очень плохо, потому что я теряю базовый элемент REST, который говорит: "Коллекция - это проект проекта, тогда URL должен быть" localhost/project/1/collections/1 "
Я не понимаю, какой интерес представляет мелкий, если он потеряет большое преимущество действий Rest. Какой интерес? И в чём заключается интерес потерять действие "Шоу"? Я уже разместил это для SO, но единственный комментарий, который я получил, это "Это что-то нормальное". WTF? В чем это нормальное поведение, чтобы "удалить" действие из остального API?
Я воспроизвел проблему в нейтральном проекте, чтобы быть уверенным, что я не делаю что-то неправильно, и эта же проблема произошла. Итак, да, может быть удобно, чтобы хелперы использовали мелкие, но не ВСЕ, удобные для остальных, вы теряете всю заинтересованность "одна коллекция вложен в один проект, поэтому это отражается в URL".
Я не знаю, есть ли другой способ сделать это, правда, что неглубокие позволяют большую гибкость в отношении помощников, но это ложно, что он соответствует требованиям. Итак, есть ли шанс заставить "помощников" работать (довольно просто, чтобы иметь "nested3_path (collection)" вместо "nested1_nested2_nested3" ([nested1.nested2.nested3, nested1.nested2, nested1]) "и сохранение" url part "и сохранить" nested1/123/nested2/456/nested3/789?
Спасибо!
Ответы
Ответ 1
Я не считаю, что Rails предлагает какой-либо встроенный способ использования URL-адресов в полной иерархии (например, /projects/1/collections/2
), но также имеет помощники быстрого доступа (например, collection_path
вместо project_collection_path
).
Если вы действительно хотели это сделать, вы можете развернуть свой собственный вспомогательный помощник, как показано ниже:
def collection_path(collection)
# every collection record should have a reference to its parent project
project_collection_path(collection.project, collection)
end
Но это было бы довольно громоздким для ручного действия для каждого ресурса.
Я думаю, что идея использования маршрутов shallow
лучше всего подытожена документацией:
Один из способов избежать глубокого вложения (как рекомендовано выше) - генерировать действия коллекции, охваченные родителем, чтобы получить смысл иерархии, но не вставлять действия элемента. Другими словами, создавать маршруты с минимальным объемом информации для уникально идентифицировать ресурс
источник: http://guides.rubyonrails.org/routing.html#shallow-nesting
Таким образом, хотя это может быть не совместимо с REST (как вы говорите), вы не теряете какую-либо информацию, потому что каждый ресурс может быть идентифицирован однозначно, и вы можете вернуться к иерархии, предполагая, что ваши ассоциации настроены правильно.
Ответ 2
Так как там id
для a Collection
, он избыточен, чтобы вложить маршрут в проект, за исключением действий index
и create
.
Там есть правило о URL, где должен быть только один URL для GET (с 200) данного ресурса, если есть другой URL, вы должны перенаправить его. Таким образом, у вас может быть маршрут /projects/:id/collections/:collection_id
, который перенаправляется на /collections/:collection_id
.
В вашем случае коллекция привязана к проекту, но это не обязательно верно для всех отношений. Если у вас есть :collection_id
, вам не нужно ссылаться на контекст Project
для доступа к нему.
Ответ 3
Уровни
Понятие, которое вы должны использовать только 1 уровень во вложенных ресурсах, действительно применимо только к дизайну системы:
Соответствующим помощником маршрута будет publisher_magazine_photo_url, требуя указать объекты на всех трех уровнях. Действительно, это ситуация достаточно запутанна, что популярная статья Джамиса Бака предлагает правильное правило для хорошего дизайна Rails:
Я считаю, что Rails может обрабатывать несколько уровней, хотя это не рекомендуется с точки зрения удобства использования
Shallow
Несмотря на то, что раньше я видел мелкий, я никогда не использовал его сам
От взгляда на документация, кажется, что мелкая имеет довольно неясную цель (я действительно не знаю, почему она там). Проблема в том, что вы не публично передаете параметр post_id
вашему контроллеру, оставляя вас загружать collection
без важного параметра
Я бы предположил (и это всего лишь предположение), что цель состоит в том, чтобы передать необходимый вам параметр за кулисами, поэтому вы остаетесь с публичным "мелким" маршрутом:
#config/routes.rb
resources :projects do
resources :collections, shallow: true
end
Я бы предположил, что вы получите вспомогательный URL-адрес следующим образом:
collection_path(project.id, collection.id)
Это будет выглядеть как domain.com/collection/2
Ответ 4
Хотя это может усложнить ситуацию, если вам это нужно только для некоторых моделей, было бы неплохо проверить Inherited Resources (IR). Он поддерживает вложенность ресурсов, полиморфные принадлежит и может автоматически генерировать более короткие пути и методы помощника URL, которые вы ищете. Причина, по которой вы больше не слышите об IR, заключается в том, что ее оригинальный автор и некоторые другие разработчики несколько отказались от этого из-за осложнений, возникающих при попытке расширить ваши контроллеры. Тем не менее, у него все еще есть сообщество, и мы попытались расширить его немного больше и больше сосредоточиться на простоте расширений контроллера с помощью Irie.
"Лучшая практика" в Rails зависит от того, с кем вы разговариваете.
Rails традиционно был нацелен на базовые CRUD для (не-вложенных) ресурсов. Да, он позволяет извлекать и обновлять вложенные ресурсы, но предполагается, что это происходит не так часто.
Однако то, что появилось в сообществе Rails, это ActiveModel:: Сериализаторы/json-api. При этом обычно происходит не более одного уровня вложенности ресурсов, а вложенный ресурс - это либо список ссылок, либо небольшая версия дочерних ресурсов, которые затем можно запросить на этом ресурсе, чтобы получить больше данных. Это также было охвачено Ember/Ember Data.
Есть также roar и ряд других проектов, которые направлены на то, чтобы реализовать что-то ближе к их пониманию чего-то близкого к оригиналу Роя Филдинга видение REST.
Я думаю, это просто зависит от того, что ваш дизайн и что вам нужно. Если эффективность является целью, то дополнительное время, чтобы развиться, чтобы быть явным и гнездо больше, может окупиться. В настоящее время мы используем AngularJS и Irie. Но каждому свое.
Как и последнее примечание, обязательно избегайте n + 1 поиска с использованием includes(...)
(или подобных) в ваших запросах, иначе все это вложение может укусить вас в производительности.
Ответ 5
Из этого ответа кажется, что мелкие маршруты несколько противоречат конвенции Rails, IMO.
Я думаю, вам не нужен явный помощник пути для шоу-маршрута. Помощник link_to должен иметь возможность вывести его из метода object to_param.
#your helper becomes
link_to "show", collection
Если вы используете помощника, как у вас есть выше, вам, вероятно, также нужно передать вложенный идентификатор родительского ресурса помощнику.
link_to "show", collection_path([project, collection])