Setting up my models with has_many through - ruby

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.

Related

Rails 5.1: polymorphic association create/update record with additional data saved in Join table

I have polymorphic association, which looks something like this:
class Permission < ApplicationRecord
belongs_to :permitable, polymorphic: true, optional: true
belongs_to :user, optional: true
enum status: {creator: 1, editor: 2}
end
class User < ApplicationRecord
has_many :permissions, class_name: 'Permission'
end
class Book < ApplicationRecord
has_many :permissions, class_name: 'Permission', as: :permitable, dependent: :destroy
has_many :users, class_name: 'User', through: :permissions
end
I need to do save/update multiple users for books and for each saved user set permission.status.
Lets say for book=Book.first I need to add:
1) user=User.find(2) with permission.creator
2) user=User.find(1) with permission.editor
In ideal world I would need to do it in one query.
How do I do this, please? Thank you!
Update
At the moment I can do:
def create
Book.create(book_params)
end
private
def book_params
params.require(:book).permit(:name, {user_ids[]})
end
This would create record in Book and add record in Permission, but would not set status there.
Probably not the best example with Permission model - it's for example purposes and maybe is confusing. Should I change that to something else?
Update 2
I've added accepts_nested_attributes_for in Book model:
class Permission < ApplicationRecord
belongs_to :permitable, polymorphic: true, optional: true
belongs_to :user
enum status: {creator: 1, editor: 2}
end
class User < ApplicationRecord
has_many :permissions, class_name: 'Permission'
end
class Book < ApplicationRecord
has_many :permissions, class_name: 'Permission', as: :permitable, dependent: :destroy
accepts_nested_attributes_for :permissions
has_many :users, class_name: 'User', through: :permissions
end
then in my controller:
def create
Book.create(:name, permissions_attributes: [{user_id: "1", status: :creator},
{user_id: "2", status: :owner}])
end
private
def book_params
params.require(:book).permit(:name, {permissions_attributes[:user_id, :status]})
end
How do I create some loop to do create dynamically, please?
If each book can only have one creator and one editor, you could just do this:
class User < ApplicationRecord
# Maybe you'd like to define e.g. `created_books` and `edited_books`?
has_many :books
end
class Book < ApplicationRecord
belongs_to :creator, class_name: 'User'
belongs_to :editor, class_name: 'User'
end
If the book can have one creator and many editors, you could instead do this:
class User < ApplicationRecord
# Maybe you'd like to define e.g. `created_books` and `edited_books`?
has_many :books
end
class Book < ApplicationRecord
belongs_to :creator, class_name: 'User'
has_many :editors, through: :book_editors
end
class BookEditor
belongs_to :editor, class_name: 'User'
belongs_to :book
end
When you create a book, pass in he creator_id and the editor_id/editor_ids (if it's a has_many relationship):
def create
Book.create(book_params)
end
private
def book_params
params.require(:book).permit(:name, :creator_id, editor_ids:[])
end
I don't see the need for polymorphism here.

Using Ransack to generate custom Active Admin Filter

I am looking to create a custom filter that will search other tables data based on the associations I have set up, I have achieved this within my view but cannot transfer this to a custom filter with Ransack.
I am trying search on Tournament name.
I have the following 3 models and associations setup:
class Gallery < ActiveRecord::Base
belongs_to :tournament
has_many :gallery_images, dependent: :destroy
accepts_nested_attributes_for :gallery_images, allow_destroy: :true
end
class Tournament < ActiveRecord::Base
has_many :fixtures, dependent: :destroy
has_one :gallery, dependent: :destroy
has_many :gallery_images, through: :gallery, dependent: :destroy
end
class GalleryImage < ActiveRecord::Base
belongs_to :gallery
has_attached_file :photo
end
In my view as stated I have achieved this:
ActiveAdmin.register GalleryImage do
index do
selectable_column
column 'Tournament' do |gal|
tourn = gal.gallery
tourn.tournament.name
end
end
end
How would I make this into a query using Ransack?
filter :custom_ransacker_method
as: :select,
collection: -> { Tournament.pluck(:name, :id) },
label: 'Tournament Name'
I am not sure on how to construct the custom ransacker method.

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

Rails 3.1 - Scope for multiple objects through belongs_to association?

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{...}

Resources