除非已存在,否则自动创建关系

时间:2014-04-24 04:02:45

标签: ruby-on-rails activerecord

class Cat < ActiveRecord::Base
    has_many :catbowls
    has_many :bowls, through: :catbowls
end

class CatBowl < ActiveRecord::Base
    belongs_to :cats
    belongs_to :bowls
end

class Bowl < ActiveRecord::Base
    has_many :catbowls
    has_many :cats, through: :catbowls
end

在导轨控制台中,我可以给猫一个碗:

cat = Cat.create(name: 'tibbles')
bowl = Bowl.create(color: 'blue')
cat.catbowls #=> []
cat.catbowls.create(bowl_id: bowl.id)
cat.catbowls #=> [#<Bowl id: 1, color: "blue", created_at: "2014-04-23 22:53:15", updated_at: "2014-04-23 22:53:15">]

这是有道理的,并且存在以下关联:

CatBowl.all #=> [#<Bowl id: 1, cat_id: 1, bowl_id: 1>]

然而,这是我的问题。如果我再次创建关联,它实际上并没有改变结果,但我确实得到了一个无效的相同catbowl关系。

cat.catbowls.create(bowl_id: bowl.id)
cat.catbowls #=> [#<Bowl id: 1, color: "blue", created_at: "2014-04-23 22:53:15", updated_at: "2014-04-23 22:53:15">]> 

这种关系与前一种关系相同,完全没用:

CatBowl.all #=> [#<Bowl id: 1, cat_id: 1, bowl_id: 1>,#<Bowl id: 2, cat_id: 1, bowl_id: 1>]

那么如何阻止现有关系的创建?应该使用什么方法链来替换 cat.catbowls.create ,并创建一个关系,除非已经存在?

我可以使用unless语句来执行此操作,

cat.catbowls.create(bowl_id: bowl.id) unless Catbowl.where(cat_id: cat.id, bowl_id:bowl.id).present?

然而,这非常麻烦并导致大量查询。我想要做的是如此常见,我想知道一些铁魔法是否可以帮助我?

3 个答案:

答案 0 :(得分:1)

首先将's'移除到猫和碗上 class CatBowl&lt;的ActiveRecord :: Base的     belongs_to:cat     belongs_to:碗 端

然后创建桥接条目

cat.bowls&lt;&lt;碗

但它无法阻止重复。 防止重复处理条件

条件可能会喜欢: (cat.bowls.find_by(id: bowl.id) || cat.bowls << bowl) if rails 4

(cat.bowls.find_by_id(bowl.id)|| cat.bowls&lt;&lt; bowl)if rails 3

答案 1 :(得分:0)

似乎find_or_create_by就是答案:

Catbowl.find_or_create_by(cat_id: cat.id, bowl_id: bowl.id )

通过方法cat_idbowl_id传递方法,我们会检查仅涉及这些列的唯一性,并忽略所有其他列(例如id)。

答案 2 :(得分:0)

cat = Cat.create(name: 'tibbles')
bowl = Bowl.create(color: 'blue')

简单地将碗与猫联系起来:

cat.bowls << bowl 

将碗与猫联系起来,但先检查是否存在关系:

cat.bowls << bowl unless cat.bowl_ids.include?(bowl.id)