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:
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 withparams[:order][:address_id]
.Don't use
nested_attributes_for
at all and instead override theaddress=
method in the model, then just use the controller to create a newAddress
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.
精彩评论