How to give foreign key a name in RoR 3? - ruby-on-rails-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

Related

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')

Implementing a has_many :through

I am thinking of creating the following models using 'has_many :through':
class Contract < AR::Base
has_many :clientlines
has_many :codelines
has_many :clients, :through => :clientlines
has_many :codes, :through => :codelines
end
class clientlines < AR::Base
belongs_to :contract
belongs_to :client
end
class Client < AR::Base
has_many :clientlines
has_many :contracts, :through => :clientlines
end
class codeline < AR::Base
belongs_to :contract
belongs_to :code
units_alloc -------**I would like to add this attribute after this intermediate
end has been created?
class Code < AR::Base
has_many :codelines
has_many :contracts, :through => :codelines
end
Do I first create the models with 'rails generate model Contract authnum:string, client_id:integer, st_date:date, end_date:date' for example.
Then fill in all of the associations before the migrations?.
Also, my understanding is that all of the join tables are created automatically by rails when using the has_many :through association. When does that happen?
Lastly, as indicated by the **, can I have this attribute in codelines, and do I create a 'rails generate migration add_units_alloc_to_codelines units_alloc:number' in order to add this attribute to the join table? I was also wondering how I declare the number to be too two decimal places?
If you have the time and inclination could you please comment on my proposed design for my database?
Thanks.
by using has_many :through you use a third model that makes the connection between the other two, so rails doesn't automatically build that model, you build it yourself and reference the foreign keys for the other two models.
don't name your models at their plural, always singular. if you can't name them at singular, you're doing it wrong
The order in which you create your models shouldn't matter too much (rails generates some migrations which you can easily modify later)
That units_alloc attribute, just add it to the model when you create it, simple as that!
for 2 decimals use something like t.decimal :amount, :precision => 6, :scale => 2 in your migration (that example gives you 6 digits and 2 decimals)
Read the Rails Guides, it will really help you get out a lot of trouble

Rails model naming convention

I have an application which has projects and users. I don't care about tracking the many-to-many relationships between users and projects but I am interested in tracking the many-to-one relationship between projects and a specific user, which is the project manager. Ideally, I would like to create a column in the projects table called projectmanager_id and link it to a user. However, rails convention dictates that I use user_id instead. While this works, I feel the semantics aren't quite right. Is there a way to have both?
One way I thought of is to create a projectmanagers table with user_id and project_id and have a has_many :projects, :through=> :projectmanagers in the user model, and has_one :user, :through => :projectmanagers in the project model. Is there a better way?
You could try this if you keep the db column as user_id:
In your project.rb file:
belongs_to :projectmanager, :foreign_key => "user_id", :class_name => "User"
And in your user.rb file still have:
has_many :projects
Or if it's that you want your db column to be projectmanager_id
In your project.rb file:
belongs_to :projectmanager, :foreign_key => "projectmanager_id", :class_name => "User"
In your user.rb file:
has_many :projects, :foreign_key => "projectmanager_id", :class => "Project"

Joining 3 tables in Ruby on Rails 3

I am trying to extract all Posts for a given User in the bellow relationships. Not sure whether I got them right, so I'll better explain. A User has Ownerships and Memberships in some Groups. A User can be either a Member or an Owner of the Group, but not both. Every Post has an id of the user and of the group. I think the problem is due to the relationships noted below. How can I get around it? One more thing. I have to also find all posts that were posted by other users in the user's groups. In other words, I have to pass through groups.
/-- Owner ---\
User -- -- Group -- Post
| \-- Member --/ |
|_______________________________|
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
has_many :ownerships, :foreign_key => "user_id", :dependent => :destroy
has_many :memberships, :foreign_key => "user_id", :dependent => :destroy
# Problem with these two? I think so.
has_many :groups, :through => :memberships, :source => :user
has_many :groups, :through => :ownerships, :source => :user
class Ownership < ActiveRecord::Base
belongs_to :users, :class_name => "User"
belongs_to :groups, :class_name => "Group"
has_many :posts, :through => :groups, :source => :posts
class Membership < ActiveRecord::Base
belongs_to :users, :class_name => "User"
belongs_to :groups, :class_name => "Group"
has_many :posts, :through => :groups, :source => :posts
class Group < ActiveRecord::Base
has_many :posts, :dependent => :destroy
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :groups
The errors is coming from the line:
_groups = user.groups
The error as following:
Could not find the source association(s) :user in model Ownership. Try 'has_many :groups, :through => :ownerships, :source => '. Is it one of :users, :groups, or :postings?
First up: you're getting that error you're seeing because you've defined the associations in the Membership and Ownership table as this:
belongs_to :users
When they should belong to only one user, i.e. singular user:
belongs_to :user
But even then you will run into problems!
I think having a Membership model and an Ownership model are what will trip you up next. I don't understand what the purpose of having an Ownership model provides, other than signifying ownership of a group, which could be done by a field on the memberships table's records called owner for instance. It's over-engineering.
The problem with the Rails code you've got there is that you're defining that you have many posts through one association and then you're telling it that you have many posts through another association. In effect, you're doing this:
def posts
# find posts for the groups that I own
end
def posts
# find posts for the groups I belong to
end
It is not mistake here that there are two identically-named methods. This is exactly what you are doing by defining two has_many associations with the same name.
So hopefully now you can see why having an Ownership and a Membership model is the path to madness.
I would really recommend that you just have a Membership model that has a boolean attribute declaring an owner for a group. This would also mean that, if you wanted to, you could have new owners for a group in a very easy fashion: just flip the boolean. No need to create another record in another table.
One Membership model to rule them all.

Multiple Associations between the same model in Rails

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"