Rails 3.1 - Scope for multiple objects through belongs_to association? - activerecord

class Item < ActiveRecord::Base
belongs_to :account
belongs_to :category
end
class Category < ActiveRecord::Base
belongs_to :account
has_many :items
end
I'd like to do the following:
#items = #account.items.where(...)
#categories = #items.categories.order(...)
#items.categories should get all categories of #items through the belongs_to association. The best I've come up with is:
#categories = #items.map{|item| item.category }
But isn't there a scope for managing this?

Since you want the categories from an account by going through the items, I think you could do something like
has_many :categories, :through => :items
in your Account model, and then just call account.categories
Also, for the record, the map you're doing there does n+1 queries (it should be something like #categories = #items.includes(:category).map{...}

Related

Ruby: Associations to enable querying across 4 models

I've been trying all day to figure this out and not getting anywhere. I have these 4 models. Shop Customer Credit and CreditChange
I want to return the CreditChanges for a shop's customers, NOT the customers. Like this:
latest_changes = Shop.last.customers.credit_changes
I'm trying to join like this, but its returning the customers and not the credit_changes
Shop.last.customers.joins(:credit, :credit_change).where.not(credit_changes: {date: nil})
My models look like the following:
class Shop < ActiveRecord::Base
has_many :customers, dependent: :destroy
class Customer < ActiveRecord::Base
has_one :credit, dependent: :destroy
has_many :credit_changes, through: :credit
belongs_to :shop
class Credit < ActiveRecord::Base
belongs_to :customer
has_many :credit_changes, dependent: :destroy
class CreditChange < ActiveRecord::Base
belongs_to :credit
Where did I go wrong?
You want a has-many-through relationship.
A Shop has many CreditChanges through its Customers.
Try:
class Shop
has_many :customers
has_many :credit_changes, through: :customers
end
#shop.credit_changes
You can read more in the Rails documentation
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Try inverting the way you're looking at things:
CreditChange.joins(:credit => :customer).where(customers: {shop: Shop.last}).where.not(credit_changes: {date: nil})

Define a custom has_many relation

Let's say an application has a User who can Like a Post. But also a Post can be owned by a User (on owner_id). So models would look like
class User < ApplicationRecord
has_many :likes
has_many :posts, through: :likes
end
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
end
class Post < ApplicationRecord
has_many :likes
has_many :users, through: :likes
belongs_to :owner, class_name: 'User'
end
How can I define a relation that would return all users with any correlation to a post?
So trying to replace the users association:
class Post < ApplicationRecord
# no owner_id, lambda is called in an User::ActiveRecord_Relation context
has_many :users, -> { where('users.id = ? OR likes.post_id = ?', owner_id, id) }, through: :likes
# doesn't work because "Relation passed to #or must be structurally compatible."
def users
User.where(id: owner_id).or(users)
end
# works but isn't an association.
def users
User.joins(:likes).where('users.id = ? OR likes.post_id = ?', owner_id, id)
end
end
I'd love to be able to define something that would allow me to write
Post.includes(:users)
without
ActiveRecord::AssociationNotFoundError: Association named 'users' was not found on Post; perhaps you misspelled it?

Setting up my models with has_many through

