views:

97

answers:

4

I have models

class Question < ActiveRecord::Base
  WEIGHTS = %w(medium hard easy)

  belongs_to :test
  has_many :answers, :dependent => :destroy
  has_many :testing_questions
end

class Testing < ActiveRecord::Base
  belongs_to :student, :foreign_key => 'user_id'
  belongs_to :subtest
  has_many :testing_questions, :dependent => :destroy
  has_many :questions, :through => :testing_questions
end

So when I try to bind questions to testing on it's creation:

>> questions = Question.all
...
>> questions.count
=> 3
>> testing = Testing.create(:user_id => 3, :subtest_id => 1, :questions => questions)
  Testing Columns (0.9ms)   SHOW FIELDS FROM `testings`                              
  SQL (0.1ms)   BEGIN                                                                
  SQL (0.1ms)   COMMIT                                                               
  SQL (0.1ms)   BEGIN                                                                
  Testing Create (0.3ms)   INSERT INTO `testings` (`created_at`, `updated_at`, `user_id`, `subtest_id`) VALUES('2010-05-18 00:53:05', '2010-05-18 00:53:05', 3, 1)                                                                                                                                                        
  TestingQuestion Columns (0.9ms)   SHOW FIELDS FROM `testing_questions`                                                                                     
  TestingQuestion Create (0.3ms)   INSERT INTO `testing_questions` (`question_id`, `created_at`, `updated_at`, `testing_id`) VALUES(1, '2010-05-18 00:53:05', '2010-05-18 00:53:05', 31)                                                                                                                                  
  TestingQuestion Create (0.4ms)   INSERT INTO `testing_questions` (`question_id`, `created_at`, `updated_at`, `testing_id`) VALUES(2, '2010-05-18 00:53:05', '2010-05-18 00:53:05', 31)                                                                                                                                  
  TestingQuestion Create (0.3ms)   INSERT INTO `testing_questions` (`question_id`, `created_at`, `updated_at`, `testing_id`) VALUES(3, '2010-05-18 00:53:05', '2010-05-18 00:53:05', 31)                                                                                                                                  
  TestingQuestion Create (0.3ms)   INSERT INTO `testing_questions` (`question_id`, `created_at`, `updated_at`, `testing_id`) VALUES(1, '2010-05-18 00:53:05', '2010-05-18 00:53:05', 31)                                                                                                                                  
  TestingQuestion Create (0.3ms)   INSERT INTO `testing_questions` (`question_id`, `created_at`, `updated_at`, `testing_id`) VALUES(2, '2010-05-18 00:53:05', '2010-05-18 00:53:05', 31)                                                                                                                                  
  TestingQuestion Create (0.3ms)   INSERT INTO `testing_questions` (`question_id`, `created_at`, `updated_at`, `testing_id`) VALUES(3, '2010-05-18 00:53:05', '2010-05-18 00:53:05', 31)                                                                                                                                  
  SQL (90.2ms)   COMMIT                                                                                                                                      
=> #<Testing id: 31, subtest_id: 1, user_id: 3, created_at: "2010-05-18 00:53:05", updated_at: "2010-05-18 00:53:05">

There are 6 SQL queries and 6 records in testing_questions are created. Why?

A: 

A first issue could be the naming of your join table

testing_questions

Rails expects the join table’s name to be a concatenation of the two table names in alphabetical order

