Handling relationship with ActiveRecord - ruby-on-rails-3

I have two models to make a relationship between them, where I need to access stores of a radar and the radars of a store. A radar could monitoring zero or many stores. A store could belong to zero, one or many radars.
I would like to have something like this:
store = Store.first
store.radars #all radars of the store location
And the opposite too:
radar = Radar.first
radar.stores #all stores of the radar location
My classes:
class Store < ActiveRecord::Base
attr_accessible :title, :description, :user, :store_group, :city,
:neighborhood, :sublocality, :post_code, :route,
:street_number, :latitude, :longitude
end
class Radar < ActiveRecord::Base
attr_accessible :name, :radius, :latitude, :longitude, :user
end
How can I create a migration to handle this?

What you are looking for is a has_and_belongs_to_many association between radars and stores. The question you need to ask your self is will there ever be any attributes on the the joining between the two models? If so you might considering using an explicit join model, that will hold those attributes. In that case you would be looking at a has_many :through association.
see http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association for information on the HABTM association.
your migration for a HABTM would be something like this.
class CreateRadarStores < ActiveRecord::Migration
create_table :radars_stores, :id => false do |t|
t.belongs_to :radar
t.belongs_to :store
end
end
The order of the table name is important, since by default rails creates it in alphabetical order of the models.
Your models would need to be updated to include the HABTM
class Store < ActiveRecord::Base
has_and_belongs_to_many :radars
....
end
class Radar < ActiveRecord::Base
has_and_belongs_to_many :stores
....
end
or if using a has many :through look here http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Building that join model would be up to you depending upon attributes reuqired.

Related

Counter cache doesn't get updated - not sure why

I have a stores application on github. I'm trying to implement counter_cache for two models, 1. Divisions model and 2. Products model. For some reason I'm not sure of the counter cache(divisions_count) doesn't get incremented automatically for the Company model whenever I create a new Division and similarly the products_count doesn't get incremented for the Divisions model when I add a new product to the division.
I'm on rails 3.2.11 and on ruby 1.9.3-p327
My application is only at POC level.
PFB the model structure wrt Company, Division and Product:-
company.rb
class Company < ActiveRecord::Base
attr_accessible :contact_no, :email_id, :fax_no, :name, :website, :divisions_count
has_many :divisions #just added divisions_count to attr_accessible not sure if it helps
has_many :products, :through => :divisions
end
division.rb
class Division < ActiveRecord::Base
attr_accessible :company_id, :name, :products_count
#just added products_count to attr_accessible not sure if it helps
belongs_to :companies, :counter_cache => true
has_many :products
end
product.rb
class Product < ActiveRecord::Base
attr_accessible :division_id, :model, :name, :price
belongs_to :divisions, :counter_cache => true
end
In case you want to refer to the migrations that I've created for the counter cache implementation , you may find them here.
I believe the problem is that you’ve set up belongs_to incorrectly by using the plural name. Switching to singular solves the problem, i.e.
# pseudo diff:
- belongs_to :companies, :counter_cache => true
+ belongs_to :company, :counter_cache => true
- belongs_to :divisions, :counter_cache => true
+ belongs_to :division, :counter_cache => true
When editing models/associations I found it helpful to think of an actual instance. So, it makes sense that the “Windows”-division belongs to “Microsoft”-company, but it makes no sense that it belongs to “Microsoft”-companies. Or just remember belongs_to is always singular and has_many is always plural.
If you need your divisions to belong to multiple companies, you need to use a different association called “has and belongs to many” or HABTM for short (see [1]).
[1] http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

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.

referencing attributes in models with belongs_to relationships through a nested namespace

Ok, so I thought I understood how the relationship specifications work in rails but I've been struggling with this for a day now.
Some context, I have two models Cars & Model Names (e.g. Impala, Charger, etc), where Cars are instances of Model Names, and Model Names really is nothing more than a lookup table of Model Names and some other model level attributes. The Model Name controller is nested within the admin namespace as only admins can CRUD Model Names. End users can add instances of cars to the Cars model.
So, in routes.rb I have:
resources :cars
namespace :admin do resources :model_names end
The Model's are defined as:
class Admin::ModelName < ActiveRecord::Base
end
class Car < ActiveRecord::Base
belongs_to :admin_model_name
end
The Migrations are:
class CreateCars < ActiveRecord::Migration
def self.up
create_table :cars do |t|
t.string :chassis_number
t.string :description
t.references :admin_model_name
t.timestamps
end
end
class CreateAdminModelNames < ActiveRecord::Migration
def self.up
create_table :admin_model_names do |t|
t.string :model
t.integer :sort_index
#...additional attributes removed
t.timestamps
end
The admin CRUD of ModelName all work great. The problem is in the Car views. I think I should be referencing a particular cars model name like such:
<%= #car.admin_model_names.Model =>
But I get the error:
undefined method `admin_model_names' for #<Car:0x000001040e2478>
I've tried the attr_accessible on the ModelNames model but to no avail. The underlying data is referenced correctly. I have also have HABTMT relationship between Cars & Users and that all worked fine referencing each others attributes from the different entities views. But haven't been able to get this to work. Is it due to the nested resource & admin control inheritance?
The only referencing method I found that works is:
<%= Admin::ModelName.find(#car.admin_model_name_id).model %>
But this really seems to be too much code (and expense of a find) to get to that attribute. Is there a Rails way?
Thanks in advance.
Scott
Have you tried:
class Car < ActiveRecord::Base
belongs_to :admin_model_name, :class_name => "Admin::ModelName"
end
as stated in
http://guides.rubyonrails.org/association_basics.html
section 3.4?
you may also need to set the :foreign_key => "admin_model_name_id" attribute to specify the referencing model.
Hope it helps.
Did you try
class Car < ActiveRecord::Base
belongs_to :admin_model_name, :class_name => 'Admin::ModelName'
end
and if necessary add :foreign_key => '' and add this column to your cars table.