Rails: How to handle associations among namespaced models? - ruby-on-rails

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

Related

Rails: Favoriting System - models associations

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).

Implement a "Likes system" in rails

I'm trying to figure out how to setup the following. A user can create a review and then like his review or like other reviews if he wants to. I came up with the following setup:
class Like < ApplicationRecord
belongs_to :user
belongs_to :review
end
class User < ApplicationRecord
has_many :reviews
has_many :likes
has_many :liked_reviews, through: :likes, source: :review
end
class Review < ApplicationRecord
belongs_to :user
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
end
class CreateLikes < ActiveRecord::Migration[6.0]
def change
create_table :likes do |t|
t.references :user, index: true
t.references :likes, index: true
t.integer :review_id
t.timestamps
end
end
end
I'm pretty sure that the model associations I came up with are correct, but I'm not so sure about the like table. Can someone please review the above and tell me if the model associations and like table are correct? Thanks in advance!

Join two has many through associations in Rails 5

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

Is there a way to have two join tables associate the same two classes in a rails application?

Long time listener, first time caller. I'm trying to create two associations between the same database tables, Chatrooms and Users. What I have so far is a has_many through relationship where a Chatroom has many Users through Messages. This part works fine. What I want to do is to create a second join table that connects Chatrooms to Users, through a join table called Chatroom_players. So what I'd like is for Chatroom.first.users to get me users through the messages join table and Chatroom.first.players to get me everyone from the chatroom_players join table. The reason I want this is so that I can maintain user presence even if a user hasn't written any messages in the chat, also so that a user can leave the room but maintain his or her messages in the chat.
Here is what I have so far that does not work:
chatroom.rb:
class Chatroom < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :users, through: :messages
has_many :chatroom_players
has_many :users, through: :chatroom_players
end
message.rb:
class Message < ApplicationRecord
belongs_to :chatroom
belongs_to :user
validates :content, presence: true, length: {minimum: 2, maximum: 200}
end
chatroom_player.rb
class ChatroomPlayer < ApplicationRecord
belongs_to :chatroom
belongs_to :user
end
user.rb
class User < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :chatrooms, through: :messages
has_many :chatroom_players
has_many :chatrooms, through: :chatroom_players
end
chatroom_players migration:
class AddChatroomPlayers < ActiveRecord::Migration[5.0]
def change
create_table :chatroom_players do |t|
t.references :user, index: true, foreign_key: true, null: false
t.references :chatroom, index: true, foreign_key: true, null: false
t.boolean :creator, default: false
t.timestamps null: false
end
end
end
You need to use different names for the associations:
class Chatroom < ApplicationRecord
has_many :messages, dependent: :destroy
has_many :users, through: :messages
has_many :chatroom_players
# this is a separate association to users through the
# chatroom_players table.
has_many :participants,
through: :chatroom_players,
source: :user, # what association on chatroom_players to use
class_name: 'User' # since it cannot be deduced automatically
end

has_many, through - undefined method `relation_delegate_class'

I have a Users class, and a UserGroup class:
class User < ActiveRecord::Base
has_many :group_memberships
has_many :users_groups, through: :group_memberships
...
class UsersGroup < ActiveRecord::Base
has_many :group_memberships
has_many :users, through: :group_memberships
... and a GroupMembership class to join them -
class GroupMembership < ActiveRecord::Base
belongs_to :users, dependent: :destroy
belongs_to :users_groups, dependent: :destroy
My migrations look like this -
class CreateUsersGroups < ActiveRecord::Migration
def change
create_table :users_groups do |t|
t.string :title
t.string :status
t.string :about
t.timestamps null: false
end
end
end
class CreateGroupMembership < ActiveRecord::Migration
def change
create_table :group_memberships do |t|
t.integer :user_id, index: true
t.integer :users_group_id, index: true
t.boolean :owner
end
end
end
So user.group_memberships is perfectly happy, but user.users_groups returns an error -
undefined method `relation_delegate_class' for Users:Module
Similarly, users_group.group_memberships is fine, but users_group.users returns exactly the same error -
undefined method `relation_delegate_class' for Users:Module
... on the users module. I've stared at this for a couple of hours, but I'm sure it's simple syntax somewhere. What's the problem?
When using belongs_to I believe you need to use a singular format:
So not belongs_to :users but belongs_to :user
class GroupMembership < ActiveRecord::Base
belongs_to :user, dependent: :destroy
belongs_to :writers_group, dependent: :destroy
I believe that since you are using 'through' you will have to use:
user.group_memberships.users_groups
This is because users does not have users_groups or vice versa.
Instead you access the users_groups through group_memberships.

Resources