Can you please help how to add items to cart. Rails Agile explains how to add products to carts through line_items.
Let's say my websites offers tourist packages, limo services, apartment rooms,some other deals.
Customer adds limo car, tour package, offers to cart and pays for it.
Are they different models model Package, model Limo, model Apartment and other models. if so when I add to cart through line_items(cart_id, product_id) I can't figure out how to add other models. Or Should I link all models to product model?
Thanks in advance
You probably don't need a different model for every product type. You can break down the products into different categories. So you would need just one extra model--a Category model. Each product would belong_to a category (and category would have many products). In the database each product would have a category_id.
Like this:
class Product < ActiveRecord::Base
belongs_to :category
....
class Category < ActiveRecord::Base
has_many :products
...
You mention that models are too different (in terms of attributes). Therefore single-table-inheritance may not be what you want. In this case use polymorphic association to achieve this.
class LineItem < ActiveRecord::Base
belongs_to :purchasable, :polymorphic => true
end
class Tour < ActiveRecord::Base
has_many :line_items, :as => :purchasable
end
class LimoService < ActiveRecord::Base
has_many :line_items, :as => :purchasable
end
Related
So lets say that I have a Product and Category model. There is a one to many relationship between them ( category has many products, product belongs to category ).
I create a collection of categories based on certain criterias using a named scope. Lets call this collection A.
Then I would like to create a collection of products which belongs to either category in collection A.
How can I do this?
My guess would be to use the ids of collection A ( A.ids ) and use map or lambda to check if the category_id of a product is included in the array of ids I built from collection A. I am not sure how to do this exactly, and if is the most efficient way to achieve the end result.
I'd be tempted to employ a has_many :through relationship, although it's really just a "join" model that's required:
I originally thought of a has_and_belongs_to_many relationship, but that wouldn't be able to discern the different categories.
--
#app/models/collection.rb
class Collection < ActiveRecord::Base
#columns id | name | category_id | product_id
belongs_to :category
belongs_to :product
end
Having a Collection model like this will give you the ability to call the following:
#collection = Collection.find_by name: "New products"
#collection.categories #-> all categories
#collection.products #-> all products
This will give you the ability to relate the products and categories as follows:
#app/models/product.rb
class Product < ActiveRecord::Base
has_many :categories
has_many :collections
has_many :collection_categories, through: :collections
end
#app/models/category.rb
class Category < ActiveRecord::Base
has_many :products
has_many :collections
has_many :collection_products, through: :collections
end
Changing your Product and Category models is optional. It would be for if you wanted the following:
#category = Category.find "1"
#category.collection_products # -> pulled from collections (IE not the `has_many/belongs_to` relationship you have now.
#product = Product.find "2"
#product.collection_categories # -> all categories through the collections table
I would suggest to arrange your models like this:
class Category
has_many :products
has_many :category_collections
end
class CategotyCollection
belongs_to :category
belongs_to :collection
end
class Collection
has_many :category_collections
has_many :categories, through: :category_collections
has_many :products, through: :categories
end
In the end of the day, you can select products in given collection as simple:
collection = CategoryCollection.first
collection.categories # => all categories in this collection
collection.products # => all products in this collection
Join on association, filter by accociated objects (joins/merge combo)
Product.joins(:category).merge(Category.whatever_scope(you, have))
Subquery on association
Product.where(category: Category.whatever_scope(you, have))
The simplest way is actually to take ids and use them to select products.
category_ids = Category.some_scope.pluck(:id)
products_collection = Product.where(category_id: category_ids)
Unless it is not always - that you need to select products from many categories - then maybe you should do that way with collection model, that suggested #dimakura
When you say that you "would like to create a collection of products which belongs to either category in collection A" that automatically makes it a many to many relationship. In reality one product can belong to many categories, and a category can have many products. Therefore, I would create a join table to solve your issue.
I Have a shoppinglist model which has many recipes, which again has many ingredients.
class Shoppinglist < ActiveRecord::Base
has_many :shoppinglistrecipezations
has_many :recipes, :through => :shoppinglistrecipezations
end
class Recipe < ActiveRecord::Base
has_many :shoppinglistrecipezations
has_many :shoppinglists, :through => :shoppinglistrecipezations
has_many :ingredients, :through => :ingredienzations
has_many :ingredienzations
end
class Ingredient < ActiveRecord::Base
has_many :recipes, :through => :ingredienzations
has_many :ingredienzations
end
When I add several recipes to the shopping model (some of them have the same ingredients) I want to print a list of all the ingrediens in the shoppinglist and the right amount of ingredients. Shoppinglistingredienzations has an integer "Persons" to tell how many persons served should be calculated for and Recipe has a persons variable to show how many persons the recipe is for. Ingredienzations contains the amount, measurement type (grams, teaspoons etc.) and recipe_id.
You could do this:
Shoppinglist.where(id: list_id).joins({recipes: : ingredients}).group('ingredients.name').sum('ingredients.amount')
assuming your Ingredient model has the attributes amount and name. It becomes much more tricky if you have units on you amounts. But this query can be rewritten to make that work as well.
So I have two models: Product and Sale. they are related via a has_many :trough association and both have the same attributes and attribute names (sale has one more tough):
THE CODE SNIPPETS:
# Product Model
class Product < ActiveRecord::Base
attr_accessible :qty, :color, :name, :price, :size, :type, :code
has_many :orders
has_many :sales, :through => :orders
end
# Sale Model
class Sale < ActiveRecord::Base
attr_accessible :qty, :color, :name, :salesman_name, :price, :size, :type, :code
has_many :orders
has_many :productos, :through => :orders
end
What I want to do is to decrement the Products>Quantity attribute of a specific record (product) every time a new Sale is created with it's name on it... The substraction must be equal to the value of "qty" set in the new sale creation in relation with the created product...
The app is a very simple inventory system where I track stock items and sales, let's give a pratical example: Imagine I have a product called "Socks" in the Products Database and I have "30" as the qty attribute of that product, Then someone sells two socks (creates a new sale with 2 socks in the app): in that case I want the value of "Socks" to be updated to 28 in the Products database automatically.
I've been reading and I think this may can be accomplished with an after_create callback in the sale model with an ActiveRecord transaction, but I'm not sure how to implement this, Can someone help me?
By the way, I was thinking about using something similar to this thing here:
after_create :decrement_stock
def decrement_stock
Sale.transaction do
Product.transaction do
#substraction = product.sale(params[:qty])
product.update_attributes!(:qty => #qty - #substraction)
end
end
end
But I'm pretty sure it isn't going to work, Please Point me in the right directions... I've been struggling with this one a little.
Thanks and have a nice day.
I think you should rebuild this associations and models
What I'd do is to have this models (a little bit different than yours):
Salesman
class Salesman < ActiveRecord::Base
attr_accessible :last_name, :name
has_many :sales
end
Product
class Product < ActiveRecord::Base
attr_accessible :name, :qty
has_many :product_sales
end
Sale
class Sale < ActiveRecord::Base
belongs_to :salesman
has_many :product_sales
end
And here's the trick
Model ProductSale
class ProductSale < ActiveRecord::Base
belongs_to :sale
belongs_to :product
attr_accessible :qty
after_create :decrement_stock
def decrement_stock
self.product.update_attribute("qty", (product.qty - self.qty))
end
end
There you have Sale that belongs_to salesman.
Sale has_many product_sales
and product_sales belongs_to product and sales that way in one sale you have multiple product
Then when a ProductSale is saved there's the callback after_create :decrement_stock
And you can update the attribute for that specific product.
Remember that you'll have a quantity for each product and not for the whole sale.
Here's a little example I set up for you:
https://github.com/Mr-Nizzle/inventory
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.
I'm developing a web application about gift lists in Rails 3. I'd would like to get all the purchases for a certain list. These are the models and their relationships:
class Gift < ActiveRecord::Base
belongs_to :list
has_many :purchases
class List < ActiveRecord::Base
has_many :gifts
class Purchase < ActiveRecord::Base
belongs_to :gift
So, basically, I've tried the following code (I don't think it's the best way to do this at all), and eventhough the results are correct, I've realized that I get a Gift object instead of a Purchase:
#purchases = List.find(params[:id]).gifts.joins(:purchases).select("purchases.*")
Any ideas?
#purchases = List.find(params[:id]).
gifts.joins(:purchases).
map(&:purchases).flatten
or just refactor your models:
class List < ActiveRecord::Base
has_many :gifts
has_many :purchases, :through => :gifts
so
List.find(params[:id]).purchases
If you want all the purchases maybe...
#purchases= List.find(params[:id]).gifts.collect{|g| g.purchases}
Although you may want to split it up and check that List.find returns a valid List.