Multiple Associations between the same model in Rails - ruby-on-rails-3

I am working on QA site where I have a Question model and an Answer model and the association between them is like
class Question < ActiveRecord::Base
has_many :answers
end
My answers model is
class Answer < ActiveRecord::Base
belongs_to :question
end
Now I need to create another association between question and answer where I can access the one answer the author of the question finds the best. So what I need is something like
class Question < ActiveRecord::Base
has_many :answers
has_one :accepted_answer, :class_name => 'Answer', :foreign_key => ['answer_id, accepted']
end
This association fails rightly as I have no way of specifying that I expect accepted to be true and I get a MySQL error. Is there a way I can get this to work while using a boolean as composite foreign key?
MySQL server version for the right syntax to use near '["answer_id", "accepted"] = 30) LIMIT 1' at line 1: SELECT `answers`.* FROM `answers` WHERE (`answers`.["answer_id", "accepted"] = 30)
The solution that I have currently employed is creating an association from answers to Question as follows
class Answer < ActiveRecord::Base
belongs_to :question
has_one :inverse_accepted_answer, :class_name => 'Question', :foreign_key => 'accepted_id'
end
and for Question as
Class Question < ActiveRecord::Base
belongs_to :answer, :foreign_key => 'accepted_id'
has_many :answers
end
The problem is in this case i access the selected answer as Question.find(10).answer instead of using a more expressive name as selected_answer.
Is there a way i can define a name for the relationship from the belongs_to end. Secondly what would be the right way to go about this. I technique seems to round about to correct
I am using Rails 3 and Ruby 1.9.2
Thanks in advance

what about something like that:
has_one :accepted_answer, :class_name => 'Answer', :conditions => "accepted = true"

Related

Rails has_one through relationship

I have three tables: question, question_choices and response.
Question
ID
Name
Tag
Question_Choice
ID
Question_id
Label
Value
Response
ID
User_id
Question_id
Value
Given a response, I want to see the question tag and the question_choice label for that. Getting either response or choice to relate to question are no problem, but getting response to relate to choice is proving challenging because I need to say "value=value". I have tried setting up a :has_one, :through => :question relationship but i'm not sure how to express the "value=value" stuff. Do I just need to use raw sql?
I assume that you don't want to change the table structure (which would be the easiest solution). This is the solution for your current table structure:
app/models/question.rb
class Question < ActiveRecord::Base
has_many :question_choices
end
app/models/question_choice.rb
class QuestionChoice < ActiveRecord::Base
belongs_to :question
end
app/models/response.rb
class Response < ActiveRecord::Base
belongs_to :question
belongs_to :question_choice, :primary_key => 'value', :foreign_key => 'value', :conditions => proc { "question_id = #{self.question_id}" }
end
Response.first.question_choice will give you the question_choice of the first response.
I'm guessing questions have many question choices, and that responses have a single 'selected' question choice. To model this you would need a direct association between the response and question choice as well, e.g.
class Question < ActiveRecord::Base
has_many :question_choices
end
class QuestionChoice < ActiveRecord::Base
belongs_to :question
end
class Response < ActiveRecord::Base
belongs_to :user
belongs_to :question
belongs_to :question_choice
end
This means that you would need to add a question_choice_id column to your responses table.

Should I use an ActiveRecord has_may-through relation, or a method fetching related items?

I have a FormResponse which belongs_to a Form; the Form then has_many Questions:
class FormResponse < ActiveRecord::Base
belongs_to :form
end
class Form < ActiveRecord::Base
has_many :form_responses
has_many :questions
end
class Question < ActiveRecord::Base
end
When I find myself needing questions in the context of a form, a lot, I prefer to call questions on FormResponse, like so:
form_response = FormResponse.find(id)
form_response.questions
In order to make questions avaiable, I can do this in ActiveRecord:
class FormResponse < ActiveRecord::Base
belongs_to :form
has_many :questions, :through => :form
end
or with an instant-method:
class FormResponse < ActiveRecord::Base
belongs_to :form
def questions
self.form.questions unless self.form.nil?
end
end
I am not interested in setting questions on FormResponse (I won't need
things like FormResponse.questions << or
FormResponse.questions.build), just the fetching.
What are the benefits of using the has_many :questions, :through =>
:form over using a method, and vice-versa? Are there benefits like like lazy-loading, better
SQL and so on?
Does AR give any rules of thumb on when to use an AR-relation and when
to simply write your own methods?
I think this is more a question of encapsulation then of ActiveRecord.
In general I would apply I rule of thumb here:
Use has_many :through when you actually want to model a relation.
Implement a delegation method when all you want to do is not violating the Law of Demeter or keeping your code DRY.

