bluums
bluums

Reputation: 61

Rails - How to handle record create with has_many association error

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

Answers (1)

bluums
bluums

Reputation: 61

The two solutions that were suggest to me outside of StackOverflow are as follows:

  1. 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
    
  2. 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

Related Questions