I have the following models:
product.rb
class Product
has_many :purchases
has_many :line_items
has_many :orders, :through => :order_products
lineitem.rb
class LineItem
belongs_to :product
belongs_to :cart
belongs_to: order
order.rb
class Order
belongs_to :user
belongs_to :product
has_many :purchases
has_many :line_items, :dependent => :destroy
has_many :orders, :through => :order_products
purchase.rb
class Purchase
belongs_to :order
belongs_to :product
updated:
order_product.rb
class OrderProduct < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
order_controller.rb
if #order.save
if #order.purchase
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
The above are my association for the models. However I face problems in displaying the products from a user. When the items are successfully purchased, the line_items are destroyed.
Does anyone know how do I store all the purchased items into purchase or any other better methods for me to display the products bought by a user?
I initially tried to retrieve line_items and it works. However, after the line_items are destroyed, I am not able to retrieve the relevant products.
Appreciate any help here.
You could try making an order_history for each user -
user.rb
def order_history
#set this up according to how your models are related, etc
#with the idea being to call #user.order_history and getting an array of previous orders
end
and then in order_controller.rb
if #order.save
if #order.purchase
#adapt this pseudocode to suit your needs and
#according to how you've defined `#user.order_history
current_user.order_history = current_user.order_history + #order.line_items
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
Essentially, push the line_items elsewhere so you have a record of them before you destroy the cart.
You need to look at http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association association.
User
has_many :orders
has_many :products, through: :orders
Order
has_many :line_items
has_many :products, through: :line_items
Related
I currently have a ProductSale model that has_many sales.
Also a sale belongs to an invoice.
My goal is to access an invoice through a ProductSale's association to sales. (product_sale.invoice)
Current ProductSale model below:
class ProductSale < ApplicationRecord
has_many :sales
has_one :invoice, through: :sales
end
However my current error is saying that this can't be done because the :through association is a collection, which i understand. Is there a way that this can be possible?
class Sale < ApplicationRecord
belongs_to :invoice
end
class Invoice < ApplicationRecord
has_many :sales, inverse_of: :invoice, dependent: :destroy
end
All the sales on the ProductSale object have the same invoice. You know that you can just use the invoice of the first sale, but associations won't know that all the sales have the same invoice so you can use any, for instance the first one.
To have a invoices method to get each of the invoices you could do this:
class ProductSale < ApplicationRecord
has_many :sales
has_many :invoices, through: :sales
end
However, if you want to use your business logic that all the invoices are assumed to be the same, you will have to write a method to implement that business logic.
class ProductSale < ApplicationRecord
has_many :sales
def invoice
sales.first&.invoice
end
end
If all the sales on the invoice will be sales in the ProductSale object, then maybe you should refactor as below.
class ProductSale < ApplicationRecord
has_one :invoice
delegate :sales, :to => :invoice, :prefix => false, :allow_nil => true
end
Then you can both call the invoice method to get the invoice and also call the sales method to get all the sales on the invoice for the ProductSale object.
How about:
class ProductSale < ApplicationRecord
has_many :sales
has_one :sale
has_one :invoice, through: :sale
end
I have a model for Sellers and they own games and unlimited_games which can be bought by other players. I want to pull all the purchases for a specific seller. My model when I only had games was:
class Seller
has_many :purchases, through: :games, dependent: :destroy
end
class Purchase
belongs_to :game
end
class Game
has_many :purchases
belongs_to :coach
end
Updated Models
def UnlimitedGame
has_many :purchases
belongs_to :coach
end
def Purchase
belongs_to :game
belongs_to :unlimited_game
end
And I could do a #seller.purchases to get all purchases for that seller. Now I want to extend the same feature to include all unlimited_games
So that doing #seller.purchases includes both unlimited_games and games. I tried doing:
has_many :purchases, through: :games, dependent: :destroy, class_name: :purchases
has_many :purchases, through: :unlimited_games, dependent: :destroy, class_name: :purchases
Supussing unlimited_games is not a model on itself and instead is a scope on the games model, then you could do something like this in the Seller model:
def unlimited_game_purchases
purchases.merge(Game.unlimited)
end
which uses merge to merge the condition of both queries
By the way, there is of course a way to create another association with the same model, if as you say the seller also has a has_many :unlimited_games already working, then your association will look like:
has_many :unlimited_game_purchases, trough: :unlimited_games, source: :game
You can choose the way you understand better
I'm fiddling with rails and trying to build a small app for practicing purposes:
I want a client to order one or more products
I have a client table, a product table and last an order table which has a client_id and product_id
Now, I'm not quite sure how to set up a good relation between these table as in: client goes to product page, chooses product and saves the order.
Any help about which model should have which relation is greatly appreciated.
You can set up associations like this
Class Client < ActiveRecord::Base
has_many :orders
has_many :products,through: :orders
end
Class Product < ActiveRecord::Base
has_many :orders
has_many :clients,through: :orders
end
Class Order < ActiveRecord::Base
belongs_to :client
belongs_to :product
end
For more details,see these Guides
The association should look something like this
Class Client < ActiveRecord::Base
has_many :orders
has_many :products,through: :orders
end
Class Product < ActiveRecord::Base
has_many :orders
has_many :clients,through: :orders
end
Class Order < ActiveRecord::Base
belongs_to :client
belongs_to :product
end
You could do it like this:
#app/models/product.rb
has_many :orders
has_many :clients, through: :orders
#app/models/order.rb
belongs_to :client
has_many :order_products
has_many :products, through: :order_products
#app/models/client.rb
has_many :orders
has_many :products, through: :orders
The way to handle the creation of a new order is to set a uuid for it, and then create another join model which will handle order products. You could do this with the order model, but I felt it best to describe the basic way, as it will give you something to work from
--
uuid
We like to set uuid columns for sensitive data like orders:
#migration
add_column :orders, :uuid, :varchar, after: :id
#app/models/order.rb
before_create :set_uuid
private
def set_uuid
unless self.uuid
loop do
token = SecureRandom.hex(5)
break token unless self.class.exists?(uuid: token)
end
end
end
This means every order will have as many products as you want, accessible like this:
#user.orders.first.products #-> returns order products
--
Edit - just saw #Pavan's answer. Sorry if it's the same - hope it helps!
You have two approaches
1) set has_and_belongs_to_many between client and product model.
class Client < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :clients
end
2) set through relation between the client and production through using keyword through
class Client < ActiveRecord::Base
has_many :orders
ha_smany :products, through: orders
end
class Product < ActiveRecord::Base
has_many :orders
ha_smany :clients, through: orders
end
I am suggesting you to go with second option since you have an intermediate model.
**Updated
I have the following models:
product.rb
class Product
belongs_to :user
has_many :line_items
has_many :orders, :through => :order_products
has_many :order_products
lineitem.rb
class LineItem
belongs_to :product
belongs_to :cart
belongs_to: order
order.rb
class Order
belongs_to :user
belongs_to :product
has_many :purchases
has_many :line_items, :dependent => :destroy
has_many :orders, :through => :order_products
has_many :order_products
accepts_nested_attributes_for :order_products
order_product.rb
class OrderProduct < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
order_controller.rb
if #order.save
if #order.purchase
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
The above are my association for the models. I have a big problem in storing multiple product_id from line_items after the cart has been purchased.
I believe there should be codes after if#order.purchase for it to work. How am I suppose to store the order_id and product_id into order_products table.
Can anyone assist me on this?
Appreciate any help here. Thanks
It looks like you might benefit from learning about accepts_nested_attributes_for, as well as how foreign_keys work with ActiveRecord
Foreign Keys
If you set up your ActiveRecord associations correctly, you'll be able to call something like #product.line_items from a single call
ActiveRecord (& relational databsaes in general) work off foreign_keys, which are basically a reference to a model's id in another table. When you say you want to input order_id in another table, what you really need to look at is how to get ActiveRecord to put the correct foreign key into the record
The reason why I'm writing this is because if you can appreciate how ActiveRecord works, you'll be in a much stronger position to fix your problem
Accepts_Nested_Attributes_For
I believe the function that will help you is accepts_nested_attributes_for - which basically allows you to save another model's data through your current model
It works like this:
#/app/models/order.rb
class Order
belongs_to :user
belongs_to :product
has_many :purchases
has_many :line_items, :dependent => :destroy
has_many :orders, :through => :order_products
has_many :order_products
accepts_nested_attributes_for :order_products
This means that if you send the appropriate nested attributes to this model, it will process :order_products for you
To do this, you need to add this to your form & controller:
#app/controllers/orders_controller.rb
def new
#order = Order.new
#order.order_products.build
end
private
def strong_params
params.require(:order).permit(:standard_params, order_products_attributes: [:name] )
end
#app/views/orders/new.html.erb
<%= form_for #order do |f| %>
<%= f.fields_for :order_products do |product| %>
<%= product.text_field :name %>
<% end %>
<% end %>
Connecting tables to shopping cart
I have three models and three database tables that I want to connect to one cart, I'm new in rails and have some problem to do this.
My initial idea was
Create model called Service as a parent of models Adverts, Package_of_products, and Subscriptions. And then connect it to cart by Line_item
Already know that I am doing something wrong
Each time when trying add one of my services to Line_items I getting message
ActiveRecord::RecordNotFound in LineItemsController#create
Couldn't find Service without an ID
app/controllers/line_items_controller.rb:44:in `create'
Already I have
def create
#cart = current_cart
service = Service.find(params[:service_id])
#line_item = #cart.line_items.build(:service => service)
respond_to do |format|
if #line_item.save
format.html { redirect_to(#line_item.cart, :notice => 'Line item was successfully created.')
end
I have 4 databas and models my Line_items
class LineItem < ActiveRecord::Base
belongs_to :service
belongs_to :cart
end
Cart
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
has_many :services,
has_many :adverts, :through => :services
has_many :package_of_products, :through => :services
has_many :subscriptions,:through => :services
Advert
class Advert < ActiveRecord::Base
belongs_to :service
end
Subscriptions
class Subscription < ActiveRecord::Base
belongs_to :service
end
Package_of_products
class PackageOfProduct < ActiveRecord::Base
belongs_to :service
end
ok, first the association name is belongs_to instead of belong_to, so please correct that misprint.
and then i think you need smth like this:
class Cart < ActiveRecord ::Base
has_many :line_items, :dependant => destroy
has_many :ads, :through => :line_items
has_many :products, :through => :line_items
has_many :services, :through => :line_items
end
check the has_many :through association here