I have become a little unstuck trying to setup my models with the correct associations, I have 3 models as follows
class Image < ActiveRecord::Base
has_many :categories
end
class Category < ActiveRecord::Base
has_many :image_categories
has_many :images, through: :image_categories
end
class ImageCategory < ActiveRecord::Base
# Holds image_id and category_id to allow multiple categories to be saved per image, as opposed to storing an array of objects in one DB column
belongs_to :image
belongs_to :category
end
The ImageCategory table is my join table as I see it which holds all my image_ids with their corresponding category_ids, as a Image can have multiple Categories
Form to create Image
permit_params :id, :title, :description, :photo,
category_ids: []
form html: { multipart: true } do |f|
inputs do
f.semantic_errors
f.input :title
f.input :description, as: :text, input_html: { rows: 10, cols: 10 }
f.input :categories, as: :check_boxes
end
end
When I try and then create an image, I get the following error:
can't write unknown attribute `image_id`
What error(s) have I made here?
I think the way that you have set up your associations is causing you this issue.
class Image < ActiveRecord::Base
has_many :image_categories
has_many :categories, through: :image_categories
end
class Category < ActiveRecord::Base
has_many :image_categories
has_many :images, through: :image_categories
end
class ImageCategory < ActiveRecord::Base
belongs_to :image
belongs_to :category
end
What your current associations describe is that a category can have many images through image_categories but image and image_categories are associated via category in your model which is not what I think you intend.
As I could see, there is a mistake in Image Model
class Image < ActiveRecord::Base
has_many :categories, through: :image_categories
has_many :image_categories
end
Hope this help you !!!
Just to add there was no need for your join model ImageCategory in this case. Check out has_and_belongs_to_many association (HABTM).
has_many:through is useful in scenarios where you would need to add additonal attributes to your ImageCategory model other than ImageId and CategoryId.
In your scenario, however, HABTM will suffice.

Rails 4 many to many joint table

I'm trying to learn many to many relationship, so I have two model Order and Product, I generated with scaffold a join table orders_products with the follow migration:
create_table :orders_products do |t|
t.references :order
t.references :product
end
I have in order model:
has_many :orders_products
has_many :products, through: :orders_products
accepts_nested_attributes_for :orders_products
in product model:
has_many :orders_products
has_many :orders, through: :orders_products
accepts_nested_attributes_for :orders_products
in ordersproduct model:
belongs_to :order
belongs_to :product
in order controller:
def new
#order = Order.new
#order.save
#entry = OrdersProduct.create
#entry.product_id = Product.find_by(name: 'default_product').id
#entry.order_id = #order.id
end
def edit
#order = Order.find(params[:id])
#entries = #order.products
#order.save
end
private
def order_params
params.require(:order).permit(:name, orders_products: [:id, :order_id, :product_id])
end
end
I am getting undefined method `products when I am in edit on the line
#entries = #order.products
Someone can help me?
Use has_and_belongs_to_many :products on Order and has_and_belongs_to_many :orders on Product. Wouldn't think through is appropriate in this scenario for what you are trying to accomplish. You can also remove the has_many :orders_products lines as well.

has_one :through and has_many :through

rails -v = 4.0
ruby -v = 2.1.1
I am having some serious problem with has_one :through. All of the google 1st 2 pages link are blue in color ( I have gone through all of them).
My problem is when I try to do
post = Post.last
post.build_user
It say undefined method `build_user'. My classes with associations are as follow.
class Post < ActiveRecord::Base
has_one :user_post
has_one :user, class_name: "User", through: :user_post
accepts_nested_attributes_for :user
end
class UserPost < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
class User < ActiveRecord::Base
has_many :user_posts
has_many :posts, through: :user_posts
end
It would be really great if somebody please help to out to resolve this issue.
Much obliged.
You are attempting to setup a Many-to-Many Relationship between Post and User but your current setup is incorrect.
You need to use has_many instead of has_one in Post model.
class Post < ActiveRecord::Base
has_many :user_posts
has_many :users, through: :user_posts
end
After this you can build the users as:
post = Post.last
post.users.build
UPDATE
You are getting error as undefined methodbuild_user'.because you can only usepost.build_userif association betweenPostandUserishas_one` and defined as below:
class Post < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
belongs_to :post # foreign key - post_id
end
UPDATE 2
Also, logically A user has_many posts AND A post has one User so your setup should ideally be
class Post < ActiveRecord::Base
belongs_to :user # foreign key - user_id
end
class User < ActiveRecord::Base
has_many :posts
end
After this you can build the posts for a user as:
user = User.last
user.posts.build
To build a user for a post:
post = Post.last
post.build_user

Resources