Reputation: 2847
I have a bunch of character
models already in the database. Each character has_one :iconfolio
. I migrated the database to add icon_url
and post_url
attributes to the iconfolio
model, so I'm trying to update all iconfolio
models in the database using the rails console.
However, setting the correct attributes for each existing record is non-trivial, so I've also added a before_create
to iconfolio
which will correctly set the icon_url
and post_url
attributes for new characters
and iconfolios
. But how do I force this code to execute for characters
and iconfolios
already in the database?
In the console I've tried:
Character.all.find_each do |char|
char.create_iconfolio
end
but it doesn't update the database. It seems to delete the record:
(0.4ms) COMMIT
Iconfolio Load (0.2ms) SELECT "iconfolios".* FROM "iconfolios" WHERE "iconfolios"."character_id" = $1 LIMIT 1 [["character_id", 46]]
(0.1ms) BEGIN
SQL (0.2ms) DELETE FROM "iconfolios" WHERE "iconfolios"."id" = $1 [["id", 46]]
I also tried:
Character.all.find_each do |char|
Iconfolio.create(character_id: char.id)
end
This produced:
(0.2ms) BEGIN
Character Load (0.6ms) SELECT "characters".* FROM "characters" WHERE "characters"."id" = $1 LIMIT 1 [["id", 2]]
SQL (0.5ms) INSERT INTO "iconfolios" ("character_id", "created_at", "updated_at", "person_url", "followed_url", "icon_url", "post_url") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["character_id", 2], ["created_at", "2017-08-02 13:27:35.897842"], ["updated_at", "2017-08-02 13:27:35.897842"], ["person_url", "/assets/icon1.png"], ["followed_url", "/assets/icon2.png"], ["icon_url", "/assets/icon7.png"], ["post_url", "/assets/icon8.png"]]
(0.4ms) COMMIT
which looks promising, but the database still not updated. (I've tried restarting the server etc).
What am I doing wrong?
iconfolio.rb
belongs_to :character
validates :character_id, presence: true
before_create do
if self.character.type.is_a? User
self.icon_url = '/assets/icon5.png'
self.post_url = '/assets/icon6.png'
else
self.icon_url = '/assets/icon7.png'
self.post_url = '/assets/icon8.png'
end
end
Upvotes: 1
Views: 346
Reputation: 54882
As I commented on your question, you should not use Rails' console to update the existing Data to respect the new DB structure (I can provide examples why you should not do that if you need).
You should update this data in the very same migration that changes de DB structure:
# 1234_your_migration.rb
def up
# changing the DB structure
add_column :iconfolios, :icon_url, :string
add_column :iconfolios, :post_url, :string
# updating the current data
Iconfolio.includes(:character).find_each do |iconfolio|
if self.character.type.is_a? User
self.icon_url = '/assets/icon5.png'
self.post_url = '/assets/icon6.png'
else
self.icon_url = '/assets/icon7.png'
self.post_url = '/assets/icon8.png'
end
end
end
This might be a heavy migration to run, depending on the quantity of Iconfolio records you have in the DB. You can do as shown here OR define a method Iconfolio#update_icon_and_post_urls
but this method would be used only in the context of the Iconfolio creation OR in your migration, so that might be overkill, polluting your Iconfolio model in the long term and most importantly, can lead to problems when re-running migrations (see my comment on that answer).
Upvotes: 1
Reputation: 10111
What I like to do is move the before_create to a method
class Iconfolio
belongs_to :character
validates :character_id, presence: true
before_create :set_attributes
def set_attributes
if self.character.type.is_a? User
self.icon_url = '/assets/icon5.png'
self.post_url = '/assets/icon6.png'
else
self.icon_url = '/assets/icon7.png'
self.post_url = '/assets/icon8.png'
end
end
end
now you can always call it when you need to
Upvotes: 2