web-dev-qa-db-fra.com

Rails 4 [Meilleures pratiques] Ressources imbriquées et superficielles: vrai

Le message suivant est basé sur Rails 4.

Je recherche actuellement de bonnes pratiques sur les multiples ressources imbriquées (plus de 1) et l'option peu profonde: true.

D'abord dans mes itinéraires, il y avait ceci:

resources :projects do 
  resources :collections
end

Les itinéraires associés sont:

    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

J'ai lu dans la documentation sur la limitation des ressources imbriquées:

Les ressources ne doivent jamais être imbriquées à plus d'un niveau de profondeur.

Source: http://guides.rubyonrails.org/routing.html#limits-to-nesting Ok. Ensuite, comme le dit la documentation, je vais utiliser "peu profond" dans mes itinéraires.

shallow do
  resources :projects do 
    resources :collections
  end
end

Les itinéraires associés sont:

   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

La différence majeure que je vois est le "show" des collections, celui-ci:

collection GET    /collections/:id(.:format)                      collections#show

Donc si j'ai raison, le lien pour l'action show pour une collection est:

<%= link_to 'Show", collection_path(collection)%>

et devrait renvoyer quelque chose comme ceci: " http://example.com/collections/1 "

MAIS ! 2 choses:

  • Cela ne fonctionne pas. Je reçois à la place " http://example.com/projects/1 ". WTF?
  • Même si cela fonctionnait, c'est en fait assez mauvais car je perds le REST basique qui dit "La collection est enfant du projet, alors l'url devrait être" localhost/project/1/collections/1 "

Je ne comprends pas quel est l'intérêt du superficiel si c'est pour perdre le gros avantage des actions de repos. Quel intérêt? Et quel est l'intérêt de perdre l'action "Show"? J'ai déjà posté ceci sur SO, mais le seul commentaire que j'ai reçu est "C'est quelque chose de normal". WTF? Dans quel cas s'agit-il d'un comportement normal pour "supprimer" une action de l'API de repos?

J'ai reproduit le problème sur un projet neutre, pour être sûr que je ne faisais pas quelque chose de mal, et le même problème s'est produit. Donc, oui, il peut être pratique pour les assistants d'utiliser peu profond, mais ce n'est PAS AT TOUS pratique pour le reste, vous perdez tout l'intérêt de "une collection est imbriquée dans un projet, donc cela se reflète dans l'URL ".

