has_many through with different primary keys - ruby-on-rails-3

class Foo
set_primary_key :foo_user_id
end
class Bar
set_primary_key :bar_account_id
end
class Baz
end
Foo has many Bar through Baz. How do I write relationship with different primary keys involved?

You can specify the primary id on the association by adding :primary_key => "my_custom_id". Check the documentation.
belongs_to :Foo, :primary_key => "foo_user_id"
The rest depends on whether Foo has_many or has_one Baz.

Related

Unique Association :through

I have a many to many :through relationship between a set of classes like so:
class Company
has_many :shares
has_many :users, :through => :shares, :uniq => true
end
class User
has_many :shares
has_many :companys, :through => :shares, uniq => true
end
class Share
belongs_to :company
belongs_to :user
end
I want to ensure a unique relationship so that a user can only have one share in any one company, which is what I have tried to achieve using the "uniq" argument.
At first I thought this was working, however it seems the behaviour os the "uniq" is to filter on the SELECT of the record, not pre-INSERT so I still get duplicate records in the database, which becomes an issue if I want to start dealing with the :shares association directly, as calling user.shares will return duplicate records if they exist.
Can anyone help with an approach which would force truely uniq relationships? so that if I try adding the second relationships between a user and a company it will reject it and only keep the original?
Have you tried adding this to your Share class?
validates_uniqueness_of :user, scope: :company
Also, in your User class I think it should be:
has_many :companies, through: :shares
I hope that helps.

Rails - two foreign keys on one model both refer to same model

I'm fairly new to ActiveRecord associations. I'm sketching out an application that tracks who owes each other money among a set of users. An Expense model and a User model seem like natural choices, I'm just not sure how to define the relationship between the two. For example, I want to track the creditor ("owner") and the debtor of each expense, but that's really just two foreign keys that go back to User. In addition, each user can have multiple expenses (both as creditor and debtor) My best guess for the associations thus far is something like:
class Expense
# belongs_to or has_one here?
# Not sure about class => User syntax:
# need to alias to foreign keys that reference the same model
belongs_to :creditor, :class => User
belongs_to :debtor, :class => User
class User
# has_many expenses defines a creditor relationship (user owns expense)
# how to define debtor relationship? (belongs_to...?)
has_and_belongs_to_many :expenses
I've read the Rails guide on associations but I'm still fairly lost on foreign keys and join tables. Any input is much appreciated!
So this is definately not a has_and_belongs_to_many thats for many-to-many relationships. You just need to use a couple has_many relationships. I think it should end up looking like this:
Edit: oops I fudged that a bit that up sorry let me have another go:
class Expense
# make sure expense table has 'creditor_id' and 'debtor_id' rows
belongs_to :creditor, :class_name => "User", :foreign_key => :creditor_id
belongs_to :debtor, :class_name => "User", :foreign_key => :debtor_id
class User
has_many :debts, :class_name => "Expense", :foreign_key => :debtor_id
has_many :credits, :class_name => "Expense", :foreign_key => :creditor_id
The other answers tell you what you need to do, but it can be kind of confusing to people who are new to Rails, as I am, to piece all these things together, so here is a complete solution, including both Migrations and Models.
Also, as a side note: I prefer Loans, Lender and Borrower to Expense, Creditor and Debtor, or Debt, Creditor and Debtor. Mostly because Expense is ambiguous and Debt is too similar to Debtor. But it's not that important; just do what makes sense to you, since you will be maintaing your code.
Migrations
class CreateLoans < ActiveRecord::Migration
create_table :loans do |t|
def up
t.references :lender
t.references :borrower
end
end
end
Here you are specifying that there are two columns in this table that will be referred to as :lender and :borrower and which hold references to another table. Rails will actually create columns called 'lender_id' and 'borrower_id' for you. In our case they will each reference rows in the Users table, but we specify that in the models, not in the migrations.
Models
class Loan < ActiveRecord::Base
belongs_to :lender, class_name => 'User'
belongs_to :borrower, class_name => 'User'
end
Here you are creating a property on the Loan model named :lender, then specifying that this property is related to the User class. Rails, seeing the 'belongs_to', will look for a column in the loans table called 'lender_id', which we defined above, and use that to store the foreign key. Then you're doing the exact same thing for the borrower.
This will allow you to access your Lender and Borrower, both instances of the User model, through an instance of the Loan model, like this:
#loan.lender # Returns an instance of the User model
#loan.borrower.first_name # Returns a string, as you would expect
As a side note: the 'belongs_to' nomenclature makes decent sense in this case, but can be kind of confusing elsewhere. Just remember that it is always used on whichever thing contains the foreign key.
class User < ActiveRecord::Base
has_many :loans_as_lender, :class_name => 'Loan', :foreign_key => 'lender_id'
has_many :loans_as_borrower, :class_name => 'Loan', :foreign_key => 'borrower_id'
end
Here you are creating a property on the User model named :loans_as_lender, specifying that this property is related to the Loan model, and that the foreign key on the Loan model which relates it to this property is called 'lender_id'. Then you are doing the same thing for :loans_as_borrower.
This allows you to get all the loans where a User is the lender or borrower, like this:
#address.loans_as_lender
#address.loans_as_borrower
Doing either of these will return an array of instances of the Loan model.
If your expense migration looks like this:
create_table :expenses do |t|
t.integer :creditor_id, :null => false
t.integer :debtor_id, :null => false
# other attributes here
end
then your Expense model is sufficient. If you take a look at the documentation for belongs_to, you'll see that it will correctly infer the foreign keys into the user table:
:foreign_key
Specify the foreign key used for the association. By default this is guessed to be the name of the association with an “_id” suffix. So
a class that defines a belongs_to :person association will use
“person_id” as the default :foreign_key. Similarly, belongs_to
:favorite_person, :class_name => "Person" will use a foreign key of
“favorite_person_id”.
So you don't need to explicitly specify a foreign key here. If you use other naming conventions for the ids in your expenses model, then you need to explicitly specify them in your associations.
For your User model, you don't have a many_to_many relationship with expenses - an expense always belongs to exactly one debtor and exactly one creditor. So all you need is two has_many associations:
has_many :debts, :class_name => 'Expense', :foreign_key => :debtor_id
has_many :credits :class_name => 'Expense', :foregin_key => :creditor_id

