Асоциации и (множество) външни ключове в релси (3.2): как да ги опишем в модела и да напишем миграции

Имам 3 модела: въпрос, опция, правило

Въпросът има_много опции; Опцията се нуждае от външен ключ за question_id

Таблицата с правила се състои от 3 чужди_ключа:

  • 2 колони/препратки към question_ids -> външни ключове, именувани като 'assumption_question_id' и 'consequent_question_id'
  • 1 колона/препратка към option_id -> външен ключ, именуван като option_id или condition_id

Асоциации за правило: Въпросът има_много правила; и Option has_one rule

Искам да разбера как да напиша миграции за това и как това се свързва с изразите 'has_many'/'belongs_to', които пиша в моя модел, и опцията ':foreign_key', която мога да включа в моя модел.

Имах това за моята миграция на Option, но не съм сигурен как операторът "add_index" работи по отношение на чужди ключове и как мога да го използвам за моята миграция на Rule: (моделите ми Question и Options имат подходящи оператори has_many и belongs_to - и работят добре)

class CreateOptions < ActiveRecord::Migration
  def change
    create_table :options do |t|
      t.integer :question_id
      t.string :name
      t.integer :order

      t.timestamps
    end
    add_index :options, :question_id
  end
end

Благодаря ти за помощта!


person sooks    schedule 12.07.2012    source източник


Отговори (3)


add_index добавя индекс към посочената колона, нищо Повече ▼.

Rails не предоставя собствена поддръжка при миграции за управление на външни ключове. Такава функционалност е включена в скъпоценни камъни като foreigner. Прочетете документацията на този скъпоценен камък, за да научите как се използва.

Що се отнася до асоциациите, просто добавете колоните, които споменахте във вашия въпрос, към всяка таблица (предоставената от вас миграция изглежда добре; може би липсва :rule_id?)

След това задайте асоциациите във вашите модели. За да започнете

class Question < ActiveRecord::Base
  has_many :options
  has_many :assumption_rules, class_name: "Rule"
  has_many :consequent_rules, class_name: "Rule"
end

class Rule < ActiveRecord::Base
  belongs_to :option
  belongs_to :assumption_question, class_name: "Question", foreign_key: :assumption_question_id, inverse_of: :assumption_rules
  belongs_to :consequent_question, class_name: "Question", foreign_key: :consequent_question_id, inverse_of: :consequent_rules
end

class Option < ActiveRecord::Base
  belongs_to :question
  has_one    :rule
end

Забележка Това е само (нетествано) начало; опции може да липсват.

Горещо ви препоръчвам да прочетете

Редактиране: За да отговорите на въпроса във вашия коментар

class Option < ActiveRecord::Base
  belongs_to :question
  # ...

belongs_to казва на rails, че колоната question_id във вашата options таблица съхранява id стойност за запис във вашата questions таблица. Rails предполага, че името на колоната е question_id въз основа на символа :question. Можете да инструктирате rails да разглеждат различна колона в таблицата options, като посочите опция като foreign_key: :question_reference_identifier, ако това е името на колоната. (Обърнете внимание, че вашият Rule клас в моя код по-горе използва опцията foreign_key по този начин).

Вашите миграции не са нищо повече от инструкции, от които Rails ще чете и изпълнява команди във вашата база данни. Асоциациите на вашите модели (has_many, belongs_to и т.н...) информират Rails за това как бихте искали Active Record да работи с вашите данни, предоставяйки ви ясен и лесен начин за взаимодействие с вашите данни . Моделите и миграциите никога не взаимодействат помежду си; и двамата взаимодействат независимо с вашата база данни.

