I have a database like so:
class Store
hasMany :items
class Item
belongsTo :store
class Order
hasMany :items, :through => :order_items
class OrderItem
belongsTo :order
First off, is this the correct way to set up this database?
And finally, how do you create records properly with multiple items?
eg.
o = Order.new
Order.items = [ [0,1], [3,4] ] # do you add them as an array? [order_id, item_id] ?
Am I on the right track for this?
Thanks!
I think you should start here: association basics. Rails has great documentation unlike a lot of other frameworks. It is very easy to digest and makes a great deal of sense. Also picking up a good Intro to Rails 3 book would be well advised.
To answer your question though, there are a few problems with your example above. for starters, it is has_many and belongs_to rather than hasMany and belongsTo. In terms of your modeling, you were close although I would object to your current structure.
The reason I object is because an Order should be a record that snapshots the item as it were at that given instance. Now, if you version your items, then your schema works perfectly fine. If you do not, then you need to be sure to record the relevant information about the product at the time of the order and only have the Item reference for minimal usage.
To answer the structuring question, with the way it is designed now, here is how you'd model it:
class Store < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :store
has_many :order_items
end
class Order < ActiveRecord::Base
has_many :order_items
has_many :items, :through => :order_items
end
class OrderItem < ActiveRecord::Base
belongs_to :order
belongs_to :item
end
On to your next question:
And finally, how do you create records properly with multiple items?
#order.items << item #where item is the instance of the item you want to add
Related
"Has and belongs to many" allows having multiple associations between same objects. So, I'm trying to implement shopping carts that may have several of some products in them.
# /app/models/cart.rb
class Cart < ApplicationRecord
has_and_belongs_to_many :products
end
.
# /app/models/product.rb
class Product < ApplicationRecord
has_and_belongs_to_many :carts
end
However, when I try deleting one many-to-many association, it deletes them all:
#cart = Cart.last
#product = Product.last
3.times { #cart.products << #product }
#cart.products.delete(#product)
puts #cart.products.count
# Returns 0; should be 2
Is there a way to delete only one association?
The main problem with your approach is that you are using the product itself directly without actually caring about the association involved.
For example:
When telling rails to delete the (Product 1) it means that it will search in the association table (cart.cart_products.where(product_id: 1)) and apply delete_all to the result.
What you should do instead is:
declare the relationship between the 2 object formally has_many :cart_products
then use it to delete a single instance cart.cart_products.where(product_id: 1).first.delete
NB: the name of the join table (cart_products) may be different for your application.
You can do it like this.
cart = Cart.find(card_id)
product = cart.products.find(product_id)
cart.products.delete(product)
From documentation:
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks or extra attributes on the join model.
Which is what I am going to do in short while anyway. So I ended up using has_many :through.
# cart.rb
class Cart < ApplicationRecord
has_many :carts_products, class_name: 'CartsProduct'
has_many :products, through: :carts_products
end
.
# product.rb
class Product < ApplicationRecord
has_many :carts_products, class_name: 'CartsProduct'
has_many :carts, through: :carts_products
end
.
# carts_product.rb
class CartsProduct < ApplicationRecord
belongs_to :product
belongs_to :cart
end
This is a bit of a sticky issue. I have a Rails association but need to add functionality to handle some special cases.
class Item < ActiveRecord::Base
has_many :notes
end
class Note < ActiveRecord::Base
belongs_to :item
end
The issue is that you could have something like this in db (identical items that might have a difference in sizing or quantity but are part of the desc and can't really be broken out due to accounting software):
items
id desc
1 a glass something
..
10 a bottle of something
..
20 a case of bottles of something
notes
id note item_id
3 "a note about something" 1
What I want to do is create a linke so that items 1,10, and 20, when they load notes, will all load notes with an id of 1
For example:
item1=Item.find(1)
item1.notes[0].id # 3
item10=Item.find(10)
item10.notes[0].id # 3
item20=Item.find(20)
item20.notes[0].id # 3
I feel like there should be a really basic way of doing this and was looking for suggestions. This is hacky but might work would be to write a dash separated list of other_ids into the notes table so that a notes.other_ids="-10--20-"
class Item < ActiveRecord::Base
has_many :notes
def sym_items
Note.where('other_ids like ?',"%-#{self.id}-%")
end
end
We really would only have to deal with this in a single scenario so a hacky sol'n woudl be ok but obviously would like better. Possibly do a has_many :through. I'm not sure about the latter - perhaps too much added complexity. Any help or advice would be appreciated.
thx
So I'm not sure I fully understand the situation. It seems like you might want something like this:
class Item < ActiveRecord::Base
has_many :notes
has_many :other_item_notes
has_many :other_notes, class_name: "Note", through: :other_item_notes, source: :note
has_many :other_items, class_name: "Item", through: :notes, source: :other_items
end
class Note < ActiveRecord::Base
belongs_to :item
has_many :other_item_notes
has_many :other_items, class_name: "Item", through: :other_item_notes, source: :item
end
class OtherItemNote < ActiveRecord::Base
belongs_to :item
belongs_to :note
end
The trick here is that you have a many-to-many association between items and notes (I assume you know how/why those other associations are supposed to work). Then we use the associations of those other items that we can now access to access their associated items. There may be complications with N+1 queries and such, but some of that can be dealt with by using inverse_of on your associations.
If you do it this way, you are querying against indexed id columns in your database searches, rather than a string comparison query like in your example above.
I am working in Ruby on Rails 3. And trying to map out three models which mimic the data of a Company its employees and their respective departments.
In arrived at the following solution:
class Company < ActiveRecord::Base
has_many :departments
has_many :employees, through => :departments
end
class Department < ActiveRecord::Base
belongs_to :company
has_many :employees
has_one :department_description
end
class DepartmentDescription < ActiveRecord::Base
belongs_to :department
end
class Employee < ActiveRecord::Base
belongs_to :department
end
Is this the 'correct' way to associate these models?
I think your last response may explain why you are struggling to find a correct way to associate these models.
It seems that you see your Department merely as a join_table, and that may be due to the fact that you don't fully understand the has_many => :through construction and that it actually allows your Department to be a proper model with many attributes and methods in it, hence also a 'description' attribute.
To create a separate DepartmentDescription model is actually a waste of resource. Chad Fowler has a few good examples for :has_many => through and nested resources in his Rails Recipes... so check that out.
I am currently trying to set up a model structure that seems quite simple, but I haven't quite got it down.
I have a model payment that can belong to either a customer or a supplier (which can both have many payments).
My question is simply whether I need to manually create an interface table to allow this, or if declaring the polymorphic associations will do this for me?
e.g. I have:
class Payment < ActiveRecord::Base
belongs_to :payment_originator, :polymorphic => true
end
class Customer < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
class Supplier < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
Is this enough, or do I also need to use a generator to manually create the payment_originator model?
Thanks!
As far as the models go, this is good enough. You just need to migrate a :payment_originator_type and :payment_originator_id to the payments table. The associations you defined above will automatically fill these in for you.
I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. If you need to work with the relationship model as its own
entity, use has_many :through. Use has_and_belongs_to_many when
working with legacy schemas or when you never work directly with the
relationship itself.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end