我的某些模型之间存在has_many
和belongs_to
关联,类似于以下内容:
class Customer < ActiveRecord::Base
# attributes id, name, hash_salt etc
has_many :contacts
end
class Contact < ActiveRecord::Base
# attributes id, customer_id, email_hash, email etc
belongs_to :customer
end
我希望有一个自定义创建器方法,允许某些逻辑在创建新对象之前运行查找现有记录,类似于以下内容:
class Contact < ActiveRecord::Base
belongs_to :customer
class << self
def find_or_create(customer_id, params = {})
customer = Customer.find(customer_id)
email_hash = SCrypt::Engine.hash_secret(params[:email], customer.hash_salt)
record = Contact.where(customer_id: customer_id).where(email_hash: email_hash).first
if !record
record = new(params)
end
record
end
end
end
如果我通过
等关联来调用自定义创建者cust = Customer.find(1)
cust.contacts.find_or_create(cust.id, contact_params)
有什么方法可以省略customer_id
param并以某种方式从调用者那里获取它?当我致电cust.contact.create(contact_params)
答案 0 :(得分:3)
这里有一些不同的东西,但我认为你所关注的关键是如何“扩展”关联以在集合代理上定义自定义方法。如果这听起来像希腊语,我有好消息......这很容易:
class Customer
has_many :contacts do
def find_or_create(params = {})
proxy_association.owner #=> Customer instance
end
end
end
这就是说,你可能想要在find_or_create
上重构那个Contact
方法,所以它没有那么多。
class Customer
has_many :contacts do
def by_email(email)
proxy_association.target.by_email proxy_association.owner.hashed_value(email)
end
end
def hashed_value(value)
SCrypt::Engine.hash_secret(value, hash_salt)
end
end
class Contact
scope :by_email, ->(email) { where(email_hash: email) }
end
现在你可以得到这样的联系人:
@customer.contacts.by_email(params[:email])
或者你可以检查这样的存在:
@customer.contacts.by_email(params[:email]).any?
由于你已经得到了这种散列前驱,因此幂等创造更具挑战性。通常你可以这样做:
@customer.contacts.where(params).first_or_create
# won't work for your case
在这种情况下,您需要预先散列电子邮件并将其从参数中删除。您也可以扩展与此关联,但这是要点:
contact = @customer.contacts.by_email(params.delete(:email))
contact = contact.where(params).first_or_create
祝你好运。
答案 1 :(得分:0)
您可以在浏览相关行时使用first_or_create。试试这样: -
cust.contacts.where(contact_params).first_or_create