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

前端 未结 3 537
無奈伤痛
無奈伤痛 2020-12-22 03:07

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:

相关标签:
3条回答
  • 2020-12-22 03:16

    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.

    0 讨论(0)
  • 2020-12-22 03:21

    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.

    0 讨论(0)
  • 2020-12-22 03:22

    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.

    0 讨论(0)
提交回复
热议问题