I'm in front of a little problem, I try to have a polymophic has many through association :
post.rb
has_many :categories, as: :categorizable, through: :categorizations
category.rb
has_many :categorizables, through: :categorizations, dependent: :destroy
event.rb
has_many :categories, as: :categorizable, through: :categorizations
categorization.rb
belongs_to :categorizable, :polymorphic => true
belongs_to :category
My migration :
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
create_table :categorizations do |t|
t.references :category
t.integer :categorizable_id, :polymorphic => true
t.string :categorizable_type, :polymorphic => true
t.datetime :created_at
end
add_index :categorizations, :category_id
end
the problem :
I got this error :
Could not find the association :categorizations in model Post
Or when I try in category
Could not find the association :categorizations in model Category
Does anyone know where is the problem?
You need to specify :categorizations association also, in Category, Post and Event. Also, your as option should go to categorizations association, since this is where you have polymorphism.
Post class:
class Post < ActiveRecord::Base
has_many :categorizations, as: :categorizable
has_many :categories, through: :categorizations
# ...
end
You should modify Event class in similar manner.
Category class:
class Category < ActiveRecord::Base
has_many :categorizations
has_many :categorizables, through: :categorizations, dependent: :destroy
# ...
end
Related
I have the following model associations setup were a consumer can favorite a product or a variant. I just wanted to ask if my approach is correct?
class Favorite < ApplicationRecord
belongs_to :consumer
belongs_to :favorited, polymorphic: true
belongs_to :product, optional: true
belongs_to :variant, optional: true
end
class Consumer < ApplicationRecord
has_many :favorites
has_many :favorite_products, through: :favorites, source: :favorited, source_type: 'Product'
has_many :favorite_variants, through: :favorites, source: :favorited, source_type: 'Variant'
end
class Product < ApplicationRecord
has_many :favorites, dependent: :destroy
end
class Variant < ApplicationRecord
has_many :favorites, dependent: :destroy
end
class CreateFavorites < ActiveRecord::Migration[6.0]
def change
create_table :favorites do |t|
t.references :consumer, index: true
t.references :favorited, polymorphic: true, index: true
t.integer :product_id
t.integer :variant_id
t.timestamps
end
end
end
It is not clear why you have both polymorphic favorited and separate relation on product_id/variant_id. Usually one would go with one of these approaches.
Polymorphic associations can be unidirectional too - has_many :favorites, as: :favorited, dependent: :destroy
If you go with separate columns for each relation - indexes on product_id and variant_id may also be useful to prevent full table scan on product/variant deletion (on dependent: :destroy, also if you do not expect callbacks/nested relations there - delete_all is faster).
I am trying to figure out what's the best way to handle namespaced models. Here's the models that i have in my project:
class Member < ApplicationRecord
has_one :ledger, inverse_of: :member, class_name: "Member::Ledger", dependent: :destroy
has_many :ledger_entries, through: :ledger
end
class Member::Ledger < ApplicationRecord
belongs_to :member, inverse_of: :ledger
has_many :ledger_entries, foreign_key: "member_ledger_id", dependent: :destroy
end
class Member::LedgerEntry < ApplicationRecord
belongs_to :ledger, foreign_key: "member_ledger_id"
end
And here's how my migrations files look like:
create_table :members do |t|
t.timestamps
end
create_table :member_ledgers do |t|
t.references :member, foreign_key: true, null: false, index: { unique: true }
t.timestamps
end
create_table :member_ledger_entries do |t|
t.references :member_ledger, foreign_key: true, null: false
t.timestamps
end
So I have few questions here:
Are migration files correct? I mean should i have member_ledger_id in the member_ledger_entries table or just ledger_id?
Are associations defined in a correct way? Even though this works but i am not sure this is how we are supposed to proceed.
I am using ruby-2.5.1 and rails-5.2.0.
Any help would be appreciated. Thanks in advance !!
Perhaps your associations could look more like:
class Member < ApplicationRecord
has_one :member_ledger, inverse_of: :member, dependent: :destroy
has_many :member_ledger_entries, through: :member_ledger
end
class Member::Ledger < ApplicationRecord
belongs_to :member, inverse_of: :member_ledger
has_many :member_ledger_entries, dependent: :destroy
end
class Member::LedgerEntry < ApplicationRecord
belongs_to :member_ledger
end
I have two has many through associations in my application which allows me to assign maps to a user through an admin dashboard.
Here are the tables I have in my application.
Mapgroups
class Mapgroup < ApplicationRecord
belongs_to :map, optional: true
belongs_to :group, optional: true
end
Usergroups
class Usergroup < ApplicationRecord
belongs_to :user, optional: true
belongs_to :group, optional: true
end
Groups
class Group < ApplicationRecord
has_many :usergroups, dependent: :destroy
has_many :users, through: :usergroups, dependent: :destroy
has_many :mapgroups, dependent: :destroy
has_many :maps, through: :mapgroups, dependent: :destroy
end
Users
class User < ApplicationRecord
has_many :groups, through: :usergroups, dependent: :destroy
end
I have been using this method to fetch the maps that the user has access to with this method:
def fetch_maps
self.groups.flat_map { |g| g.maps }
end
However, according to my logs and testing, this is a very expensive way to do this. How can I join tables on an association like this to just get the maps in this method instead?
I have tried this joins query
def fetch_maps
Map.joins(mapgroups: [:group, :user]).where(user: self)
end
But get this error
Can't join 'Mapgroup' to association named 'user'; perhaps you
misspelled it?
Any ideas on how I can join these tables to produce the maps the user has access to?
This is the usergroups table in my schema file:
create_table "usergroups", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.bigint "user_id"
t.bigint "group_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["group_id"], name: "index_usergroups_on_group_id"
t.index ["user_id"], name: "index_usergroups_on_user_id"
end
Maps model
class Map < ApplicationRecord
has_many :units, dependent: :destroy
belongs_to :subaccount, optional: true
belongs_to :term, optional: true
has_many :mapgroups, dependent: :destroy
has_many :groups, through: :mapgroups, dependent: :destroy
end
Your user.rb will be like this
class User < ApplicationRecord
has_many :usergroups
has_many :groups, through: :usergroups, dependent: :destroy
end
And reference the user with usergroups
and you don't need to dependent: :destroy use twice like
has_many :usergroups #=> remove this from this line , dependent: :destroy
has_many :users, through: :usergroups, dependent: :destroy
The has_many :through Association
A Video, a Song and an Article can have many Tags. And each Tag also can have many Video, Songs or Articles. So I have 5 models: Video, Song, Article, Tag and Taggings.
Here are these models:
class Video < ActiveRecord::Base
has_many :tags, :through => :taggings
end
class Song < ActiveRecord::Base
has_many :tags, :through => :taggings
end
class Article < ActiveRecord::Base
has_many :tags, :through => :taggings
end
class Tag < ActiveRecord::Base
has_many :articles
has_many :videos
has_many :songs
belong_to :taggings, :polymorphic => true #is this correct?
end
The database definition of Taggings
create_table "taggings", :force => true do |t|
t.integer "tag_id"
t.string "taggable_type"
t.integer "taggable_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Taggings model:
class Taggings < ActiveRecord::Base
belongs_to :tag #is this correct?
belong_to :taggable, :polymorphic => true #is this correct?
end
The issue I'm worried by is, do I have right definitions of model (belongs_to, has_many?) ? My gut tells me that I missed something. I've seen many articles and I'm quite confused by them.
You need these changes:
class Video < ActiveRecord::Base # or Song, or Article
has_many :taggings, :as => :taggable # add this
has_many :tags, :through => :taggings # ok
class Tag < ActiveRecord::Base
# WRONG! Tag has no tagging_id
# belong_to :taggings, :polymorphic => true
has_many :taggings # make it this way
# WRONG! Articles are available through taggings
# has_many :articles
# make it this way
with_options :through => :taggings, :source => :taggable do |tag|
tag.has_many :articles, :source_type => 'Article'
# same for videos
# and for songs
end
About with_options.
Your class Taggings seems ok except its name. It has to be singular, Tagging:
class Tagging < ActiveRecord::Base # no 's'!
belongs_to :tag
belong_to :taggable, :polymorphic => true
end
How do you eager load polymorphic has_many :through associations in Rails/ActiveRecord?
Here's the base setup:
class Post < ActiveRecord::Base
has_many :categorizations, :as => :categorizable
has_many :categories, :through => :categorizations
end
class Category < ActiveRecord::Base
has_many :categorizations, :as => :category
has_many :categorizables, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :category, :polymorphic => true
belongs_to :categorizable, :polymorphic => true
end
Assuming that we want to solve this eager loading problem for Rails 2.3.x and double polymorphic associations on the join model, how do you eager load the :through association on something like this:
posts = Post.all(:include => {:categories => :categorizations})
post.categories # no SQL call because they were eager loaded
That's not working, any ideas?
This is easier to accomplish with has_many :through. Do you have a specific reason for wanting to use polymorphic associations?
With has_many :through you can use this ActiveRecord query
posts = Post.all(:include => [:categorizations, :categories])
posts[0].categories # no additional sql query
posts[0].categorizations # no additional sql query
Model definitions
class Post < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations
end
class Category < ActiveRecord::Base
has_many :categorizations
has_many :posts, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :post
belongs_to :category
end
Using these migrations
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.timestamps
end
end
def self.down
drop_table :posts
end
end
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name
t.timestamps
end
end
def self.down
drop_table :categories
end
end
class CreateCategorizations < ActiveRecord::Migration
def self.up
create_table :categorizations do |t|
t.integer :post_id
t.integer :category_id
t.timestamps
end
end
def self.down
drop_table :categorizations
end
end