Database creation/saving with a many-to-many relationship - ruby-on-rails

I have two tables, item and category, and a joined table named Items_Categories. The one restriction I have is that an item HAS to have a category.
In my Item Model, I have this code:
class Item < ActiveRecord::Base
has_many :items_categories
has_many :categories, through: :items_categories
has_many :item_orders
has_many :orders, through: :item_orders
even though categories is not a column in my Items database.
Here's my ItemsCategory model:
class ItemsCategory < ActiveRecord::Base
belongs_to :item
belongs_to :category
end
Here's my Category Model:
class Category < ActiveRecord::Base
has_many :items_categories
has_many :items, through: :items_categories
end
In my test, I have this issue:
test 'it is valid' do
category = Category.create(name: "hot beverages")
item = category.items.create(title: "fjdkasf", description: "fjakdsf", price: 20901290)
assert item.valid?
end
Basically, this test fails because I cannot create an item until it item.categories is not nill. But the validation doesn't pass until I create the item in item_categories. So... what do I do? Basically I can't pass the validation when I create the item because the item doesn't yet have a category. But I want to make sure that every time I create an item, it has an associated item. So what do I do?

Validation with has_many :through relations can be tricky sometimes, there's a hint in the has_many :through documentation that you should define the inverse_of attribute on your join model:
If you are going to modify the association (rather than just read from
it), then it is a good idea to set the :inverse_of option on the
source association on the join model. This allows associated records
to be built which will automatically create the appropriate join model
records when they are saved. (See the 'Association Join Models'
section above.)
class ItemsCategory < ActiveRecord::Base
belongs_to :item, inverse_of: :items_categories
belongs_to :category, inverse_of: :items_categories
end
To be honest, I'm not 100% sure this will fix it, but give it a try and report back.

Related

User model with relationship table

I have two models User and UserRelation. The case is that User has several related users with himself(recommended by him), but he has only one person related_to(person who recommended him).
I would like to return from User object collection of recommended users and user who recommended him. I have written association for returning users collection and it works but I have no idea how should I write has_one association.
I get this error:
ActiveRecord::HasOneThroughCantAssociateThroughCollection: Cannot have a has_one :through association 'User#relation' where the :through association 'User#user_relations' is a collection. Specify a has_one or belongs_to association in the :through option instead
User model:
class User < ActiveRecord::Base
has_many :user_relations
has_many :related_users, through: :user_relations, source: :related_user
has_one :relation, through: :user_relations, source: :user
end
UserRelation model:
class UserRelation < ActiveRecord::Base
belongs_to :user
belongs_to :related_user, class_name: 'User'
end
UserRelation columns:
user_id
related_user_id
My choice would be to put a foreign key in your User table for the possible related_to field.
If the requirement is that it can only be one (or none) then why not?
You still keep the other "user_relations" for all other types. All the time in rails, we map to the same entity in different ways. It's not uncommon at all

Correct use of has_many_through

I have two models: Orders and Items. This is a many to many relationship, but I'm unsure if HMT is correct
Order model:
-user_id
-transaction_cost
-delivery_time
Item model:
-price
-name
Orders should be able to get all items in the order
Items do not need to be able to get all orders
The convention on this is to use the names of both models. A good name might be ItemOrders. Has many through is almost certainly a correct choice here.
class Order < ActiveRecord::Base
has_many :item_orders, dependent: :destroy
has_many :items, through: :item_orders
end
class Item < ActiveRecord::Base
has_many :item_orders, dependent: :destroy
has_many :orders, through: :item_orders
end
class ItemOrder < ActiveRecord::Base
belongs_to :item
belongs_to :order
end
Now you just have another ActiveRecord model, and you can add to it as you'd like. It will also be helpful for debugging. You can even use a model/scaffold generator to generate these:
rails g model item_order order:references item:references
This way you get the migrations correct right away. Nothing needs to be altered on your other models except for the above code.

How make categories relationships with several model?