Je ne sais pas s'il y a une autre façon de le faire, c'est vrai que peu profond permet plus de flexibilité sur les assistants, mais c'est faux qu'il soit conforme au repos. Donc, y a-t-il une chance de faire fonctionner les "helpers" (c'est assez génial d'avoir "nested3_path (collection)" au lieu de "nested1_nested2_nested3 ([nested1.nested2.nested3, nested1.nested2, nested1])", et de garder le " partie url "et continuer à avoir" nested1/123/nested2/456/nested3/789?

Merci !

27
Erowlin

Je ne pense pas que Rails offre un moyen intégré pour que les URL utilisent la hiérarchie complète (par exemple /projects/1/collections/2) mais également les raccourcis (par exemple collection_path au lieu de project_collection_path).

Si vous vouliez vraiment le faire, vous pouvez déployer votre propre assistant personnalisé comme suit:

def collection_path(collection)
  # every collection record should have a reference to its parent project
  project_collection_path(collection.project, collection)
end

Mais ce serait assez lourd à faire manuellement pour chaque ressource.


Je pense que l'idée derrière l'utilisation des routes shallow est mieux résumée par la documentation:

Une façon d'éviter l'imbrication profonde (comme recommandé ci-dessus) consiste à générer les actions de collecte définies sous le parent, afin d'avoir une idée de la hiérarchie, mais de ne pas imbriquer les actions membres. En d'autres termes, pour créer uniquement des itinéraires avec le minimum d'informations pour identifier de manière unique la ressource

source: http://guides.rubyonrails.org/routing.html#shallow-nesting

Ainsi, bien que cela ne soit pas conforme à REST (comme vous le dites), vous ne perdez aucune information car chaque ressource peut être identifiée de manière unique et vous pouvez remonter la hiérarchie en supposant que vos associations sont correctement configurées.

20
Carlos Ramirez III

Puisqu'il existe un id pour un Collection, il est redondant d'imbriquer la route sous le projet à l'exception des actions index et create.

Il y a une règle sur les URL où il n'y a qu'une seule URL pour GET (avec 200) une ressource donnée, s'il y a d'autres URL, vous devez les rediriger. Vous pourriez donc avoir un itinéraire /projects/:id/collections/:collection_id qui redirige vers /collections/:collection_id.

Dans votre cas, une collection est liée à un projet, mais ce n'est pas nécessairement vrai pour toutes les relations. Une fois que vous avez le :collection_id vous n'avez pas besoin de référencer le contexte de Project pour y accéder.

10
aceofspades

Niveaux

La notion selon laquelle vous ne devez utiliser qu'un seul niveau dans vos ressources imbriquées n'est vraiment applicable qu'à la conception du système:

L'assistant d'itinéraire correspondant serait publisher_magazine_photo_url, vous obligeant à spécifier des objets aux trois niveaux. En effet, cette situation est assez déroutante pour qu'un article populaire de Jamis Buck propose une règle d'or pour de bon Rails design:

Je crois que Rails peut toujours gérer plusieurs niveaux, bien que ce ne soit pas recommandé du point de vue de l'utilisabilité


Peu profond

Bien que j'aie déjà vu de l'eau peu profonde auparavant, je ne l'ai jamais utilisée moi-même

En regardant le documentation , il semble peu profond a un but plutôt obscur (je ne sais pas vraiment pourquoi il est là). Le problème est que vous ne passez pas publiquement le post_id paramètre à votre contrôleur, vous laissant charger le collection sans paramètre important

Je suppose (et ce ne sont que des spéculations) que l'objectif est de passer les paramètres dont vous avez besoin dans les coulisses, de sorte que vous vous retrouvez avec un itinéraire public "peu profond":

#config/routes.rb
resources :projects do 
   resources :collections, shallow: true
end

J'imagine que vous obtiendrez un assistant URL comme celui-ci:

collection_path(project.id, collection.id)

Cela sortirait comme domain.com/collection/2

2
Richard Peck

Bien que cela puisse compliquer les choses si vous n'en avez besoin que pour certains modèles, il peut être utile de vérifier Ressources héritées (IR). Il prend en charge l'imbrication des ressources, appartient à polymorphe et peut générer automatiquement les méthodes d'assistance de chemin d'accès et d'URL plus courtes que vous recherchez. La raison pour laquelle vous n'entendez plus beaucoup parler d'IR est que son auteur d'origine et certains autres développeurs l'ont quelque peu abandonné en raison des complications qui surviennent lorsque vous essayez d'étendre vos contrôleurs. Cependant, il a toujours une communauté, et nous avons essayé de l'étendre un peu plus et de nous concentrer davantage sur la facilité des extensions de contrôleur avec Irie .

La "meilleure pratique" dans Rails dépend de qui vous parlez.

Rails a traditionnellement visé principalement des CRUD de base pour les ressources (non imbriquées). Oui, il permet de récupérer et de mettre à jour les ressources imbriquées, mais on suppose que cela ne se produit pas aussi souvent.

Cependant, ce qui a émergé dans la communauté Rails est l’approche ActiveModel :: Serializers / json-api . Dans ce cas, généralement pas plusieurs niveaux d'imbrication des ressources se produisent, et la ressource imbriquée est soit une liste de liens, soit une petite version des ressources enfants, que vous pouvez ensuite interroger sur cette ressource pour obtenir plus de données. Cela a également été adopté par Ember / Ember Data .

Il y a aussi rugissement et un certain nombre d'autres projets qui visent à mettre en œuvre quelque chose de plus proche de leur compréhension de quelque chose de proche de la vision originale de Roy Fielding de REST.

Je pense que cela dépend simplement de votre conception et de vos besoins. Si l'efficacité est un objectif, alors le temps supplémentaire pour développer pour être explicite et imbriquer plus peut être payant. Nous utilisons actuellement AngularJS et Irie , par exemple. Mais a chacun le sien.

Comme dernière remarque, assurez-vous d'éviter les recherches n + 1 en utilisant includes(...) (ou similaire) dans vos requêtes, sinon tout l'imbrication pourrait vous nuire aux performances.

2
Gary S. Weaver

D'après cette réponse il semble que les routes peu profondes défient quelque peu la convention de Rails, OMI.

Je pense que vous n'auriez pas besoin de l'aide de chemin explicite pour un itinéraire de spectacle. L'assistant link_to doit pouvoir le déduire de la méthode to_param de l'objet.

#your helper becomes 
link_to "show", collection

Si vous utilisez l'aide comme vous le faites ci-dessus, vous devrez probablement également transmettre l'ID imbriqué de la ressource parent à l'aide.

link_to "show", collection_path([project, collection])
1
engineerDave