Подтвердить что ты не робот

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?

Спасибо!

4b9b3361

Ответ 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])