Searching a has_many :through association including the middle model

First, the topic.
I have three models, which are linked between each other with a has_many :trough association like this:
#User model
has_many :chars_del, :class_name => CharDelegated, :dependent => :destroy
has_many :chars, :through => :chars_del
#CharDelegated model
#has a field owner:integer
belongs_to :char
belongs_to :user
#Char model
#has fields name:string
has_many :chars_del, :class_name => CharDelegated
has_many :users, :through => :chars_del
What I need to do is I need to search from a User Record to find all the Chars that the particular user ownes (:owner field is true) ordered by name. I have been stuck with this for a couple hours now, so I believe that I could have missed a very simple answer... But nothing that I have tried so far did work even a bit.
UPDATE
found something that works:
user.chars.where(:char_delegateds => {:owner => 1}).order('name')
don't know why the :chars_del gave an error, but the full table name did the job.
Andrew, your answer works well too and is a little faster on the database, thans alot.
Does
user.chars.order('name')
not work? (Given user is a single User instance.)
Edit
Given your new information:
CharDelegated.where(user_id: user.id, owner: true).map(&:char)
should work.
In your specific example you don't need to search through the middle table but if you want to see an example of how to use the joining table and search through it for a more complex scenario you can do it this way.
#char = Char.all(:include => :users, :conditions => ["char_delegated.user_id in (?)", user_id]).order('name')

How to give foreign key a name in RoR 3?

How can I give foreign key a name in RoR?
I use following command to give foreign key:
rails generate scaffold Table2 id:integer Table1:references
This command adds foreign key of Table1 in Table2 but with default name that is Table1_id. So how can I give custom name to it for example my_table_f_key instead of Table1_id.
I'm using Ruby 1.9.2 and Rails 3.0.3.
Edit:-
In my project.rb model:
belongs_to :own, :class_name => User
In my user.rb model:
has_many :owned_projects, :class_name => Project, :foreign_key => :owner
how I created my project model
rails generate scaffold Project name:string owner:integer
Now when I access user_id from Project like
project.owner.userid it throws exception.
Based on your responses in the comments, this is one way of implementing what you want to do:
Assuming two models in your app (Users and Questions), and two different relationships:
User asks many Questions, Question belongs_to Asker
User edits many Questions, Question belongs_to Editor
You could implement this structure in the following way:
rails generate scaffold Question asker_id:integer editor_id:integer
Specifying id:integer in your generate command is redundant, as Rails will generate that column for you automatically. It's also conventional to name your foreign keys in terms of the relationship (ie, asker_id).
Then, inside each of your models:
class Question < ActiveRecord::Base
belongs_to :asker, :class_name => User
belongs_to :editor, :class_name => User
end
class User < ActiveRecord::Base
has_many :asked_questions, :class_name => Question, :foreign_key => :asker_id
has_many :edited_questions, :class_name => Question, :foreign_key => :editor_id
end
That way, you can use them together like this:
#question.asker # => User
#question.editor # => User
#user.asked_questions # => [Question, Question, Question]
#user.edited_questions # => [Question, Question]
Hope this helps.
Adding to #Dan's answer, pass the class name as String.
DEPRECATION WARNING: Passing a class to the class_name is deprecated and will raise an ArgumentError in Rails 5.2. It eagerloads more classes than necessary and potentially creates circular dependencies. Please pass the class name as a string
class Question < ActiveRecord::Base
belongs_to :asker, :class_name => User
belongs_to :editor, :class_name => User
end
class User < ActiveRecord::Base
has_many :asked_questions, :class_name => 'Question', :foreign_key => :asker_id
has_many :edited_questions, :class_name => 'Question', :foreign_key => :editor_id
end

how do I write that join query with ActiveRecord?

anyone know how I would write that query with AR?
select *, (m.user_id=1) as member from band b join memberships m on m.band_id = g.id;
Thanks in advance.
The assumption here is that you have something that looks like this:
class Band < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
end
class User < ActiveRecord::Base
has_many :memberships
has_many :bands, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :band
end
In which case, you can perform this query easily.
user = User.find(1)
user.bands
Your question is quite vague, however, so if this isn't what you're looking for please considering expanding your question with some more details. You are also referencing an alias "g" in your question which is never defined.
I don't see why you would want to set boolean attributes via the SQL queries. You can do this with good ol' Ruby, which also lets you use has many through as jdl said.
class Book
def member?
user_id == 1
end
end