question_testings
tommasop
I know, but there is has_many :testing_questions, :dependent => :destroy and has_many :questions, :through => :testing_questionsI think it's enough for determining the intermediate table.
Antiarchitect
I think nonetheless you have to use set_table_name to use legacy table naming
tommasop
That is not true: the table-name is derived from the modelname. If the modelname is TestingQuestion, the table-name will be testing_questions. Using the `:through` you explicitly specify the name of the model of the join-table.
nathanvda
A: 
Vamsi
I do not want to use obsolete has_and_belongs_to_many, I want to use has_many :through. Changing scheme or something like this can be a problem solution, but the problem is solved already, now I want to know why Rails do so.
Antiarchitect
can you tell me which gem or plugin you are using for displaying the sql queries in the console?And could you share your solution as well?
Vamsi
I do not remember exactly, but try to add this file to your home directory: http://gist.github.com/405733
Antiarchitect
Thanks it worked.
Vamsi
@Vamsi, @antiarchitect: Logs in console can be done by adding this code to the ~/.irbrc file. All projects would have it.(the formatting cannot be controlled in comments, so I am adding semi-colons)if ENV.include?('RAILS_ENV') ('RAILS_DEFAULT_LOGGER') ; require 'logger' ; RAILS_DEFAULT_LOGGER = Logger.new(STDOUT) ;end ;
ramonrails
A: 

Without looking at the Rails source or trying around, I'd suggest to try either removing the "has_many :testing_questions" from the Question class or add a has_many ... :through there. Just now Rails just has a relation to the join table, but not to the "real" target from that side.

The name of the join table shouldn't cause any problems here.

averell
+1  A: 

I have created a very simple example that handles your example:

class Question < ActiveRecord::Base
  has_many :testing_questions
end

class Testing < ActiveRecord::Base
  has_many :testing_questions
  has_many :questions, :through => :testing_questions
end

class TestingQuestion < ActiveRecord::Base
  belongs_to :question
  belongs_to :testing
end

.. and then i can just do the following, and no duplicate records are created:

Loading development environment (Rails 2.3.5)
>> q1 = Question.new
=> #<Question id: nil, title: nil, ask: nil, created_at: nil, updated_at: nil>
>> q1.title = "Dit is de eerste vraag"
=> "Dit is de eerste vraag"
>> q2 = Question.new
=> #<Question id: nil, title: nil, ask: nil, created_at: nil, updated_at: nil>
>> q2.title = "Dit is de tweede vraag"
=> "Dit is de tweede vraag"
>> q1.save
=> true
>> q2.save
=> true
>> tt = Testing.new
=> #<Testing id: nil, name: nil, description: nil, action: nil, created_at: nil, updated_at: nil>
>> tt.questions
=> []
>> tt.name = "Test1"
=> "Test1"
>> tt.questions << q1
=> [#<Question id: 1, title: "Dit is de eerste vraag", ask: nil, created_at:   "2010-05-18 19:40:54", updated_at: "2010-05-18 19:40:54">]
>> tt.questions << q2
=> [#<Question id: 1, title: "Dit is de eerste vraag", ask: nil, created_at: "2010-05-18 19:40:54", updated_at: "2010-05-18 19:40:54">, #<Question id: 2, title: "Dit is de tweede vraag", ask: nil, created_at: "2010-05-18 19:40:59", updated_at: "2010-05-18 19:40:59">]
>> tt.testing_questions
=> []
>> tt.save
=> true
>> tt.testing_questions
=> [#<TestingQuestion id: 1, question_id: 1, testing_id: 1, extra_info: nil, created_at: "2010-05-18 19:41:43", updated_at: "2010-05-18 19:41:43">, #<TestingQuestion id: 2, question_id: 2, testing_id: 1, extra_info: nil, created_at: "2010-05-18 19:41:43", updated_at: "2010-05-18 19:41:43">]
>>

Actually that is completely the same as you have, except for the TestingQuestion (which you didn't show). Does that help?

nathanvda
I see that It's all ok in your example. TestingQuestion model exactly the same as yours... But my way to bind associated records to testings different. I try to bind them on creation of Testing
Antiarchitect
Yes but that should not make a difference, it is only at save that the TestingQuestion's get created. I assume in your example you write Testing.create, you still have to call save explicitly. Right? I need to. I am testing against Rails 2.3.5. Also: i see that my TestingQuestion has an id, which should not be needed.
nathanvda