Rails basic association

I'm trying to do a basic model association in rails.
Basically I have a List table which stores item_id and user_id.
One user can create multiple "list-items."
Is this the correct way to do it?
Thanks.
class Item < ActiveRecord::Base
has_many :users, :through => :lists
end
class User < ActiveRecord::Base
has_many :items, :through => :lists
end
class List < ActiveRecord::Base
belongs_to :user
belongs_to :item
end
Depending on what you want to reach, your solution is the right one (or not). I see the following cases:
You want to create an n:m association between items and users. So each item could be referenced by many users, and each user references many items. If this is the right context, then your solution is the right one. See the Rails Guides: Associations for more information on that.
An alternative for that situation could be to use the has_and_belongs_to_many Association. The situation is the same, but it does not make sense to talk about lists, there will be no model object for it.
If each users may have many lists, and each list may have many items, your solution would be wrong. This would be no n:m association with list as the join table in between, but two 1:n relations.
The code for the third example would look like that:
class User < ActiveRecord::Base
has_many :items, :through => :lists
has_many :lists
end
class List < ActiveRecord::Base
has_many :items
belongs_to :user
end
class Item < ActiveRecord::Base
belongs_to :list
end
In the first solution, you should add the relations for users to lists and items to list:
class Item < ActiveRecord::Base
has_many :lists
has_many :users, :through => :lists
end
class User < ActiveRecord::Base
has_many :lists
has_many :items, :through => :lists
end
If the "list" entity truly is a pure association/join, that is, it has no inherent attributes of its own, then you can simplify a bit and use has_and_belongs_to_many. Then you don't need a "List" class.
class Item < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :items
end
Rails will look for the references in a "items_users" table, so in your migration, you need to create it a la:
create_table :items_users, :id => false do |t|
t.references :users, :items
end
Many people will tell you to always use has_many :through, but others (like me) will disagree - use the right tool for job.

Table and Ruby ActiveRecord Class design for (sub)categories

I guess i have a rather simple question since I'm new to Ruby and even newer to ActiveRecords.
What I want to achieve is a class representation with ActiveRecords (and the corresponding SQL schema) that models the following problem:
There exist categories and subcategories (modeled by parent_id)
Products belong to only one category
Each product can have 0..inf features
Features simply have some data fields and are only referenced by the products
My current schema is shown below in the picture:
Is this schema suitable for ActiveRecords? How would the classes look like? I simply cant figure out how the JoinTable fits into the ActiveRecord structure.
Further, how can i model the link from parent_id->categories.id?
Any help appreciated!
cheers
To model the relationships you described you would do:
models/category.rb
class Category < ActiveRecord::Base
has_many :products
has_many :subcategories, :class_name => "Category", :foreign_key => :parent_id
end
models/product.rb
class Product < ActiveRecord::Base
belongs_to :product
has_many :features, :through => :product_features
has_many :product_features
end
models/feature.rb
class Feature < ActiveRecord::Base
has_many :product_features
has_many :products, :through => :product_features
end
models/productfeature.rb
class ProductFeature < ActiveRecord::Base
belongs_to :product
belongs_to :feature
end
Given this structure then you have the join modelled as a Many-to-Many relation. This is useful since the HABTM style of join is going away in Rails 3.1
To get the information, I often use the console rails console for testing and this would allow you do do
#category = Category.first #get the first category
#category.subcategories #returns an array of categories
The traversal of the links is via the relations that you setup in the models, with the intention that its readable, in the context of using sensible names. The self-joins, as per your question, is also covered in Rails Guides: Associations with a good example. The rest of this guide also details the other relationships.
One other thing to remember is to create your migrations so that the join table is created with the id's which are the foreign keys.
My models would look like this:
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :category
has_many :product_features
has_many :features, :through => :product_features
end
class ProductFeature < ActiveRecord::Base
belongs_to :product
belongs_to :feature
end
class Feature < ActiveRecord::Base
has_many :product_features
has_many :products, :through => :product_features
end
Rails has an association called has_and_belongs_to_many. Rails expects a table with two columns to store the join data. I usually use dual has_many to achieve the same results as it gives you flexibility to add additional information in the join table.
Sample code
product.category
product.category = category1
category.products
category.products << product1
product.features
product.features << feature1
feature.products
feature.products << product1
Here is the API for ActiveRecord::Associations::ClassMethods
There are a lot of examples in there of different relationships and how to construct them. It's worth taking the time to understand how/why you construct these associations.
For the Many-to-many join you will want to look at
has_many ..., :through => ...
has_and_belongs_to_many ...
The docs explain when and why to use each.

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