Home > Mobile >  RAILS - CHANGE FIELD OF ANOTHER TABLE - BOOLEAN
RAILS - CHANGE FIELD OF ANOTHER TABLE - BOOLEAN

Time:05-27

Good afternoon. I'm new to rails and I'm using google translate to post in English here, so sorry if it's not very readable.

My question is, I have a User table, and a Setting table.

They are related (but I don't know if the relationship is correct), they can even confirm me, and I would like to know if: when creating a user, I would like to automatically change the "email" and "push" fields of that user's settings table to true. Would it be possible via a method that in the user model called: "setting_default"?

  • User model.
class User < ApplicationRecord
   has_one :setting

   before_save :setting_default

   def setting_default
     self.setting.update(:email, 'true')
     self.setting.update(:push, 'true')
   end
  • Setting Model
class Setting < ApplicationRecord
   has_one :user
end

The Controller is normal, if you need it, I can put it in the post

My migration:

class CreateSettings < ActiveRecord::Migration[6.0]
  def change
    create_table :settings do |t|
      t.boolean :email, default: true
      t.boolean :push, default: true

      t.timestamps
    end
  end
end


class AddSettingsToUser < ActiveRecord::Migration[6.0]
  def change
    add_reference :users, :setting, null: true, foreign_key: true
  end
end

database-setting-user

CodePudding user response:

Google translate has worked well for you here.

First off you'll want to change your Setting model to belong to the User:

class Setting < ApplicationRecord
   belongs_to :user
end

Your settings DB table is missing a user_id field to tie the setting back to the user. I'm not used to the add_reference technique so I just do things myself in the migrations. This would work:

class CreateSettings < ActiveRecord::Migration[6.0]
  def change
    create_table :settings do |t|
      t.integer :user_id

      t.boolean :email, default: true
      t.boolean :push, default: true

      t.timestamps
    end
  end
end

(Make note that your users DB table has a field setting_id that it does not need. I don't think it should be there. I would remove it. Unless it's a Rails 6 thing I'm not used to.)

Next it would probably be better to assign the values if the save succeeds (and not if it fails) so you'll want an after_save instead. And I'm simplifying your value assignment just in case you're having an issue there:

class User < ApplicationRecord
   has_one :setting

   after_save :setting_default

   def setting_default
     setting.email = true
     setting.push = true
     setting.save
   end

And to answer what seems to be your question, yes, what you're trying to do should be easily possible. This is a very common thing to do. It should work.

CodePudding user response:

When you use one-to-one association you need to choose has_one in one and belongs_to in another model

Semantically user has one setting, but not setting has one user

So it's better to reverse them

To change your schema you need to write new migration

class ChangeOneToOneDirection < ActiveRecord::Migration[6.0]
  def up
    change_table :settings do |t|
      t.belongs_to :user, foreign_key: true, null: false
    end

    User.where.not(setting_id: nil).find_each |user|
      Setting.find(user.setting_id).update_columns(user_id: user.id)
    end

    change_table :users do |t|
      t.remove :setting_id
    end
  end

  def down
    add_reference :users, :setting, null: true, foreign_key: true

    Setting.find_each do |setting|
      User.find(setting.user_id).update_columns(setting_id: setting.id)
    end

    change_table :settings do |t|
      t.remove :user_id
    end
  end
end

After migration you can change User model

class User < ApplicationRecord
  has_one :setting

  after_commit :setting_default

  private

  def setting_default
    setting&.update(email: true, push: true)
  end
end

It's better to update associated model only if saves are in the database. And user can haven't setting. That's why after_commit and safe-navigator &

  • Related