Rails 4 scope through belongs_to association - ruby-on-rails

I am trying to scope through an array of child records based on a value in one of the parent columns. I am trying to find all the ShoppingCartItems that belong to a Product with a category "Bundle."
I am trying to use acts_as_shopping_cart_gem
My Models.
User.rb
class User < ActiveRecord::Base
has_many :shopping_carts, dependent: :destroy
end
ShoppingCart.rb
class ShoppingCart < ActiveRecord::Base
acts_as_shopping_cart_using :shopping_cart_item
belongs_to :user
has_many :shopping_cart_items, dependent: :destroy
has_many :products, through: :shopping_cart_items
end
Product.rb
class Product < ActiveRecord::Base
has_many :shopping_cart_items, dependent: :destroy
end
ShoppingCartItem.rb
class ShoppingCartItem < ActiveRecord::Base
belongs_to :product, dependent: :destroy
scope :bundles, -> {
joins(:product).where('products.category = ?', 'Bundles') unless category.blank?
}
end
I am getting this error:
> undefined local variable or method `category' for
> #<Class:0x007fc0f40310d0>

Your problem is actually straight forward - there is nowhere you defined the category variable.
This is how I would do that (generalized scope):
scope :by_category, lambda { |category|
joins(:product).where(products: { category: category })
}
Note, there is no unless statement - if the category is not passed to scope, it will raise the ArgumentError.
Then use the scope for any category:
ShoppingCartItem.by_category('Bundles')
To prevent the blank category to be passed into scope, just make sure you pass the right string. You can create a dropdown of categories:
Product.pluck(:category)
or something similar, if it is a part of user interface.

The category field on your scope regards the ShoppingCartItem? If so, try self.category.blank?. If not, just remove the unless statement.

Maybe you need to add Category model and add this relation:
class Product < ActiveRecord::Base
has_many :shopping_cart_items, dependent: :destroy
belongs_to :category
end

Related

Can a scope that looks up on a polymorphic be turned into an association between the two models?

Suppose an Invoice belongs_to Invoiceable, a polymorphic, being the possible invoiceable_types Subscription, SubscriptioCart and Purchase.
The Invoice table has the columns invoiceable_type and invoiceable_id. So for example, if I want to retrieve all Invoices related to a SubscriptionCart through the polymorphic, I can do Invoice.where(invoiceable_type: "SubscriptionCart").
Now how can I transform such scope into a direct association between Invoice and a SubscriptionCart through the polymorphic?
I've tried adding belongs_to :subscription_cart to the Invoice model, resulting in #invoice.subscription_cart returning nil .
This makes sense as the table invoices doesn't have a column subscription_cart_id (nor should it, as that's why we use the polymorphic).
But how do I specify what to look for in Invoiceable then?
I've tried class_name: :SubscriptionCart and foreign_key: subscription_cart_id but it still returns nil.
Stripped down models:
Invoice model:
class Invoice < ApplicationRecord
belongs_to :invoiceable, polymorphic: true
scope :subscription_cart, -> {
where(invoiceable_type: "SubscriptionCart")
}
end
SubscriptionCart model:
class SubscriptionCart < ApplicationRecord
include ::Invoiceable::Subscription
belongs_to :subscription
has_many :invoices, as: :invoiceable
end
Subscription model:
class Subscription < ApplicationRecord
include ::Invoiceable::Subscription
belongs_to :user
has_many :subscription_carts, dependent: :restrict_with_exception
has_many :invoices, as: :invoiceable, dependent: :restrict_with_exception
end
Invoiceable concern:
module Invoiceable::Subscription
extend ActiveSupport::Concern
include Invoiceable
included do
def attributes_for_invoice_items
{}.tap do |attributes|
attributes["flat_fee"] = plan_invoice_item
attributes["delivery_price"] = delivery_price_item_invoice_item if plan.deliverable?
attributes["setup_fee"] = setup_invoice_item if setup_fee_billing_pending?
attributes["per_unit"] = per_unit_invoice_item if base_plan.per_unit?
end
end
end
end

How to destroy... with has_many

There are models: member(id,project_id,user_id), project(id), user(id), somemodel(id, project_id, user_id).
member belongs_to :project, belongs_to :user
somemodel belongs_to :project, belongs_to :user
I would like that when a member is deleted so somemodel (that has project_id and user_id the same as that member) is deleted also. How to do that?
To do that I would like has_many :somemodels, dependent: :destroy to be added to member , but I don't know the right parameters to give to has_many :somemodels in member class. has_many :somemodels, dependent: :destroy alone doesn't work as it tries to search for somemodel by member_id which is not present on the somemodels table and so the no column error occurs.
What would be the right has_many :somemodels ... to be added to Member?
An alternative way could be to create a :through association for this specific relation, and then set that one to dependent: :destroy. I think it would be structured like this:
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
has_many :some_models, ->(member) { where(user_id: member.user_id) },
through: :project,
dependent: :destroy
end
I haven't tested it, but it seems right as far as I can see.
I think you should add a after_destroy callback to Member model:
# In member model
class Member < ApplicationRecord
after_destroy :also_destroy_somemodel
...
def also_destroy_somemodel
# Fast, use in case somemodel dont have any callbacks
Somemodel.where(project_id: project_id, user_id: user_id).delete_all
# Or
# Slow, use in case somemodel has callback(s)
# Somemodel.where(project_id: project_id, user_id: user_id).each &:destroy
end
end
Hope it helps.

Is: grandparent.parents.children association chaining not correct in Rails 4?

I'm having trouble figuring out the proper way of retrieving all children of multiple parents through association chaining.
To simplify I have three models:
class Customer < ActiveRecord::Base
has_many :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
After creating a few objects I tired to use the example from rails guides (association basics: 4.3.3.4 includes):
Customer.first.invoices.line_items
It returns:
undefined method `line_items' for #<Customer::ActiveRecord_Associations_CollectionProxy
Is grandparent.parents.children not usable?
EDIT
I'm not searching for the grandparent.parents.first.children, but all children of all parents in the collection, rails guides state:
If you frequently retrieve line items directly from customers (#customer.orders.line_items),
As a valid operation, I would like to know if that is a mistake.
FINAL As stated in the comments of the selected answer: in ActiveRecord: scopes are chainable but associations are not.
The customer.invoices.line_items cannot work the way you want to, since the has_many always is linked to a single record. but you can achieve what you want (if I understand correctly) using has_many through
as follows:
class Customer < ActiveRecord::Base
has_many :invoices
has_many :line_items, through: :invoices
end
class Invoice < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :invoice
end
and now you can write:
customer.line_items
and it will return all line_items which are connected to a customer's invoices.
Customer.first.invoices.first.line_items
Or if you want all of the data together, you can do something like:
results = Customer.first.invoices.includes(:line_items)
Then you may access data with no DB call, by looping results. For first data ex: results.first.line_items
Hope it helps!
Customer.first.invoices will return an collection (like an array) of invoices. The line_items method isn't defined for a collection, but its defined for an invoice. Try Customer.first.invoices.first.line_items
EDIT - If you always want the orders to include the line items, you can just do:
class Customer < ActiveRecord::Base
has_many :orders, -> { includes :line_items }
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class LineItem < ActiveRecord::Base
belongs_to :order
end

Rails 4, Active Record, create order scope on belongs_to for has_many_through

I have the following models set up:
class Location < ActiveRecord::Base
has_many :location_parking_locations
has_many :parking_locations, through: :location_parking_locations
end
class LocationParkingLocation < ActiveRecord::Base
belongs_to :location
belongs_to :parking_location
end
class ParkingLocation < ActiveRecord::Base
has_many :location_parking_locations
has_many :locations, through: :location_parking_locations
end
The LocationParkingLocation has an integer field called upvotes. I would like to create a 'by_votes' scope that I can add to a query to order the results by this upvotes field. Where and how do I define this scope, so that I can call it like this:
location.parking_locations.by_votes
I can't define it like this, because then it's not a valid method on parking_locations:
class LocationParkingLocation < ActiveRecord::Base
belongs_to :location
belongs_to :parking_location
scope :by_votes, -> { order("upvotes DESC") }
end
Should it be defined in the 'ParkingLocation' class? If so, how do I tell it that I want to order by a field on the location_parking_locations table?
I think you might be able to use merge here.
You can leave your scope in the LocationParkingLocation class, and the result would look like:
location.parking_locations.merge(LocationParkingLocation.by_votes)
I just read a little about it in this blog post.

conditionally retrieve records in a has_many through extend method

I'm trying to figure out how to conditionally retrieve records in a has_many through association.
I have three classes:
class User < ActiveRecord::Base
has_many :user_associations
has_many :projects,
source: :user_associatable,
source_type: 'Project',
through: :user_associations do
def owned
# retrieve projects that have is_owner = true on the user_association
end
end
end
class UserAssociations < ActiveRecord::Base
belongs_to :user_associatable, polymorphic: true
belongs_to :user
end
class Project < ActiveRecord::Base
has_many :user_associations, as: :user_associatable
has_many :users, through: :user_associations
end
UserAssociation has a polymorphic association to Project for added complexity.
When I run User.first.projects.owned I only want to retrieve projects that are associated to the user through UserAssociations that have the is_owner boolean set to true.
I'm guessing I have to do something with proxy_association?
I think you just need a where clause within the owned method: where("users_associations.is_owner": true).
You can do it by specifying a scope on your project class and using that scope in your has_many in your user class.
In your user class...
has_many :projects,
source: :user_associatable,
source_type: 'Project',
through: :user_associations,
-> { is_owner }
in your project class...
scope :is_owner, where('is_owner = true')
You can write a scope in project model
scope :owned, -> {where(user_authentications: {is_owner: true})}

Resources