:has_many, :through with ActiveResource models
Three models on a UserService backend Rails app:
class User < ActiveRecord::Base
has_many :services
has_many :members
has_many :groups, :through => :members
has_many :managed_groups, :class_name => "Group"
accepts_nested_attributes_for :managed_groups, :services, :groups
end
class Group < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
belongs_to :manager, :class_name => "User", :foreign_key => :user_id
accepts_nested_attributes_for :users
end
class Service < ActiveRecord::Base
belongs_to :user
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
Three ActiveResource models in a separate Rails front-end
class User < ActiveResource::Base
self.site = "http://localhost:8801"
end
class Group < ActiveResource::Base
self.site = "http://localhost:8801"
end
class Service < ActiveResource::Base
self.site = "http://localhost:8801"
end
From the Front-end, it works to do
Service.first.user_id = User.first
Because Service.user_id is a belongs_to… it's a single property.
However I can't do either
User.first.groups << Group.first
or
Group.first.users << User.first
Nothing happens because the ActiveResource is just spawning GET requests.
In the int开发者_如何学Cerest of completeness, doing things like the following (on the front-end) also fails.
u = User.first
g = Group.first
u.groups.push(g)
and so on. I was assuming that going through the users_controller::update method would work, but now I question whether this is possible at all.
Anyone done a :has_many, :through with two ActiveResource models? Is there any way to affect the collection of an AR model? Do I need to manipulate the join table manually? (Thereby creating a members_controller?)
This might be something for more than StackOverflow- more for the core team.
It seems that either ActiveResource is being stupid about how it talks to a RESTful client, or ActiveRecord is being stupid about how it listens to one (Or, the alternative, and much more likely option, these two things just fundamentally can't talk to one another.)
To wit: Given User, Service, and Group above, my front-end, upon placing a PUT request on the back-end server at /users/1.xml the back-end logs the following parameters (some values removed for conciseness):
{"user"=>
{"created_at"=>2011-06-23 02:58:15 UTC,
"id"=>1,
"name"=>"John Metta",
"updated_at"=>2011-06-23 02:58:15 UTC,
"managed_groups"=>[],
"services"=>[
{"created_at"=>2011-06-23 02:58:15 UTC,
"id"=>1,
"updated_at"=>2011-06-23 02:58:15 UTC,
"user_id"=>1}
],
"groups"=>[
{"created_at"=>2011-06-23 02:56:37 UTC,
"id"=>1,
"updated_at"=>2011-06-23 02:56:37 UTC,
"user_id"=>nil,
"users"=>[
{"created_at"=>2011-06-23 03:36:28 UTC,
"id"=>6,
"name"=>"Jefferey",
"updated_at"=>2011-06-23 03:36:28 UTC},
{"created_at"=>2011-06-23 02:59:36 UTC,
"id"=>2,
"name"=>"George",
"updated_at"=>2011-06-23 03:05:13 UTC}
]
},
{"created_at"=>2011-06-23 02:56:37 UTC,
"id"=>1,
"name"=>"Site Admin",
"updated_at"=>2011-06-23 02:56:37 UTC,
"user_id"=>nil,
"users"=>[
{"created_at"=>2011-06-23 03:36:28 UTC,
"id"=>6,
"name"=>"Jefferey",
"updated_at"=>2011-06-23 03:36:28 UTC},
{"created_at"=>2011-06-23 02:59:36 UTC,
"id"=>2,
"name"=>"George",
"updated_at"=>2011-06-23 03:05:13 UTC},
{"created_at"=>2011-06-23 02:58:15 UTC,
"id"=>1,
"name"=>"John Metta",
"updated_at"=>2011-06-23 02:58:15 UTC}
]
}
]
},
"id"=>"1"}
So, I can see a few issues with ActiveResource (or, more likely, how I am trying to use ActiveResource), but the main is this:
Since ActiveResource seems to know nothing about associations, it just bundles everything up in a dumb fashion. In otherwords, ActiveResource dumbly says "Duh, you have a list of groups, so I'll call it a 'groups'" instead of knowing that, in order for this to actually work, it should be saying "Hrm, you have a list of groups, and Group is another model, I'll call it groups_attributes so that it can be rebuilt into a list of groups on the other end.
Now, before I get flamed, I understand that 'groups_attributes' is an ActiveRecord vocabulary word, not a general REST word, so if ActiveResource started using it, it would break, say, my other back-end which runs on Scala.
However, given that Rails should be able to work with Rails, and given that ActiveResource has to be dumb about it, then it seems that ActiveRecord should be able to figure out that "This incoming RESTful model has objects contained that map to known models that are referenced, let's try to create those models and make it work.
Workable Solution:
I haven't thought about this enough to feel comfortable assuming that ActiveRecord should always automatically try to map an incoming parameter set's included MODEL-named parameters to a MODEL. That will require some more testing and thought. However, my solution has been to catch that incoming parameter set in the users_controller with something like the following:
…
begin
groups = []
params[:user][:groups].each do |g|
groups << Group.find(g[:id])
end
params[:user][:groups] = groups
rescue
# Do something not incredibly stupid here
end
…
Which seems to grab things relatively cleanly. My fear is that this seems pretty bulky for Rails-- which usually has much more elegant solutions to things-- and that I'm missing something basic.
(In the interest of completion, I realize that there are other problems with that parameter string, such as: Since I bundle the group with the user, and the user with the group, I'm passing a ton of data around necessarily (like, the entire group, and all it's users) when I pass the user around. This needs some sort of proxy- like a list of Group IDs or something. More likely, my ActiveResource models should have methods wrapping those objects. I'm considering those problem outside the scope of this issue.)
精彩评论