DEV Community

El Marshall (she/they)
El Marshall (she/they)

Posted on

Ruby on Rails .create Rollback Mystery

I interrupt your regularly scheduled Binary Tree blogs to bring you a frustration I ran into, and then the fix.

In my pet project, The World Builder's Tome, which started as my final project at Flatiron, I have quite a few interconnected models in my Ruby backend. There are worlds and stories and characters and notes and more, and each of these models "has many" images. The way I have handled this is to have only one Images model, with foreign keys for all these different categories.

Initially, the image.rb file in models looked like this:

class Image < ApplicationRecord
    belongs_to :world_note
    belongs_to :story_note
    belongs_to :world
    belongs_to :story
    belongs_to :character
    belongs_to :location 
end

In the midst of solving some minor bug (I honestly can't remember what it was now), I added a reference to the foreign keys, which is more syntactically correct. This looked like this:

class Image < ApplicationRecord
    belongs_to :world_note, :foreign_key => "world_note_id"
    belongs_to :story_note, :foreign_key => "story_note_id"
    belongs_to :world, :foreign_key => "world_id"
    belongs_to :story, :foreign_key => "story_id"
    belongs_to :character, :foreign_key => "character_id"
    belongs_to :location, :foreign_key => "location_id"

end

Little did I know, however, that this would cause me another issue somewhere else.

When I next came back to the program, I discovered that I could no longer successfully create images. Narrowing things down, I confirmed it wasn't an issue with Heroku, my hosting site, since it also wasn't working on my localhost. I even tried directly running Image.create() in the Rails Console, which confirmed that it wasn't an issue with a fetch somewhere that was creating the problem.

Looking at the output in Rails, I saw that the .create would begin, and then immediately rollback with no explanation. Here's that code, for those curious:

2.6.1 :003 > Image.create(url: "thisisaurl")
   (0.4ms)  BEGIN
   (0.4ms)  ROLLBACK
 => #<Image id: nil, url: "thisisaurl", world_id: nil, story_id: nil, 
character_id: nil, location_id: nil, story_note_id: nil, created_at: nil, 
updated_at: nil, world_note_id: nil>

Frustratingly, this proved to be a difficult problem to google. I couldn't quite get the wording right to get at the issue I was having. Eventually, however, I managed to find this question another programmer had.

They seemed to be having just the issue I was! Huzzah! Here's a key line from the winning response: "Rails, in 5.0, requires validation on belongs_to. There are two ways to fix it, adding optional: true or required: false."

There's a spot for each of these different connections on each image instance, but any given image will only have the one connection. Given that, the foreign-key for all other connections on that instance would be nil. Turns out, you need to specify whether or not those foreign-keys are required, or Rails (5.0) will default them to required and reject any new object instance that doesn't have them!

I gave that a try, altering my code to look like so:

class Image < ApplicationRecord
    belongs_to :world_note, :foreign_key => "world_note_id", optional: true
    belongs_to :story_note, :foreign_key => "story_note_id", optional: true 
    belongs_to :world, :foreign_key => "world_id", optional: true 
    belongs_to :story, :foreign_key => "story_id", optional: true 
    belongs_to :character, :foreign_key => "character_id", optional: true 
    belongs_to :location, :foreign_key => "location_id", optional: true 

end

Lo and behold, that solved it!! Who knew this was a thing? I sure didn't. Do now, though! Learn a little something new every day. (The commenter also recommended reading this blog post describing the change.

Hopefully this will help someone with my same issue discover the problem a little faster!

Top comments (3)

Collapse
 
racheladaw profile image
Rachel Williams

Thanks for posting El. I've definitely run into that issue with belongs_to before, but I don't usually use the foreign_key attribute. How come this is more syntactically correct?

On a side note, have you looked into using ActiveStorage for images? I'm starting to use it on my final project and its pretty nice. It allows you to easily upload images to cloud services to save space in your db.

Collapse
 
elmarshall profile image
El Marshall (she/they)

Ooh, we should talk some time about the images thing, I'd like to implement some proper image storage for one of my projects.

As for why it's syntactically correct... I'll be honest, it's just what I was told. Didn't question it much, maybe I should have, haha. My guess is that it's more intuitive and clearer. Walking into this code totally fresh, someone can now see, ok, these are foreign keys connecting this to other models. Without that marker, someone unfamiliar with the program would have to do a bit of searching to understand what was up.

Collapse
 
racheladaw profile image
Rachel Williams

Yeah definitely! I mostly followed this blog which is pretty straightforward. I also blogged about the process and how I implemented it in my app.

Ah okay that makes sense. I realize I would be confused too if I hadn't learned about ActiveRecord before reading the code. It definitely makes it more clear.