Rails: has_many :through without additional table - ruby-on-rails-3

I have a model named Container. This model just has a list of associated Links. So I created tables containers, links and containers_links. Now I want to connect my two models. So I did
class Container < ActiveRecord::Base
has_many :links, :through => :containers_links
end
class Links < ActiveRecord::Base
has_many :containers, :through => :containers_links
end
But I have an error because I don't have reflection with containers_links in my model. I can add something like
has_many :containers_links
But I don't have a model ContainersLinks (and I don't want to create one). What should I do?

You can use has_and_belongs_to_many relationship
class Container < ActiveRecord::Base
has_and_belongs_to_many :links
end
class Links < ActiveRecord::Base
has_and_belongs_to_many :containers
end
But you still need to create join table.

Related

Modelling a team manager through a membership table

I have the following Rails models connecting teams and users:
class User < ActiveRecord::Base
has_many :memberships
has_many :teams, through: :memberships
end
class Team < ActiveRecord::Base
has_many :memberships
has_many :users
end
class Membership < ActiveRecord::Base
belongs_to :team
belongs_to :user
end
I want to implement a team manager via the existing memberships table.
Would it be a better idea to have a foreign key in the teams table like this:
class Team < ActiveRecord::Base
belongs_to :manager_membership, class_name: 'Membership'
has_one :manager, through: :manager_membership, source: :user
# ...
end
Or would it be better to have an extra column (with unique index) on memberships like this:
class Team < ActiveRecord::Base
has_one :manager_membership, -> where { manager: true }, class_name: 'Membership'
has_one :manager, through: :manager_membership, source: :user
# ...
end
I wonder if having foreign keys going both ways from team to a membership and back is weird? Or maybe it doesn't make much difference either way?
Based on the comments, you should be able to use a single table inheritance for both your users and managers. That would mean you get to use the same membership relationship on both models.
class User < ActiveRecord::Base
has_many :memberships
has_many :teams, through: :memberships
end
class Manager < User
# your code
end
You can now use Manager wherever needed, and add any validations directly on that model. Remember to add a type column to your User table if you decide to go this route.
EDIT
Looking at this again, you will need to have a separate table to keep the relationship between your Manager's team and the actual team they manage clear. Yourteam` should be something like:
class Team < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
has_many :teams_managers
has_many :managers, through: :teams_managers
end
class Manager < User
has_many :teams_managers
# note here we're aliasing for clarity and specifying a source
has_many :managed_teams, through: :teams_managers, source: :team
end
# this is your association model for managers <-> teams
class TeamsManager < ActiveRecord::Base
belongs_to :manager
belongs_to :team
end
# so now you could call something like this
manager = Manager.find 1
manager.managed_teams =>[...]
manager.teams =>[...]
You'd have to make the corresponding migrations to instantiate your TeamsManagers table as well.

Rails join table A to table C where A has many B and B has many C

So in rails, I have
class Organization < ActiveRecord::Base
has_many :boxes
class Box < ActiveRecord::Base
has_many :items
belongs_to :organization
class Item < ActiveRecord::Base
belongs_to :box
Without adding the association on Organization, how can I query for all items that belong to an organization? I don't want to add orgaization_id to Item.
Take a look at has_many :through Association.
In your case, you can add the following to your Organization model
has_many :items, through: :boxes
Then you'll be able to write something like this: Organization.find_by(id: ..., name: ....).items

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 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.