【问题标题】:Why does associated object not save?为什么关联对象不保存?
【发布时间】:2010-10-23 21:01:01
【问题描述】:

我有一个 ruby​​ (on rails) 课程:

class User < ActiveRecord::Base
  # relationships
  belongs_to :current_shipping_address, :class_name => "Address"
  belongs_to :subscription

  # Validators
  validates_presence_of :subscription
  validates_presence_of :current_shipping_address
end

我在控制器中执行此操作:

subscription = Subscription.new

address_info = params[:user].delete(:address) rescue {}
@address = Address.new(address_info.merge(:country => "US"))

@user = User.new(params[:user].merge(:first_name => @address.first_name, :last_name => @address.last_name))
@user.subscription = subscription
@user.current_shipping_address = @address
@user.save!

此时,令人难以置信的是,我有一个 @user 已保存在数据库中,但没有 current_shipping_address(尽管进行了验证)。订阅也已保存在数据库中。

地址未保存。

我在这里缺少什么? 1 - 如何在没有验证失败的情况下保存用户? 2 - 为什么地址没有保存?

如何更改此代码以保存地址(如我所料)?

我在 ruby​​ on rails 3 下运行它。

谢谢!

【问题讨论】:

    标签: ruby-on-rails activerecord ruby-on-rails-3


    【解决方案1】:

    在您的情况下,您不能让用户保存订阅和 current_shipping_address,因为它们不是模型用户中的简单字段。您将它们定义为通过 belongs_to 关联到用户的模型,我不确定您愿意做什么,但如果我理解正确的一种方法是使用嵌套属性:

    class User < ActiveRecord::Base
      # relationships
      has_many :current_shipping_addresses, :class_name => "Address", :dependant => destroy
      has_many :subscriptions, :dependant => destroy
    
    
      # Nesting
      accepts_nested_attributes_for :subscriptions
      accepts_nested_attributes_for :current_shipping_addresses
    
    end
    

    之后,当您创建并保存用户时,订阅和 current_shipping_address 将随之保存。

    这里有更多关于关联的信息:http://guides.rubyonrails.org/association_basics.html

    【讨论】:

      【解决方案2】:

      如果您不坚持使用标准表结构,则需要告诉它外键是什么。您只需要添加:

      belongs_to :current_shipping_address, :class_name => "Address", :foreign_key => "address_id"
      

      或您用来将地址 id 存储到地址表的任何列。

      虽然这不是做嵌套属性的推荐方式。我建议在您的表单中使用fields_for,而不是使用以下行:

      address_info = params[:user].delete(:address) rescue {}
      @address = Address.new(address_info.merge(:country => "US"))
      

      你可以这样做

      # ...您的地址字段...

      这样您就可以在运行 @user.save! 时简单地保存地址

      您仍然可以预先添加:country =&gt; "US"

      params[:user][:current_shipping_address][:country] = "US"
      

      然后运行保存。不过,这完全取决于您。

      【讨论】:

      • 您的 cmets 在表单结构等方面是公平的,但这实际上并不能解决地址对象不保存的问题(据我所知)。我正在使用 formtastic,所以不幸的是,您的解决方案并不直接适用于我。使用您的解决方案,您仍然可能最终没有保存地址。
      • 哦,而且user对象上的字段是current_shipping_address_id,所以外键没有问题,
      【解决方案3】:

      试试这个方法!

      subscription = Subscription.new
      
      address_info = params[:user].delete(:address) rescue {}
      
      @user = User.new(params[:user].merge(:first_name => @address.first_name, :last_name => @address.last_name))
      @user.subscription = subscription
      @user.current_shipping_address << Address.new(address_info.merge(:country => "US"))
      @user.save!
      

      【讨论】:

      • 这不起作用,因为 current_shipping_address 不是数组
      【解决方案4】:

      似乎问题在于地址实际上未能保存。不是因为验证,而是因为“before_create”方法中的错误(是的,我知道我没有给你地址对象......当时我认为这并不重要!)。

      class Address < ActiveRecord::Base
        # relationships
      
        # Validators
        validates_presence_of :city, :state, :country, :first_name, :last_name, :address_1
      
        before_create :check_state
        before_create :check_country
      
        def check_state
          retval = true
          state.upcase!
          if country == "US" and !US_STATES.map{|s| s[1]}.include?(state)
            errors.add(:state, "Must be valid")
            retval = false
          end
      
          retval
        end
      end
      

      检查状态失败。但这意味着该地址通过了“有效?”呼叫,这似乎是所有活动记录所关心的。 (这个方法确实应该是验证)

      我已经转为这样做(感谢 enokd 提供链接!):

      @user = User.new(params[:user].merge(:first_name => @address.first_name, :last_name => @address.last_name))
      @user.build_subscription(:subscription_plan_id => @subscription_plan.id)
      @user.build_current_shipping_address(address_info.merge(:country => "US")) 
      

      我没有费心去全面调查,但是,如果地址无法保存,它会停止整个@user.save! 就我个人而言,我认为这可能是一个小错误,或者肯定是一个意外的行为,但我知道什么!

      【讨论】:

      • 这可能会给您带来比您想要的更多的问题。你应该做什么而不是“before_create”,它应该是“验证”。如果出现问题,这将为您提供行为(和失败消息)。
      【解决方案5】:

      试试:

      class User < ActiveRecord::Base
        # relationships
        has_one :current_shipping_address, :class_name => "Address", :dependant => destroy
        has_many :subscriptions, :dependant => destroy
      
        validates :current_shipping_address, :presence => true
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-04
        • 2011-12-18
        相关资源
        最近更新 更多