Preventing Record Duplication
Learning Goals
Avoid creating duplicate records in a database that is mapped to a Ruby program
Build a
#find_or_create_by
method
The Dreaded Duplication
What happens when two Ruby objects get created using the same attributes? If we are trying to persist representations of such objects to a database, would we end up with essentially identical rows in our table? That would make for a very confusing database and our program would quickly become useless as a way to store and manage information.
For example, lets say we have a Song
class that produces individual song objects, each of which has a name
and album
attribute.
Nothing stops us from creating two objects, each of which has the exact same name and album.
What happens when we save these objects to our database?
For this example, we'll assume our connection to the database is stored in DB[:conn]
.
We have two records that contain the same information! How can we avoid this? When we try to save a new Song
instance, we should first check to see whether the object we are trying to save already has an equivalent record in the database. If it does, we should simply update it. Otherwise, we can go ahead and save it.
Saving vs. Updating
Let's say we have a song, hello
:
Before we call #save
on our hello
object, we need to check and see if a record containing this name and album already exists in the database. The SQL statement to accomplish that would look something like this:
If this statement returns a record, we don't need to create a new record, only update the existing one. Otherwise, we need to insert a new record into our database table.
Let's build a method that will allow us to either find an existing record or create and save a new one.
The #find_or_create_by
Method
#find_or_create_by
MethodTake a look at our Song
class.
Let's build our #find_or_create_by
method:
Let's break this down:
First, we query the database: does a record exist that has this name and album?
If such a record exists, the song
variable will now point to an array that would look something like this:
song name and album name provided as a hypothetical example
If this is the case, then the statement: !song.empty?
will return true
. Therefore, we will use the returned values to make a new "Hello" object that Ruby can play around with, but we will not save it to the database. That re-instantiation of an existing Song object is accomplished with these lines:
We grab the song_data
from the song
array of arrays, setting song_data
equal to:
Then, we use this array to create a new Song
instance with the given id, name and album.
However, if no record exists that matches the name and album passed in as arguments, then !song.empty?
will return false
, and we will instead create and save a new Song
instance with the #create
method.
At the end of our #find_or_create_by
method, we will return the song object whose database entry we either found or created. This method assumes that there isn't already a song object matching these attributes, but that there may already exist a database entry with the same name and album. Therefore, it instantiates a new instance of the Song
class while preventing a duplicate database entry.
Our Code in Action
Now, we can use our Song
class without worrying about creating duplicate records:
Although we called #find_or_create_by
twice with the same data (gasp!), we only created one record with that data.
Bonus Section- Video Reviews
Last updated