2014-01-25 87 views
0

我一直在努力解決這個問題,並嘗試了很多選擇,觀看了http://railscasts.com/episodes/26-hackers-love-mass-assignment?view=asciicasthttp://railscasts.com/episodes/196-nested-model-form-part-1 Railscasts,並試用了Devise解決方案Github的頁面,但無法找到解決這個問題。使用Devise時不能批量分配關聯模型的受保護屬性

我正在使用Rails 3.2

我有一個用戶有很多地址。

用戶模型:

class User < ActiveRecord::Base 
    rolify 
    # Include default devise modules. Others available are: 
    # :token_authenticatable, :confirmable, 
    # :lockable, :timeoutable and :omniauthable 
    devise :database_authenticatable, :registerable, 
     :recoverable, :rememberable, :trackable, :validatable 

    # Setup accessible (or protected) attributes for your model 
    attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :stripe_token, :coupon, :addresses_attributes 
    attr_accessor :stripe_token, :coupon 
    before_save :update_stripe 
    before_destroy :cancel_subscription 

    has_many :addresses 
    accepts_nested_attributes_for :addresses, :reject_if => :all_blank, :allow_destroy => true 

    def update_plan(role) 
    self.role_ids = [] 
    self.add_role(role.name) 
    unless customer_id.nil? 
     customer = Stripe::Customer.retrieve(customer_id) 
     customer.update_subscription(:plan => role.name) 
    end 
    true 
    rescue Stripe::StripeError => e 
    logger.error "Stripe Error: " + e.message 
    errors.add :base, "Unable to update your subscription. #{e.message}." 
    false 
    end 

    def update_stripe 
    return if email.include?(ENV['ADMIN_EMAIL']) 
    return if email.include?('@example.com') and not Rails.env.production? 
    if customer_id.nil? 
     if !stripe_token.present? 
     raise "Stripe token not present. Can't create account." 
     end 
     if coupon.blank? 
     customer = Stripe::Customer.create(
      :email => email, 
      :description => name, 
      :card => stripe_token, 
      :plan => roles.first.name 
     ) 
     else 
     customer = Stripe::Customer.create(
      :email => email, 
      :description => name, 
      :card => stripe_token, 
      :plan => roles.first.name, 
      :coupon => coupon 
     ) 
     end 
    else 
     customer = Stripe::Customer.retrieve(customer_id) 
     if stripe_token.present? 
     customer.card = stripe_token 
     end 
     customer.email = email 
     customer.description = name 
     customer.save 
    end 
    self.last_4_digits = customer.cards.data.first["last4"] 
    self.customer_id = customer.id 
    self.stripe_token = nil 
    rescue Stripe::StripeError => e 
    logger.error "Stripe Error: " + e.message 
    errors.add :base, "#{e.message}." 
    self.stripe_token = nil 
    false 
    end 

    def cancel_subscription 
    unless customer_id.nil? 
     customer = Stripe::Customer.retrieve(customer_id) 
     unless customer.nil? or customer.respond_to?('deleted') 
     if customer.subscription.status == 'active' 
      customer.cancel_subscription 
     end 
     end 
    end 
    rescue Stripe::StripeError => e 
    logger.error "Stripe Error: " + e.message 
    errors.add :base, "Unable to cancel your subscription. #{e.message}." 
    false 
    end 

    def expire 
    UserMailer.expire_email(self).deliver 
    destroy 
    end 

end 

用戶控制器:

def new 

    @plan = params[:plan] 
    if @plan && ENV["ROLES"].include?(@plan) && @plan != "admin" 

     super 

    @user.addresses.build 

    else 
     redirect_to root_path, :notice => 'Please select a subscription plan below.' 
    end 
    end 

地址模型;

class Address < ActiveRecord::Base 
    attr_accessible :country, :county, :first_line, :postcode, :second_line, :town_city 
    #validates :country, :user_id, :county, :first_line, :postcode, :town_city, presence: true 
    belongs_to :user 
end 

地址控制器:

class AddressesController < ApplicationController 
def create 
end 

def update 
end 
end 

在我看來,我嵌套的地址模式,使我可以創建一個用戶,並在一個犯規一舉地址:

<div class="intro2"> 

    <% content_for :head do %> 
    <%= tag :meta, :name => "stripe-key", :content => STRIPE_PUBLIC_KEY %> 
<% end %> 

<div id="stripe_error" class="alert alert-error" style="display:none" > 
</div> 
<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {:class => 'card_form form-vertical form-signin' }) do |f| %> 
<%= link_to '<i class="icon-step-backward"></i>''back'.html_safe, root_path %> 

<h2>Sign up</h2> 
    <h3><%= params[:plan].titleize if params[:plan] %> Subscription Plan</h3> 
    <%= hidden_field_tag 'plan', params[:plan] %> 
    <%= f.error_notification %> 
    <%= display_base_errors resource %> 
    <%= f.input :name, :autofocus => true %> 
    <%= f.input :email, :required => true %> 
    <%= f.input :password, :required => true %> 
    <%= f.input :password_confirmation, :required => true %> 



