문제

I'm using Rails 3.2.17 trying to access two virtual attributes (expiration_amount & expiration_format) in a model but nothing I've tried allows me to access them.

Everything else seems to update correctly, however in my logs the virtual attributes always come up blank.

EDIT I added both :expiration_amount :expiration_format to attr_accessible as suggested but it's still not working. They both come up blank. One thing I did notice was that the logger calls were happening after it hits the DB. Is before_update not the correct filter for this?

EDIT 2 Is it possible Devise is preventing the virtual attributes from being accessed?

EDIT 3 I just decided to kick it off from the controller (which is working), though I'm still curious as to why I couldn't access those virtual attributes in the model.

Working Controller action (Update method)

@user.set_expiration(params[:expiration_amount].to_i, params[:expiration_format])

Working Model method

def set_expiration(amount, format)
  if defined?(amount) && defined?(format)
    case format
    when "hours" 
      self.expiration = amount.hours.from_now
    when "days"
      self.expiration = amount.days.from_now
    when "weeks"
      self.expiration = amount.weeks.from_now
    else
      self.expiration = amount.hours.from_now
    end
  end
end

---ORIGINAL CODE---

User.rb

class User < ActiveRecord::Base
  attr_accessible               :email, 
                                :password, 
                                :password_confirmation, 
                                :remember_me,
                                :first_name, 
                                :last_name, 
                                :company, 
                                :phone, 
                                :expiration,
                                :expiration_amount, 
                                :expiration_format

  attr_accessor                 :expiration_amount, 
                                :expiration_format

  before_update                 :set_expiration

  def set_expiration
    logger.debug "DEBUG - expiration_amount: #{expiration_amount}"
    logger.debug "DEBUG - expiration_format: #{expiration_format}"

    if expiration_amount && expiration_format
      case expiration_format
      when "hours" 
        self.expiration = expiration_amount.hours.from_now
      when "days"
        self.expiration = expiration_amount.days.from_now
      when "weeks"
        self.expiration = expiration_amount.weeks.from_now
      else
        self.expiration = expiration_amount.hours.from_now
      end
    end
  end
end

form

= form_for(resource, :as => resource_name, :url => admins_user_update_path(resource), :html => { :method => :put }) do |f| 
  = devise_error_messages! 

  %fieldset{id: "edit-user-account"}
    %legend
      Set User Access

    .field.required
      %label
        "Allow access for "
      = select_tag "expiration_amount", options_for_select(expiration_amount, 3)
      = select_tag "expiration_format", options_for_select(expiration_format, "days")
      %span.instructions
        From Now

    .field
      = f.submit "Grant Access"

logs

  Parameters: {"utf8"=>"✓", "authenticity_token"=>"xxx=", "expiration_amount"=>"3", "expiration_format"=>"days", "commit"=>"Grant Access", "id"=>"5"}
User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", "5"]]
Admin Load (0.4ms)  SELECT "admins".* FROM "admins" WHERE "admins"."id" = 1 LIMIT 1
 (0.3ms)  BEGIN
DEBUG - expiration_amount: 
DEBUG - expiration_format: 
(0.3ms)  COMMIT
Redirected to http://localhost:8080/admins/users
도움이 되었습니까?

해결책 3

I finally figured out what the problem was! Because I was using select_tags with stings it wasn't naming them correctly, so:

= select_tag "expiration_amount", options_for_select(expiration_amount, 3)
= select_tag "expiration_format", options_for_select(expiration_format, "days")

was rendering as:

<select id="expiration_amount" name="expiration_amount">
<select id="expiration_format" name="expiration_format">

But what they needed to be for the model to pick them up is:

= f.select :expiration_amount, options_for_select(expiration_amount, 3)
= f.select :expiration_format, options_for_select(expiration_format, "days")

<select id="user_expiration_amount" name="user[expiration_amount]">
<select id="user_expiration_format" name="user[expiration_format]">

After that I was able to do everything in the model as opposed to the controller.

model method

before_update :set_expiration

def set_expiration
  logger.debug "DEBUG @ User - expiration_amount: #{expiration_amount}"
  logger.debug "DEBUG @ User - expiration_format: #{expiration_format}"

  if defined?(expiration_amount) && defined?(expiration_format)
    amount = expiration_amount.to_i

    case expiration_format
    when "hours" 
      self.expiration = amount.hours.from_now
    when "days"
      self.expiration = amount.days.from_now
    when "weeks"
      self.expiration = amount.weeks.from_now
    else
      self.expiration = 0.hours.from_now
    end
  end
end

다른 팁

Rails 3: Put them in attr_accessible as well.

Rails 4: Make sure you permit them.

I'd suggest creating your form using rails form helper:

.field.required
= form_for @user do |f|
  = f.label :expiration, "Allow access for "
  = f.select "expiration_amount", options_for_select(expiration_amount, 3)
  = f.select "expiration_format", options_for_select(expiration_format, "days")
  %span.instructions
   From Now

Then in your controller:

@user = User.find(params[:id])
@user.update_attributes(params[:user])
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top