1

This has been asked on SO a lot before, but I can't find anything that quite applies. What I'm trying to do is render an edit form for SettingsController in the edit view of UsersController. I'm super new to RoR, so I'm not even sure what I'm doing wrong.

This questions seems closest, but when I initialize @setting = Setting.new in the Users controller, I get a Settings form without the defaults set for new users in the migration. But if I initialize @setting = Setting.edit or Setting.update, I get an undefined method or wrong number of arguments error.

When the Setting.new form is saved, it throws this error:

undefined method for find_by_id in the SettingsController: app/controllers/settings_controller.rb:43:in `correct_user'.

When I check the database, the settings records are being correctly created when a user is created, but the settings record is not updated when the form is saved.

setting.rb:

class Setting < ActiveRecord::Base
  belongs_to :user
  validates :user_id, presence: true
end

user.rb:

class User < ActiveRecord::Base
  has_many :posts, dependent: :destroy
  has_one  :setting, dependent: :destroy
  after_create :create_setting
end

UsersController:

 class UsersController < ApplicationController
 before_action :logged_in_user, only: [:edit, :update, :index, :destroy]
 before_action :correct_user, only: [:edit, :update]
 before_action :admin_user, only: :destroy

 def new
   @user = User.new
 end

 def edit
   @user = User.find(params[:id])
   @setting = Setting.update
 end

 def update
   @user = User.find(params[:id])
   @setting = Setting.update
   if @user.update_attributes(user_params)
     flash[:success] = "Profile updated!"
     redirect_to @user
   else
     render 'edit'
   end
 end

 def settings
   @title = "Settings"
   @setting = Setting.find_by_user_id(params[:user_id])
 end

private

 def user_params
     params.require(:user).permit(:name, :email, :password,
                               :password_confirmation)
 end

 # Confirms the correct user.
 def correct_user
   @user = User.find(params[:id])
   redirect_to(root_url) unless current_user?(@user)
 end
end

the SettingsController:

class SettingsController < ApplicationController
 before_action :logged_in_user, only: [:create, :edit, :update, :show, :index]
 before_action :correct_user, only: [:create, :edit, :update, :show, :index]

 def index
   @settings = Setting
 end

 def show
   @setting = User.find(params[:id]).setting
 end

 def new
   @setting = Setting.new
 end

 def edit
   @setting = Setting.find(params[:id])
 end

 def create
    @setting = current_user.settings.build(setting_params)
    @setting.save
 end

 def update
   @setting = Setting.find(params[:id])
   if @setting.update_attributes(post_params)
     flash[:success] = "Settings updated!"
     redirect_to request.referrer
   else
     render 'edit'
   end
 end

 private

 def setting_params
   params.require(:setting).permit(:reading_theme)
 end

 def correct_user
   @setting = current_user.setting.find_by_id(params[:id]) ##the line which throws an error when the form is saved
   redirect_to root_url if @setting.nil?
 end
end

The form partial:

 <%= form_for(@setting) do |f| %>
   <%= render 'shared/error_messages', object: f.object %>
     <div class="field">
       <%= radio_button_tag(:reading_theme, "flatly") %>
       <%= label_tag(:reading_theme_flatly, "Light (Default)") %>
     </div>
     <div class="field">
       <%= radio_button_tag(:reading_theme, "darkly") %>
       <%= label_tag(:reading_theme_darkly, "Dark") %>
     </div>
   <%= f.submit yield(:button_text), class: "btn btn-primary" %>
 <% end %>

routes.rb:

 resources :users do
   member do
     get :following, :followers
   end
 end
 resources :settings, only: [:new, :create, :edit, :update]
 ...

ETA: the settings migration:

class CreateSettings < ActiveRecord::Migration
  def change
    create_table :settings do |t|
      t.string :reading_theme, default: => "flatly"
      t.references :user, index: true, foreign_key: true

      t.timestamps null: false
    end
  end
end

How do I get the proper defaults so that my form can be saved correctly?

Community
  • 1
  • 1
Jim Hogan
  • 194
  • 2
  • 12

2 Answers2

1

Any defaults that you include for fields in the migration will be "unknown" to the model class (Setting) in Ruby. Ruby (or rather Rails ActiveRecord) does not read the default values from the table definition when creating a model object. This can lead to a dual personality problem like you're seeing here.

What you have to do is to add the relevant defaults into the Ruby code, where appropriate. For example, in your Settings controller, you can make these changes:

def new
  @setting = Setting.new
  # Set any defaults that will be visible to the user on the form
  @setting.reading_theme = "flatly"
  # The form will allow the user to choose their own values, based on the defaults
end

def create
  @setting = current_user.settings.build(setting_params)
  # Set any defaults that will NOT be visible to the user
  @setting.save
end

This gives you the ability to distinguish between default values that are visible to the user and defaults that are not.

Note that you also have the option of establishing defaults when you create the model object, but this may be more complicated in some situations, and seems to be far less common in practical use. There's an SO answer for that in How to initialize an ActiveRecord with values in Rails?, in case this better suits your needs.

Community
  • 1
  • 1
Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43
  • thanks! so, between this answer and @DennisCastro's, the form is rendering and is no longer throwing errors when it's saved, but it's also not saving to the database. I think I'm not understanding correctly where def new and def create need to be changed--I assumed in UsersController? Because if I changed them in SettingsController, I continued to get a nil argument error. Except that adding them to UsersController appears to create an empty form and doesn't save anything. I feel like I''m missing something simple. – Jim Hogan Jun 19 '16 at 18:50
  • @JimHogan I updated my answer to say that the changes should be made to the Settings controller. Be aware that the answer by DennisCastro should not be applied to either the `new` or `create` actions, as that's the approach used specifically for the `edit` or `show` actions. As evidenced by your "nil argument error", there may be additional issues with your code that will need to be solved to have a complete solution, and you'll have to update your question with the relevant error details (the entire action log, including the stack trace) to be able to figure those out. – Michael Gaskill Jun 20 '16 at 15:26
0

Not can use find_by_id in has_one relationship

@setting = current_user.setting.find_by_id(params[:id])

Just @setting = current_user.setting

DennisCastro
  • 161
  • 6