Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Samsung Internet profile imageLola
Lola forSamsung Internet

Posted on

     

The Trouble with `has_one`

Recently I was working on my dissertation and came across an interesting problem with using the has_one association in Rails. But before we get into that, let’s take a step back to understand a little about how associations work in Rails.

A woman on big brother looking confused

Rails Associations

I’m not going to assume everyone reading this knows or understands in depth about databases and object relational mappers so I’ll try to keep this brief and simple. If you want a more in depth understanding, theRails docs are a good starting point to understanding this in that specific context.

Let’s say you want to build a content site where differentcreators can createposts, you’d have a relational database with two tablesCreator andPost that may look something like this:

A spreadsheet with two tables, Creator with the rows ID and Name and Post with the rows ID, Title, Type and Body

Your creator table has anID andName column for every item that gets created and your post table has anID,Title,Type andBody column for every item. In each column, the primary key will be theID column since that’s going to be the unique identifier for each record. Theprimary key is important when creating associations because it acts as theforeign key in the associated table.

Now we need to decide what kind of relationship the two tables will have with each other since we’d likely want to know which creators created which posts. There are a few different kinds of associations you can create but I’ll only touch on two.

has_many

A has_many association, says one item in a table can be associated with many items in another table. For example, if our system is a standard content creation system then acreator can have manyposts since onecreator can create manyposts in which case, the association in Rails would look like:

classCreator<ApplicationRecordhas_many:postsendclassPost<ApplicationRecordbelongs_to:creatorend
Enter fullscreen modeExit fullscreen mode

And our database would look like:
A spreadsheet with two tables, Creator with the rows ID and Name and Post with the rows ID, Title, Type, Body and Creator_ID

Thecreator_id would be pulled from theid column, which is the primary key in thecreator table and act as a foreign key in thepost table. So if you wanted to find all the posts created by Tiffany Pollard, you’d do a search on thepost table where thecreator_id matches Tiffany Pollard’sid in thecreator table.

has_one

Ahas_one relationship says one item in a table can only be associated with one item in another table, no more than one. We may want to create a system where acreator can only ever create onepost in which case, our Rails code would look like:

classCreator<ApplicationRecordhas_one:postendclassPost<ApplicationRecordbelongs_to:creatorend
Enter fullscreen modeExit fullscreen mode

Acreatorhas_one post and a postbelongs_to acreator. The database looks the same as before:
A spreadsheet with two tables, Creator with the rows ID and Name and Post with the rows ID, Title, Type, Body and Creator_ID

And just like withhas_many thecreator_id is the foreign key for the creator’sid in the post table.

Okay, so now onto the problem.

...

The Problem

In Rails when you create ahas_one association, understandably the assumption is that acreator can only ever have onepost. So what happens when acreator tries to create anotherpost? You’d hope that it’d check to see if a post exists and ask the creator if they want to replace the post or just replace the post with the assumption that that’s the intention of the creator. But that’s not what happens, instead, you get this error:

A rails error message that reads "Failed to remove the existing associated post. The record failed to save after its foreign key was set to nil.

This is a common error withhas_one and is summed up nicely in thisissue:

This is because prior to deleting, the foreign key of the target association is set to nil and a save operation is performed on the target.

It’s unclearwhy this happens, if you go through the issue you’ll notice it was created in 2014 and the discussion is still ongoing. It seems this started out as a bug but may have evolved to become the desired functionality.

The Fix

A small dog wearing glasses and a shirt using a laptop

There are a number of ways to fix this and it all depends on your system. Essentially, you want to make sure the old association is deleted before you try to create a new one. So, you can do this in your model:

classCreator<ApplicationRecordhas_one:post,dependent: :destroyendclassPost<ApplicationRecordbelongs_to:creatorend
Enter fullscreen modeExit fullscreen mode

Or in the method where you create your resource (either in your controller or specific lib file) you can run something like:

creator=Creator.find_by_id(id)creator.post&.delete
Enter fullscreen modeExit fullscreen mode

before you create any post. Andy Croll from Coverage Book recommendsrolling things up in a transaction and there are more suggestions in theGitHub issue too.

Further Reading

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

More fromSamsung Internet

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp