Rails multiple association types - ruby-on-rails

Can you have a has_many belongs_to relationship AND a has_many :through relationship without any method call conflicts?
For example, what would user.hacks return - the hacks that the user posted, or the hacks that the user marked as favorites?
I need to be able to access both results.
class User < ActiveRecord::Base
has_many :hacks, through: :favorites
has_many :hacks
end
class Hack < ActiveRecord::Base
has_many :hacks, through: :favorites
belongs_to :users
end
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :hack
end

The has_many defines the methods depending on the relationship name.
This means, that if you are defining a relationship several times with the same name but different options, the last definition will override the methods of the previous definition calls.
So you can't define them with the same name.
If you need to access to both, hacks and favorite hacks, you have to create the relationships as follows
class User < ActiveRecord::Base
has_many :hacks
has_many :favorites
has_many :favorited_hacks, through: :favorites, source: :hack
end

I'm not sure what it will return, try it in the console.
However i would something like that in the user class (not tested, but you can look on how to alias relations):
has_many :favorite_hacks, through: :favorites, class: :hacks
There seems to be a typo in Hack.
On another note, the way you do it seems strange to me. The user hack relation seems like ownership, and the user - fav seems like bookmarking.

If all you want is to get a list of all favourite hacks (and all hacks belong to the user), then I think it would be a much better idea to model this behaviour like so:
class User < ActiveRecord::Base
has_many :hacks
end
class Hack < ActiveRecord::Base
belongs_to :user
scope :favorite, -> { where(favorite: true) }
end
and simply have a favorite attribute on your Hack model. Then you could find the favorites by invoking user.hacks.favorite.

Related

Get items with Rails relations

I have two models with a relation like this:
class Ticket < ActiveRecord::Base
belongs_to :group
end
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
has_many :tickets, as: :assignable
end
class Group < ActiveRecord::Base
has_many :tickets, -> { order(:created_at) }
has_and_belongs_to_many :users
end
I need to get all tickets belonging to the same groups the user has.
How can I accomplish that? Thank you so much!
As things stand, your relations are incomplete and so Rails won't work properly. If User has_many Tickets then Tickets must belong_to (or at least has_one) User. Alternatively, User can have_many Tickets through Group, which seems more likely in this case.
However, even then, it's not clear what your Group model is doing. Particularly, it's not clear how you intend it to relate to User - this looks like quite a complex relationship.
To start with, though, try and set the models up like this:
class Ticket < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
belongs_to :user
has_many :tickets, dependent: :destroy
end
class User < ApplicationRecord
has_many :groups, dependent: :destroy
has_many :tickets, through: :groups
end
(You'll see that I've also inherited these models from ApplicationRecord, which is how I've always done it.)
If you set it up as above, you can get your ticket records with a simple #user.tickets.
If this works, you can then add the extra HABTM relationship for Groups and Users. But be aware that HABTM relationships can be complex and there are good and bad ways to use them.
(If the primary relationship you really want is Groups > Users > Tickets then let me know and I can adjust accordingly.)

ActiveRecord: has_many choices limited to has_many of another model

I would like to achieve something as follows where PersonSubject has many topics, but the choices of these topics are limited to the the selection of topics through another model (ie: through the associated subject):
class Topic < ApplicationRecord
belongs_to :subject
end
class Subject < ApplicationRecord
has_many :topics
end
class PersonSubject < ApplicationRecord
belongs_to :person
belongs_to :subject
has_many :topics # where the choices are limited to the subject.skills
end
I would then like if any person_subject.subject.topics are deleted (or association removed), it would automatically update the person_subject.topics to no longer "point" to the Topic(s) that were deleted.
Is this possible?
You can use a lambda to put arbitrary filters on an association. See What is the equivalent of the has_many 'conditions' option in Rails 4?
has_many :topics, -> { where(skill: subject.skills) }
I don't know that this is exact code will work without seeing your schema (what is the data type of subject.skills, and how do you join this with topic?). But hopefully this gets you on the right track
edit
in response to your comment, I think
has_many :topics, through: :skills
would work

Rails has_many :through #new doesn't set associations on new record

As per the Rails docs, one can use has_many :through as a shortcut:
The has_many :through association is also useful for setting up
"shortcuts" through nested has_many associations. For example, if a
document has many sections, and a section has many paragraphs, you may
sometimes want to get a simple collection of all paragraphs in the
document.
So let's say we have this code:
class User < ApplicationRecord
has_many :sub_users
has_many :settings
end
class SubUser < ApplicationRecord
belongs_to :user
has_many :settings, through: :user
end
class Setting < ApplicationRecord
belongs_to :user
end
Based on this, if I run user.settings.new, I get a new Setting instance with user_id set to user.id.
That's great. But if I run sub_user.settings.new, I get a new Setting instance that doesn't have user_id set to sub_user.user.id.
Is this expected behavior?
I wouldn't use has_many through: for that, delegate looks like the best idea https://apidock.com/rails/Module/delegate
class SubUser < ApplicationRecord
belongs_to :user
delegate :settings, to: :user
end
Your current code is not what has_many through is for, check the docks, the relations are different https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

Rails merging fields in two way has_many through relationship

I have the following models:
class User < ActiveRecord::Base
has_many :forum_users
has_many :forums through: :forum_users
end
class Forum < ActiveRecord::Base
has_many :forum_users
has_many :users through: :forum_users
end
class ForumUser < ActiveRecord::Base
belongs_to :forums
belongs_to :users
end
ForumUser has an additional "permissions" field to designate what the user can and cannot do within a particular forum.
The problem is that the "permissions" field on ForumUser is much more difficult to access than fields on Rails models normally are. In an ideal world, I would be able to make requests like:
user = forum.users.first
user.permissions
But once it's assigned, the User doesn't even remember the Forum it was attained through in the first place. It seems like there has to be a simple and straightforward Rails way of doing this. If so, what is it?

Exposing associations of a has_many -> has_many through the parent?

If a user has many things and a thing has many stats, it seems like there's only way Rails-y way to expose the stats through the user.
class User < ActiveRecord::Base
has_many :things do
def stats
Stat.where(thing_id: proxy_association.owner.things_id)
end
end
end
class Thing < ActiveRecord::Base
belongs_to :user
has_many :stats
end
class Stat < ActiveRecord::Base
belongs_to :thing
has_one :user, through: :thing
end
User.first.things.stats == Stat.where(thing_id: User.first.thing_ids)
I'm trying to determine whether there are any other options... Some people on my team complain that this doesn't feel natural. I feel like this is the most natural expression of the relationship you could devise.
Does anyone have a better suggestion? I'll say, I've tried instance methods and they don't smell right.
I might use a has_many :stats, :through => :things in the User class.
Check out this: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

Resources