Reputation: 61
I am running into a problem when trying to handle a #create
request on a model with a has_many
association where one of the passed in IDs does not belong to an existing record.
Test request:
post authors_path, params: { book_ids: [-1] }
Controller method:
def create
@author= Author.create params
end
Model:
class Author
has_many :books
end
This results in an ActiveRecord::RecordNotFound
error being raised.
The problem is as follows:
I am already rescuing from an ActiveRecord::RecordNotFound
error and responding with 404 Record Not Found
in my ApplicationController
because such an error typically arises when a user is attempting to GET
, PATCH
, PUT
, or DELETE
a record that does not exist, e.g., get author_path(-1)
. I would prefer to avoid moving the rescue
clause onto the #show
, #create
, etc methods because I have a lot of controllers, resulting in a lot of duplicate code.
I want to keep my record and association creations atomic and this seems to be the best way to do it, but I also want to respond with a 400 Bad Request
when the situation described above occurs. What would be the best way to handle such a situation?
UPDATE
After some more research, I wrote a quick custom validation that validates a record exists for all passed in book_ids
class Author < ApplicationRecord
validate :books_exist
def books_exist
return if book_ids.blank?
Book.find book_ids
rescue ActiveRecord::RecordNotFound => e
errors.add e.message
end
end
This doesn't seem to work though as even instantiating a new Author
instance without saving it to the database throws an ActiveRecord::RecordNotFound
error:
> Author.new(association_ids: [-1])
Book Load (2.3ms) SELECT `books`.* FROM `books` WHERE `books`.`id` = -1
ActiveRecord::RecordNotFound: Couldn't find Book with 'id'=[-1]
from ...
The issue seems to be that ActiveRecord
attempts to find a record for the passed in book_id
before any validation occurs. Is there any way to rescue this error? It seems like there's not much of a workaround for this particular issue.
Upvotes: 0
Views: 1214
Reputation: 61
The two solutions that were suggest to me outside of StackOverflow are as follows:
Rescue the error in each controller action
class AuthorsController
def create
@author = Author.create(params)
render json: @author
rescue ActiveRecord::RecordNotFound => e
render json_error_message
end
end
Create a generic action in the ApplicationController
class ApplicationController
def create(model)
instance_variable_set("@#(model.class.name}", model.create(params)
rescue ActiveRecord::RecordNotFound => e
render json_error_message
end
end
class AuthorsController
def create
super(Author)
end
end
Upvotes: 1