2015-12-29 7 views
6

linkは2つのcomponentscomponenta_idcomponentb_idを持っています。この目的のために、私が持っているLinkモデルファイル内:複合フィールドの一意性のためにこの複雑な検証をモデル化する方法

belongs_to :componenta, class_name: "Component" 
belongs_to :componentb, class_name: "Component" 

validates :componenta_id, presence: true 
validates :componentb_id, presence: true 
validates :componenta_id, uniqueness: { scope: :componentb_id } 
validates :componentb_id, uniqueness: { scope: :componenta_id } 

そして、移行ファイル内:

create_table :links do |t| 
    t.integer :componenta_id, null: false 
    t.integer :componentb_id, null: false 
    ... 
end 
add_index :links, :componenta_id 
add_index :links, :componentb_id 
add_index :links, [:componenta_id, :componentb_id], unique: true 

が質問:このすべての作品。今度は、componantacomponentbの組み合わせを注文に関係なく一意にしたいと思います。したがって、どのコンポーネントがcomponentaで、どちらがcomponentbであるかにかかわらず(すべて同じリンクですが、2つの同じコンポーネント間のリンクです)。したがって、以下の2つのレコードは同じリンクを表しているため、一意ではないため、許可しないでください。

  • componenta_id = 1; componentb_id = 2
  • componenta_id = 2; componentb_id = 1

この一意性検証を作成するにはどうすればよいですか?私はモデルバリデーションを行っていますが(下記参照)、マイグレーション/ dbレベルでバリデーションを追加する必要があるかどうか、またどのように検証する必要があるのだろうか?


私は以下のコードでの作業モデルの検証を持っているモデルの検証

1. test "combination of two links should be unique" do 
    2. assert @link1.valid? 
    3. assert @link2.valid? 
    4. @link1.componenta_id = 3  #@link2 already has combination 3-4 
    5. @link1.componentb_id = 4 
    6. assert_not @link1.valid? 
    7. @link1.componenta_id = 4 
    8. @link1.componentb_id = 3 
    9. assert_raises ActiveRecord::RecordNotUnique do 
    10. @link1.save 
    11. end 
    12.end 

次のテストは上記の作品を確認

before_save :order_links 
validates :componenta_id, uniqueness: { scope: :componentb_id } 

private 
    def order_links 
    if componenta_id > componentb_id 
     compb = componentb_id 
     compa = componenta_id 
     self.componenta_id = compb 
     self.componentb_id = compa 
    end 
    end 

移行/ db検証:
セキュリティの追加レベルとして、このための検証をdbレベルで組み込む方法もありますか?それ以外の場合は、データベースにcomponenta_id = 1 ; componentb_id = 2componenta_id = 2 ; componentb_id = 1の両方のレコードを書き込むこともできます。

+0

を(http://stackoverflow.com/questions/635937/how-do-i-specify-unique-constraint-for-multiple-columns-in-mysql)多対多の関係を作成することです: '多くを持っている:components' 'validates_length_of:components、maximum:2' – skahlert

答えて

2
validates :componenta_id, uniqueness: { scope: :componentb_id } 
validates :componentb_id, uniqueness: { scope: :componenta_id } 
+0

ありがとう!これはまだ1-2の組み合わせと2-1の組み合わせを許容しません。 id 1のコンポーネントが「コンポーネントa」であり、id 2のコンポーネントが「コンポーネントb」であることを意味し、もう一度、これは私が避けたいものです。コンビネーション1-2は、どちらが「componenta」であり、どちらが「componentb」であるかにかかわらず、ユニークでなければなりません。 – Nick

+0

それは私が答えを削除しようとしていた理由です。私は投票しない限り、私はそれを残すと思った:D –

+0

上記を試してみませんか?超賢いではなく、あなたが必要とする結果を得るかもしれない –

4

おそらくとのリンクの作成を制御することが可能である:

def create_unique_link(comp_1, comp_2) 
    if comp_1.id > comp_2.id 
    first_component = comp_1 
    second_component = comp_2 
    end 
    link = Link.find_or_create_by(componenta_id: first_comp.id, componentb_id: second_comp.id) 
end 

あなたは検証が必要な場合は、カスタム検証することができます:[この会話]で

def ensure_uniqueness_of_link 
    if comp_1.id > comp_2.id 
    first_component = comp_1 
    second_component = comp_2 
    end 

    if Link.where(componenta_id: first_component.id, componentb_id: second_component).first 
    errors.add(:link, 'Links should be unique') 
    end 

end 
+0

これはすべてのモデル検証ですね。モデルの検証のために、私はそれを動作させるコードです。それはdbレベルでの検証です。つまり、まだマイグレーションファイルにありますが、これはまだ失われています。 – Nick

+0

私はデータベースレベルで一般的な方法を考えることはできません。おそらくそれを防ぐためにデータベーストリガーのいくつかの形式を使用する?データベースベンダー固有のメソッドもあります。 – roob

関連する問題