Associations and (multiple) foreign keys in rails (3.2) : how to describe them in the model, and write up migrations

ⅰ亾dé卋堺 提交于 2019-12-18 09:28:53

问题


I have 3 models: Question, Option, Rule

Question has_many options; Option needs a foreign key for question_id

Rule table consists of 3 foreign_keys:

  • 2 columns/references to question_ids -> foreign keys named as 'assumption_question_id' and 'consequent_question_id'
  • 1 column/reference to option_id -> foreign key named as option_id or condition_id

Associations for Rule: Question has_many rules; and Option has_one rule

I want to understand how to write up migrations for this, and how that associates to the 'has_many'/'belongs_to' statements I write up in my model, and the ':foreign_key' option I can include in my model.

I had this for my Option migration, but I'm not sure how the "add_index" statement works in terms of foreign keys, and how I can use it for my Rule migration: (my Question and Options models have appropriate has_many and belongs_to statements - and work fine)

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

Thank you for the help!


回答1:


add_index adds an index to column specified, nothing more.

Rails does not provide native support in migrations for managing foreign keys. Such functionality is included in gems like foreigner. Read the documentation that gem to learn how it's used.

As for the associations, just add the columns you mentioned in your Question to each table (the migration you provided looks fine; maybe it's missing a :rule_id?)

Then specify the associations in your models. To get you started

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

Note This is just a (untested) start; options may be missing.

I strongly recommend you read

  • http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
  • http://guides.rubyonrails.org/association_basics.html

Edit: To answer the question in your comment

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

The belongs_to tells rails that the question_id column in your options table stores an id value for a record in your questions table. Rails guesses the name of the column is question_id based on the :question symbol. You could instruct rails to look at a different column in the options table by specifying an option like foreign_key: :question_reference_identifier if that was the name of the column. (Note your Rule class in my code above uses the foreign_key option in this way).

Your migrations are nothing more than instructions which Rails will read and perform commands on your database based from. Your models' associations (has_many, belongs_to, etc...) inform Rails as to how you would like Active Record to work with your data, providing you with a clear and simple way to interact with your data. Models and migrations never interact with one another; they both independently interact with your database.




回答2:


Note: I have found this way to solve the problem.Kindness from China.

If you have RailsAdmin with you,you may notice that you can see all rules of one question as long as one field of both question fields(assumption_question_id,consequent_question_id) equals to id of the question.

I have done detailed test on this and found out that Rails always generates a condition "question_id = [current_id]" which make to_sql outputs

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

And the reason that the following model

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

makes Question.take.rules.to_sql be like this

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

Is that we have not yet get ride of the annoy question_id so no matter how we describe or condition properly, our condition follows that "AND".

Then,we need to get ride of it.How?

Click here and you will know how,Find sector 8.1,and you can see

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

Then lets do it:

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

All done.

Finally

This is my first answer here at stackoverflow,and this method is never found anywhere else.

Thanks for reading.




回答3:


You can set a foreign key in your model like this:

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

You do not need to specify this in a migration, rails will pull the foreign key from the model class definition.



来源:https://stackoverflow.com/questions/11458079/associations-and-multiple-foreign-keys-in-rails-3-2-how-to-describe-them-i

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!