I have 2 models, cart and line_item:
cart.rb & line_item.rb
class Cart < ActiveRecord::Base
has_many :line_items, dependent: :destroy
belongs_to :user
class LineItem < ActiveRecord::Base
belongs_to :cart
belongs_to :user
application_controller.rb
def current_cart
Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
cart = current_user.cart.create
session[:cart_id] = cart.id
cart
end
How can I add validations to my cart so that user can only add 5 items maximum into their cart? At the moment I have this code but it is not working?
def maximum_items_not_more_than_5
if line_items.count > 5
errors.add(:line_items, "must be less than 5")
end
end
Here is a way, I would try :
class LineItem < ActiveRecord::Base
belongs_to :cart, validate: true # enables validation
Then inside the Cart model, write your own custom validation like :
class Cart < ActiveRecord::Base
has_many :line_items, dependent: :destroy
validate :maximum_items_not_more_than_5 # using custom validation
private
def maximum_items_not_more_than_5
if line_items.count > 5
errors.add(:base, "must be less than 5")
end
end
Why is line_item belonging to user?? Surely it would be item:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :carts
end
#app/models/cart.rb
class Cart < ActiveRecord::Base
belongs_to :user
has_many :line_items, inverse_of: :cart
has_many :items, through: :line_items
validate :max_line_items
private
def max_line_items
errors.add(:tags, "Too many items in your cart!!!!!!") if line_items.size > 5
end
end
#app/models/line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :cart, inverse_of: :line_items
belongs_to :item #-> surely you want to specify which item is in the cart?
end
#app/models/item.rb
class Item < ActiveRecord::Base
has_many :line_items
has_many :carts, through: :line_items
end
Validation
This is certainly in the realms of validation, specifically a custom method:
#app/models/model.rb
class Model < ActiveRecord::Base
validate :method
private
def method
## has access to all the instance attributes
end
end
I also put inverse_of into the mix.
You can see how this works here: https://stackoverflow.com/a/20283759/1143732
Specifically, it allows you to call parent / child objects from a particular model, thus allowing you to call validations & methods residing in those files.
In your case, it may be prudent to add validations to the line_item model -- specifying individual quantities or something. You can call the validations in this model directly from your cart model by setting the correct inverse_of
Related
How to track in the models this command
=> order = Order.create
=> order.items << Item.first // this command
if i have such models:
class Order < ApplicationRecord
has_many :order_items
has_many :items, through: :order_items
end
class Item < ApplicationRecord
has_many :order_items
has_many :orders, through: :order_items
end
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :item
end
I try use after_add for example, but I did not succeed.
For example my task:
In controller`s (OrderController) method create:
def create
#order = Order.create(order_params)
#order.items << Item.find(params[:id])
end
And i have that models Order or Item track this (when i add item to order) and print me message in console (for example)
Have a look at the Rails Guides about Association Callbacks. There is, for example, an after_add callback.
# in your Order model
has_many :items, after_add: :track_item_added
private
def track_item_added(item)
# your tracking code, for example
Rails.logger.debug("Item ##{item.id} added to order ##{id}")
end
If I delete child record so parent record does not get deleted automatically.
class User < ActiveRecord::Base
has_one :agency, dependent: :destroy
accepts_nested_attributes_for :agency
end
class Agency < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
end
if #agency.present?
#agency.user.destroy
flash[:notice] = 'Agency Deleted'
end
Destroy child record so parent record automatically destroy.
I think, your models could be re-written like this to achieve expected output.
class User < ActiveRecord::Base
has_one :agency # Change
accepts_nested_attributes_for :agency
end
class Agency < ActiveRecord::Base
belongs_to :user, dependent: :destroy # Change
accepts_nested_attributes_for :user
end
if #agency.present?
#agency.destroy # Change
flash[:notice] = 'Agency Deleted'
end
Let's think logically now.
What have you changed is, you made User dependent on Agency and now it's rails doable to form a parent-child relationship to get accepted output. So when you destroy an #agency, it will also delete the dependent user record.
You should use the following code to delete a user and its associated agency without making any change to your model.
class User < ActiveRecord::Base
has_one :agency, dependent: :destroy
accepts_nested_attributes_for :agency
end
class Agency < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
end
if #agency.present?
user = #agency.user #Change
user.destroy # This will destroy both user and associated agency.
flash[:notice] = 'Agency and User Deleted'
end
A complete official guide on dependent: :destroy can be find here.
I have 3 models, User, Product, Coupon.
A User has many Products, Product belongs to User.
A User has many Coupons, Coupon belongs to User.
My goal is to apply a Coupon to a Product. A Product can have one coupon and a Coupon can be applied to many Products. Currently I have the models set up like this:
#coupon.rb
class Coupon < ApplicationRecord
belongs_to :user
has_many :products
validates_presence_of :code, :discount_percent, :description
end
#user.rb
class User < ApplicationRecord
has_many :products
has_many :coupons
end
#product.rb
class Product < ApplicationRecord
belongs_to :user
has_one :coupon, dependent: :destroy
end
Currently a user can successfully create a coupon, but if I apply the coupon to the product and try to delete the coupon, it gives me a foreign key error.
I've thought about making the product.coupon_id = nil inside the destroy action of the coupons_controller but I feel that is a bad practice. Ex.)
#coupons_controller.rb
def destroy
products = Product.where(coupon_id: #coupon.id)
products.each do |product|
product.coupon_id = nil
product.save
end
#coupon.destroy
end
I think I have something wrong with my associations but can't seem to figure it out! Using Postgres.
I appreciate any help!
class Coupon < ApplicationRecord
belongs_to :user
has_many :products, dependent: :nullify
validates_presence_of :code, :discount_percent, :description
end
Models:
class Factory < ActiveRecord::Base
has_many :factory_workers
has_many :workers, through: :factory_workers
end
class FactoryWorkers < ActiveRecord::Base
belongs_to :factory
belongs_to :worker
before_destroy :union_approves?
private
def union_approves?
errors.add(:proletariat, "is never destroyed!")
false
end
end
class Worker < ActiveRecord::Base
has_many :factory_workers
has_many :factorys, through: :factory_workers
end
If I attempt to update a Factory's list of Workers via Factory, and that leads to the destruction of some FactoryWorker associations, I hope that the before_destroy hook is called, but this does not seem to be the case.
Example
Factory.create(name: 'communist paradise', worker_ids: [1, 2])
Factory.find_by(name: 'commnist paradise').update(worker_ids: [1])
# before_destroy hook is not called, proletariat must riot!
How can I ensure the before_destory hook is called when updating a record's associations?
Found what I needed: ActiveRecord provides a before_remove method on associations, so I just need to rejigger as follows:
class Factory < ActiveRecord::Base
has_and_belongs_to_many :workers, before_remove: :union_approves?
private
def union_approves?
...
end
end
How can I delete nested objects in a form? I found out that I need to add :allow_destroy in the parent model at the accepts_nested_attributes_for directive.
Further, I want to restrict the deletion. A nested object only should be deleted, if the parent object is the only one that retains the association.
Example:
class Internship < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company, allow_destroy => true
end
class Company < ActiveRecord::Base
has_many :internships
end
Explanation: A company can host many internships. Therefore, I do not want to delete the company record as long as there is at least one other internship associated with it.
You could use dependent => :destroy
class Internship < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company, allow_destroy => true
end
class Company < ActiveRecord::Base
has_many :internships, :dependent => :destroy
end
If you return false in a before_destroy filter, then the destroy action will be blocked. So we can check to see if there are any internships associated to the company, and block it if so. This is done in the company model.
class Company < ActiveRecord::Base
has_many :internships
before_destroy :ensure_no_internships
private
def ensure_no_internships
return false if self.internships.count > 0
end
end