Rails RESTful delete in nested resources

2019-05-14 16:42发布

问题:

Okay, so here's an example scenario. There is a student resource resources :students, and students has and belongs to many collections: resources :clubs, resources :majors, etc.

So we can set up our routes easily enough...

resources :clubs do
  resources :students
end
resources :majors do
  resources :students
end
resources :students do
  resources :clubs
  resources :majors
end

which generates us a bunch of standard RESTful routes

  • /clubs
  • /clubs/:id
  • /clubs/:club_id/students
  • /clubs/:club_id/students/:id
  • /majors
  • /majors/:id
  • /majors/:major_id/students
  • /majors/:major_id/students/:id
  • /students
  • /students/:id
  • /students/:student_id/clubs
  • /students/:student_id/clubs/:id
  • /students/:student_id/majors
  • /students/:student_id/majors/:id

So here's my question. With REST semantics, how would one delete a major for a student? Browsing students under a major /majors/:major_id/students/:id would show that student in the specific major's 'collection'. But the route for deleting an :id, points to StudentsController#destroy, which would delete the student completely. Whoops! So maybe we go the other way, and perform DELETE on the resource at /students/:student_id/majors/:id and well now UnderwaterBasketweaving is now no longer offered at this school...Whoops!

Now granted, we could set the destroy method of ClubsController, MajorsController, or StudentsController to look for club_id, or major_id, or student_id, but lets say we also down the road want to add Fraternities and GraduatingClasses, etc. Each class would start to be comprised of huge switch conditionals looking to see what param was present... and then find the collection of either the top resource and remove the bottom resource, or vise versa. The Models themselves should decide whether they delete themselves if they have no more association records... 'Destroy' on that resource has become really a misnomer...

Is there an easier way to do this? Even popular restful rails plugins like make_resourceful or resource_controller would blow away UnderwaterBasketweaving when removing it from Joe's Majors or completely delete JohnDoe when removing him from the major UnderwaterBasketweaving. It would seem there's a potential for looking at the association to understand the desired effects of the semantics and what 'destroy' should do.

Then again, am I looking at this all wrong? Is it not UnderwaterBasketweaving -> Joe but UnderwaterBasketweaving+Joe as a single resource and what we're deleting is truly not Joe, nor UnderwaterBasketweaving, but the resource representing the combination? However, that's not easy when the controllers are Students and Majors, that in effect represent resources of the same name (MVC has really become RV...in the 'convention' approach and not developing Controllers that may have no bearing on a Model name, or the path to reach it) So you'd be deleting a Major or a Student; pick your poison...

How can I avoid managing conditionals across an infinite graph of associated resources where delete really isn't the intent when the delete is desired to be in context of a collection and not pertaining to its singularity...?

...major.student.delete... Is there a way for 'student' ActiveRecord object to know it was sent a 'delete' message in a method chain beginning with the 'major' AR object ?

回答1:

Well the standard RESTful aproach is to use has_many :through and generate a cotroller for the association resource. Naming association resources is always difficult, but I'll attempt for this example.

resources :majors do
  resources :studies
  resources :students
end
resources :students do
  resources :studies
  resources :majors
end

The models would be of course:

class Major < ActiverRecord::Base
  has_many :studies
  has_many :students, :through => :studies
end

etc. (comment if you want me to elaborate)

Then for a student you wouldn't DELETE it's associated @student.major but his @student.studies.where :major => @major.