person deefour    schedule 12.07.2012
comment
Благодаря ви за помощта.. Бях опитал нещо подобно на това, но се забих, опитвайки се да разбера как изглежда миграцията на моята таблица с правила... - person sooks; 12.07.2012
comment
(натиснат ентер твърде рано) ... Също така четох повече за „add_index“ и прочетох, че значително подобрява скоростта. Опитвам се да разбера как това се вписва в миграцията на правилата - person sooks; 12.07.2012
comment
Да, моето мнение е, че методът add_index няма нищо общо с управлението на външни ключове. Както споменахте, има много ресурси за научаване за предимствата на индексирането на колони, кога (не) да го правите и други съображения. - person deefour; 12.07.2012
comment
Добре. Как изявленията, които предоставям в модела, са свързани с моята миграция? Имат ли имената на колоните някакво значение в контекста на изразите 'has_many' и 'belongs_to'? Вече се опитах да прочета тези две връзки и все още ми липсва „обща картина“ на това, което се случва - person sooks; 12.07.2012
comment
Опитах се да отговоря на въпроса ви, но наистина трябва да помислите да отделите време за статиите, към които направих връзка - те го обясняват много по-добре, отколкото аз мога (и дори предоставят илюстрации за това как колоните в базата данни са свързани с асоциациите) - person deefour; 12.07.2012
comment
Моделите и миграциите никога не взаимодействат помежду си; и двамата взаимодействат независимо с вашата база данни. - Как релсите знаят към коя колона се препраща, когато моделът посочва тези отношения? Може ли „правилата“ да имат 3 произволни имена? (чрез експериментиране мисля, че отговорът на това е не)... и ако назова една от колоните си с правила „assumption_question“ или „assumption_question_id“, получавам дебела SQL грешка, хвърлена към мен. - person sooks; 13.07.2012
comment
Моята таблица с опции има колона, наречена question_id. Тъй като въпросите и опциите имат проста връзка "един към много", просто поставянето на has_many и belongs_to в моя модел е достатъчно за rails, за да разберат останалото. Тъй като Rules се нуждае от два question_ids и аз очевидно не мога да назова две колони с едно и също име, очевидно има някаква работа, която съответства на решението от страната на модела, което представихте по-горе - person sooks; 13.07.2012
comment
Точно така, във вашата миграция създавате ръчно 2 колони като :assumption_question_id и :consequent_question_id. След това ги препращате във вашите асоциации, както направих по-горе, като посочите нестандартните имена на колони за външните ключове с опцията foreign_key. - person deefour; 13.07.2012
comment
yes.. Foreign_key: :assumption_question_id трябва да бъде включен и за двата оператора has_many и belongs_to, а колоните в таблицата трябва да бъдат наименувани по същия начин, както казахте. благодаря ви за търпението и помощта - person sooks; 17.07.2012

Забележка: Намерих този начин за разрешаване на проблема. Любезност от Китай.

Ако имате RailsAdmin с вас, може да забележите, че можете да видите всички правила на един въпрос, стига едно поле от двете полета на въпрос (assumption_question_id, consequent_question_id) да е равно на id на въпроса.

Направих подробен тест за това и открих, че Rails винаги генерира условие "question_id = [current_id]", което прави to_sql изходи

SELECT `rules`.* FROM `rules` WHERE `rules`.`question_id` = 170

И причината е следният модел

class Question < ActiveRecord::Base
  has_many :options
  # Notice ↓
  has_many :rules, ->(question) { where("assumption_question_id = ? OR consequent_question_id = ?", question.id, question.id) }, class_name: 'Rule'
  # Notice ↑
end

прави Question.take.rules.to_sql така

SELECT `rules`.* FROM `rules` WHERE `rules`.`question_id` = 170 AND (assumption_question_id = 170 OR consequent_question_id = 170)

Е, че все още не сме се отървали от дразнещото question_id, така че без значение как правилно описваме или поставяме условия, нашето условие следва това „И“.

Тогава трябва да се отървем от него. Как?

Щракнете тук и ще разберете как, Намерете сектор 8.1 и можете да видите

Article.where(id: 10, trashed: false).unscope(where: :id)
# SELECT "articles".* FROM "articles" WHERE trashed = 0

Тогава нека го направим:

class Question < ActiveRecord::Base
  has_many :options
  # Notice ↓
  has_many :rules, ->(question) { unscope(where: :question_id).where("assumption_question_id = ? OR consequent_question_id = ?", question.id, question.id) }, class_name: 'Rule'
  # Notice ↑
end

class Rule < ActiveRecord::Base
  belongs_to :option
  belongs_to :assumption_question, class_name: "Question", foreign_key: :assumption_question_id, inverse_of: :assumption_rules
  belongs_to :consequent_question, class_name: "Question", foreign_key: :consequent_question_id, inverse_of: :consequent_rules
end

class Option < ActiveRecord::Base
  belongs_to :question
  has_one    :rule
end

Готово.

Накрая

Това е първият ми отговор тук в stackoverflow и този метод никога не се намира никъде другаде.

Благодаря за четенето.

person sunsoft    schedule 04.11.2016

Можете да зададете външен ключ във вашия модел по следния начин:

class Leaf < ActiveRecord::Base
belongs_to :tree, :foreign_key => "leaf_code"
end

Не е необходимо да указвате това в миграция, rails ще изтегли външния ключ от дефиницията на моделния клас.

person OpenCoderX    schedule 10.01.2013