I have next models: Articles, Announcements, Catalogs and Media.
For item of every model I need to create a subcategory and a category. I will plan to create a relationship table with two columns: parend_id and child_id, and a column for every model with category_id.
How many relationship models I should create?
One for all?
Or one relationship model for every model?
No need to create a relationship model instead just use belongs_to relationship
because you said following i guess belongs_to relations should do it
For item of every model I need create subcategory and category.
So just add subcategory_id and category_id in your tables: Articles, Announcements, Catalogs and Media and establish has_many belongs_to relationship
EDIT But if you insist for relationship model then I would suggest to use one relationship model for every model.
Last EDIT: I really think you should use belongs_to, has_many relation
Class Article < ActiveRecord::Base
belongs_to :category
belongs_to :subcategory
end
class Category < ActiveRecord::Base
has_many :articles
# the same relation(has_many) will be for Announcements, Catalogs and Media
has_many :subcategories
end
class Subcategory < ActiveRecord::Base
has_many :articles
belongs_to :category
end
In this way you can get all articles using category.subcategories.articles
If the category has only one Subcategory then change the relation between them to has_one and your syntax will become category.subcategory.articles
I would personally use a has_many :through relationship with a polymorphic association in a single join table:
#app/models/article.rb
Class Article < ActiveRecord::Base
has_many :categorizable_categories
has_many :categories, through: :categorizable_categories
end
class Article < ActiveRecord::Base
has_many :tags, as: :taggable,dependent: :destroy
has_many :categories, through: :tags
end
class Category < ActiveRecord::Base
has_many :tags, dependent: :destroy
has_many :articles, through: :tags, source: :taggable, source_type: 'Article'
end
class Tag < ActiveRecord::Base
belongs_to :taggable, polymorphic: true
belongs_to :category
end
This will allow you to call the categories for each model with the likes of #article.categories
To achieve the parent & child categories, I'd recommend using something like the Ancestry gem - you'd set up an ancestry column in your join table - basically allowing you to create relations between a model's categories directly

Creating a many-to-many relation in Rails

So I'm Rails n00b and I want to create a "favorites" relationship such that a User can have many favorite Item. I'm not entirely sure how to do this, this is how I'm going to try but I'm not sure if this is a good practice at all:
class User < ActiveRecord::Base
has_many :favorites
//other code
end
class Favorite < ActiveRecord::Base
belong_to :user
has_one :item
end
class Item < ActiveRecord::Base
belongs_to :item
end
Is this a good way to do it? Should I be using has_and_belongs_to_many ?
I'm specially concerned in the following scenario: Say a user has 100 favorite items.
When I do a User.find(id) will I also be retrieving the 100 favorites and the 100 Items?
In case it's important: ruby version 1.9.3, rails version 3.2.11
Can you try has_many => :through?
class User < ActiveRecord::Base
has_many :favorites
has_many :items, :through => :favorites
//other code
end
In your case has_many :through is definitely the way to go. I would recommend reading: http://guides.rubyonrails.org/association_basics.html
Of particular interest with regard to your question:
2.8 Choosing Between has_many :through and has_and_belongs_to_many
Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly:
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model:
class Assembly < ActiveRecord::Base
has_many :manifests
has_many :parts, :through => :manifests
end
class Manifest < ActiveRecord::Base
belongs_to :assembly
belongs_to :part
end
class Part < ActiveRecord::Base
has_many :manifests
has_many :assemblies, :through => :manifests
end
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.
It is better than using has_and_belongs_to_many.
When I do a User.find(id) will I also be retrieving the 100 favorites
and the 100 Items?
No. You'll just get the user object.
Update:
Calling User.include(:favourites, :items).find(id) will get you joined tables in case you want to make many calls to items table from user object.

One to many relationships in Rails

I'm having a hard time setting up the basic structure of my databases.
I have products (about 50). Each products is related to one or more place(s).
The basic schema would be this (no relation yet)
Products
id:integer
name:string
Places
id:integer
name:string
content:string
I first tought I could connect places and products by adding place_id to products and has_many belong_to in the controllers, but since a product can have more than one place, I don't know how.
If a Product has_many Places and a Place has_many Products your association needs to be many-to-many.
There are two ways to do this in Rails, the most recommended is a join model. You explicitly label the relationship between Products and Places. You could call it ProductLocation or Shop or similar, it would look like this:
product_locations:
product_id
place_id
class ProductLocation < ActiveRecord::Base
belongs_to :product
belongs_to :place
end
class Place < ActiveRecord::Base
has_many :product_locations
has_many :products, :through => :product_locations
end
class Product < ActiveRecord::Base
has_many :product_locations
has_many :places, :through => :product_locations
end
See http://guides.rubyonrails.org/association_basics.html#choosing-between-has_many-through-and-has_and_belongs_to_many
Just use through connection
ProductsPlaces (new table)
product_id
place_id
And in models
class Product < ActiveRecord::Base
has_many :places, :through => :products_places
end
class Place < ActiveRecord::Base
has_many :products, :through => :products_places
end
Also there is has_and_belongs_to_many which do in fact the same (with ProductsPlaces table)
class Product < ActiveRecord::Base
has_and_belongs_to_many :places
end
class Place < ActiveRecord::Base
has_and_belongs_to_many :products
end
But better use through, because has_and_belongs_to_many will be deprecated.
It sounds like you want to add product_id to places.
Product has_many Places
Place belongs_to Product

Resources