开发者

Rails 3 nested attributes: How can I assign a matching record to the parent model instead of creating a new record every time?

开发者 https://www.devze.com 2023-03-20 15:45 出处:网络
Here\'s the basic setup: I have an Order model. An Order has one Address and it accepts_nested_attributes_for :address.

Here's the basic setup:

I have an Order model. An Order has one Address and it accepts_nested_attributes_for :address.

I have a basic order form where I ask a user to input her address. This is handled with nested_fields_for. Everything works great - new addresses are validated and assigned nicely.

However, the problem is that it creates a new Address every time, even if an Address already exists with identical attributes.

I would like to modify the behavior so that if the user-inputted address matches all the attributes for an existing Address, the order assigns the existing Address to itself rather than creating a new one.

The methods I have tried are:

  1. In the controller, try to find an existing Address record with the nested attributes (params[:order][:address_attributes]). If a match exists, delete all the nested attributes and replace them with params[:order][:address_id].

  2. Don't use nested_attributes_for at all and instead override the address= method in the model, then just use the controller to create a new Address based on the parameters and then hand it off to the model.

Both of these solutions seem various 开发者_高级运维degrees of messy. Could somebody please enlighten me on whether this is a controller or model responsibility, and perhaps suggest an elegant way to accomplish this?

Thanks in advance.


Have you tried something like this?

class Order < ActiveRecord::Base
  # [..]

  before_save :replace_existing_address!

  def replace_existing_address!
    db_address = Address.where(:city => self.address.city,
                :street => self.address.street,
                :number => self.address.number).first
    self.address = db_address if db_address
  end

end


Since I'm asking this as a survey of good ways to do this, I figured I'd offer the solution I'm currently using as well as a basis for comment.

In the Controller:

@new_address = Address.new( params[:order][:address] )
@order.address = new_address
@order.update_attributes( params[:order] )

In the Model:

def address=( address )
    return unless address and address.is_a? Address

    duplicate_address = Address.where( address_1: address.address_1, 
                                       address_2: address.address_2,
                                       [etc. etc.] ).first

    if duplicate_address
        self.address_id = duplicate_address.id
    else
        address.save
        self.address_id = address.id
    end
end


I it's truly a :has_one relationship as you say and not a :has_many, you don't need to explicitly assign the address like you do in your own answer. That's what accepts_nested_attributes is for, after all. This line by itself should work:

@order.update_attributes( params[:order] )

That should create a new address if none exists, and update an existing one.

Your solution may work, but it a) doesn't take advantage of accepts_nested_attributes and b) will leave lots of orphaned addresses in your database.

0

精彩评论

暂无评论...
验证码 换一张
取 消