Reputation: 3272
I have two models that I want to manually associate or dissociate on demand.
class Car < ApplicationRecord
has_many :wheels, dependent: :nullify
end
class Wheel < ApplicationRecord
belongs_to :car
end
Say that my database is already full of cars
and wheels
. I don't know what is the proper way to associate a wheel
to a car
using JSON API calls or dissociate one in case a wheel
have already a car_id
filled.
Should I just create a nested route such as :
resources :cars do
resources :wheels, only: [:add_to_car, :remove_from_car]
end
But the add_to_car
and remove_from_car
methods doesn't show up in the routes list.
Ideally I would like to do something like /cars/:id/add_wheel/:wheel_id
(or something like that more RESTful friendly)
What is the Rails way to do this?
Upvotes: 0
Views: 34
Reputation: 154
Ir order to define this routes:
POST /cars/:id/add_wheel/:wheel_id for adding a wheel to a car.
DELETE /cars/:id/remove_wheel/:wheel_id for removing a wheel from a car.
You can do something like this
# config/routes.rb
resources :cars do
member do
post 'add_wheel/:wheel_id', to: 'cars#add_wheel'
delete 'remove_wheel/:wheel_id', to: 'cars#remove_wheel'
end
end
Then your controller should look like this:
class CarsController < ApplicationController
# POST /cars/:id/add_wheel/:wheel_id
def add_wheel
car = Car.find(params[:id])
wheel = Wheel.find(params[:wheel_id])
wheel.update(car: car)
if wheel.save
render json: wheel, status: :ok
else
render json: wheel.errors, status: :unprocessable_entity
end
end
# DELETE /cars/:id/remove_wheel/:wheel_id
def remove_wheel
car = Car.find(params[:id])
wheel = car.wheels.find(params[:wheel_id])
wheel.update(car: nil)
if wheel.save
render json: { message: "Wheel removed from car" }, status: :ok
else
render json: wheel.errors, status: :unprocessable_entity
end
end
end
Anyway, there is another approach even more RESTFUL, because if you see the previous answer, you can notice that there is a perform over the wheel, and that is not very RESTful. But if you have your model like:
# app/models/car.rb
class Car < ApplicationRecord
has_many :wheels, dependent: :nullify
# Adds a wheel to the car
def add_wheel(wheel_id)
wheel = Wheel.find_by(id: wheel_id)
wheels << wheel if wheel && wheel.car_id != self.id
end
# Removes a wheel from the car
def remove_wheel(wheel_id)
wheel = wheels.find_by(id: wheel_id)
wheel.update(car_id: nil) if wheel
end
end
Then in your controller you can write st like this:
def add_wheel
@car = Car.find(params[:id])
if @car.add_wheel(params[:wheel_id])
good
else
not good...
end
end
The other thing is you can consider is having routes like
# config/routes.rb
resources :wheels, only: [] do
member do
post 'add_to_car', to: 'wheels#add_to_car'
delete 'remove_from_car', to: 'wheels#remove_from_car'
end
end
And the controller:
class WheelsController < ApplicationController
# POST /wheels/:id/add_to_car
def add_to_car
wheel = Wheel.find(params[:id])
car = Car.find(params[:car_id]) # Assuming `car_id` is passed as part of the request parameters
wheel.car = car
if wheel.save
render json: wheel, status: :ok
else
render json: wheel.errors, status: :unprocessable_entity
end
end
# DELETE /wheels/:id/remove_from_car
def remove_from_car
wheel = Wheel.find(params[:id])
wheel.car = nil
if wheel.save
render json: { message: 'Car association removed successfully.' }, status: :ok
else
render json: wheel.errors, status: :unprocessable_entity
end
end
end
At the end, it depends on you and how you think your application domain!
Upvotes: -1