<%= field_set_tag 'Customer Details' do %> 
      <%= f.fields_for :address do |builder| %> 
       <%= builder.label :first_line %> 
       <%= builder.text_field :first_line %> 
       <%= builder.label :second_line %> 
       <%= builder.text_field :second_line %> 
       <%= builder.label :town_city %> 
       <%= builder.text_field :town_city %> 
       <%= builder.label :county %> 
       <%= builder.text_field :county %> 
       <%= builder.label :postcode %> 
       <%= builder.text_field :postcode %> 
       <%= builder.label :country %> 
       <%= builder.text_field :country %> 
       <% end %> 
     <% end %> 

    <% if @user.stripe_token %> 
    <p>Credit card acceptance is pending.</p> 
    <% else %> 
    <div class="field"> 
     <%= label_tag :card_number, "Credit Card Number" %> 
     <%= text_field_tag :card_number, nil, name: nil %> 
    </div> 
    <div class="field"> 
     <%= label_tag :card_code, "Card Security Code (CVV)" %> 
     <%= text_field_tag :card_code, nil, name: nil %> 
    </div> 
    <div class="field"> 
     <%= label_tag :card_month, "Card Expiration" %> 
     <%= select_month nil, {add_month_numbers: true}, {name: nil, id: "card_month"}%> 
     <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+10}, {name: nil, id: "card_year"}%> 
    </div> 
    <div class="field"> 
     <%= f.input :coupon, :label => 'Promotional Coupon (if any)' %> 
    </div> 
    <% end %> 
    <%= f.hidden_field :stripe_token %> 
    <%= f.button :submit, 'Sign up', :class => 'btn-primary' %> 
<% end %> 
    </div> 

錯誤信息我在瀏覽器中得到的是:

ActiveModel :: MassAssignmentSecurity :: Error at/users 不能大規模指派保護的屬性:地址

請求參數爲:

{"utf8"=>"✓", "authenticity_token"=>"+69wR2xYkBARu+JlsnizyF+0hrRUXo6Z+XGAipw98NY=", "plan"=>"silver", "user"=>{"name"=>"Bob", "email"=>"[email protected]", "password"=>"12345678", "password_confirmation"=>"12345678", "address"=>{"first_line"=>"7a Annette Road", "second_line"=>"Bob's Landing", "town_city"=>"London", "county"=>"Greater London", "postcode"=>"N76ET", "country"=>"UK"}, "coupon"=>"", "stripe_token"=>"tok_103NDO2KdYRHhGilKFAqsYaA"}, "action"=>"create", "controller"=>"registrations"} 

由於設計Github的頁面上推薦我試圖把下面的我的應用程序控制器:

before_filter :configure_permitted_parameters, if: :devise_controller? 

def configure_permitted_parameters 
    devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:addresses_attributes) } 
end 

無濟於事。 (我也嘗試允許'地址'和'地址')/

任何解決方案真的很感激。 (順便說一下,如果你注意到其他代碼不是最佳的,那麼也很高興知道這一點 - 熱衷於選擇好習慣)。

感謝

+0

嘗試'<%= f.fields_for:addresses do | builder | %>'而不是'<%= f.fields_for:address do | builder | %>'? – scaryguy

+0

@scaryguy這樣做會導致地址字段不會顯示在您的控制器的視圖 – JRTurner1234

+0

中,您還應該添加諸如'@ user.addresses.build'的東西 – scaryguy

回答

1

爲了您accepts_nested_attributes_for就工作,你需要創建一個然後創建以下PARAMS哈希

{:user=> { :addresses_attributes => [ 0 => {:street => "value" ...}, 1=> {:street=>"value2"}] 

爲了做到這一點,你需要使用

<%= fields_for :addresses do |form| %> 
形式

,並且在UserController#new操作中,您需要構建至少一個新地址來填充表單。如果不建立一個新的地址,的form_for節會,就像你說的,不會顯示出來,因爲對形式,也有屬於用戶

def new 
    @user = User.new 
    @user.addresses.build 
    super 
end 

我認爲你需要建立無地址地址協會之前,你叫超級

+0

這感覺很接近。我只是想不出把@ user.address.build行放在哪裏...... – JRTurner1234

+0

把@ user.addresses.build放在super之前。請參閱上面的控制器代碼 –

+0

此答案很有用。我必須定製控制器才能使其工作,但我們現在就在那裏。 - 謝謝。 – JRTurner1234

0

解決。

我改變:

def new 
    @plan = params[:plan] 
    if @plan && ENV["ROLES"].include?(@plan) && @plan != "admin" 
    super 
    else 
     redirect_to root_path, :notice => 'Please select a subscription plan below.' 
    end 
    end 

要:

def new 
    @plan = params[:plan] 
    if @plan && ENV["ROLES"].include?(@plan) && @plan != "admin" 

    build_resource({}) 
    @user.addresses.build 
    respond_with self.resource 

    else 
     redirect_to root_path, :notice => 'Please select a subscription plan below.' 
    end 
    end 

原來

@user.addresses.build 

需要是在制定新的登記控制器兩條線之間。

我需要進一步優化它,通過以適當的方式覆蓋註冊控制器。

相關問題