Rails, validate has_many through association with attribute value of children - ruby-on-rails

I have three models:
class User < ApplicationRecord
has_many :game_accounts
has_many :favorite_game_accounts, through: :game_account_favorites, source: :game_account
end
class GameAccount < ApplicationRecord
belongs_to :user
has_many :favorite_users, through: :game_account_favorites, source: :user
end
class GameAccountFavorite < ApplicationRecord
belongs_to :user
belongs_to :game_account
validates_presence_of :user, :game_account
validates_uniqueness_of :user, scope: :game_account_id
end
This means that User can have his own GameAccounts and other Users can add them to favorites.
I have added scope in order to prevent one user to have multiple favorites of the same GameAccount. However, there is one problem. User can add to favorite his own GameAccount. How to prevent user adding his own GameAccount to favorites?

I'm not sure that there is any built-in Rails validation for you case, so I'd suggest writing your own custom one.
In your particular case, you can verify on GameAccountFavorite instance, that game_account.user_id isn't equal to user.id.
There's plenty of ways of performing custom validation in Rails

Related

How to set up associations for accepting one proposal in Rails?

I am developing a feature in a Rails app that allows users to create projects. Then, other users can create proposals. Finally, the user that created the project can accept one of the proposals as the accepted proposal.
What is the best way to allow the user that created the project (the manager) to select one of the proposals as the accepted proposal?
The models are set up like this:
User model
class User < ApplicationRecord
has_many :projects
has_many :proposals
end
Project model
class Project < ApplicationRecord
belongs_to :manager, class_name: 'User', foreign_key: :user_id
has_many :proposals
end
Proposal model
class Proposal < ApplicationRecord
belongs_to :project
belongs_to :user
end
At first, I thought about an "accepted" boolean in the proposal table that would allow the user to mark the boolean as true to make the proposal "accepted". But this could lead to problems like some projects having more than one accepted proposal. It isn't the correct way to do this.
So, how should I set up an acceptance model that allows the manager to select and de-select a proposal as the one and only "accepted" proposal?
If you want to be 100% sure that no project can have more than one accepted proposal, then you can set it up with an additional relation. You would also need another relation inside Proposal
class Project < ApplicationRecord
belongs_to :accepted_proposal, class_name: 'Proposal', inverse_of: :accepted_for, optional: true
end
class Proposal < ApplicationRecord
has_one :accepted_for, class_name: 'Project', inverse_of: :accepted_proposal
end
You can also validate uniqueness only for accepted proposals, but I think you can't have a database unique index for this
class Proposal < ApplicationRecord
validates :project_id, :uniqueness, if: Proc.new { |proposal| proposal.accepted }
end

Delete has_one id associate with has_many primary key?

I have 2 models :
class Agency < ApplicationRecord
has_one :branding
end
class Branding < ApplicationRecord
has_many :agencies
end
when I was destroying any branding, it still keeps its key with the Agency, where I made a field branding_id.
I want something which nullifies it when any branding got to destroy in the process.
It automatically updates the agency branding_id to null.
Rails provides this option, please check below, it will update id to null in agency. for more info check this
class Branding < ApplicationRecord
has_many :agencies, dependent: :nullify
end
First of all, if Agency model has branding_id column, it should have belongs_to instead of has_one and provide optional: true option to make branding association not required:
class Agency < ApplicationRecord
belongs_to :branding, optional: true
end
Second, to do this, you should use nullify option, like this:
class Branding < ApplicationRecord
has_many :agencies, dependent: :nullify
end

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 - model belongs_to and has_one same class

I have two models, User and Request
By definition, A User can have many requests, and each Request has one assigned agent (who can be a different user)
with the base as,
class User < ActiveRecord::Base
has_many :requests
end
But there's a column in Request model, agent_id which has to be linked to User.
So how do I set the relationships the best way for,
1. User has_many requests (with column requests.user_id)
2. Request has_one user(with column requests.agent_id)
Both these on Requests table
This may be what you're looking for.
class User < ActiveRecord::Base
has_many :requests
end
class Request < ActiveRecord::Base
belongs_to :user
belongs_to :agent, class_name: 'User', foreign_key: 'agent_id'
end
You'd use the long form version of the associations. Look them up in the source code to see all the options, but for this example only one extra option needs to be specified since everything else is default.
For user
has_many :requests, foreign key: :agent_id
For request
has_one :user, foreign_key: :agent_id
It should works
class User < ActiveRecord::Base
has_many :requests
end
class Request < ActiveRecord::Base
belongs_to :agent, class_name: 'User'
end
If you want also link User with agent_id:
class User < ActiveRecord::Base
has_many :requests, foreign_key: 'agent_id'
end
The best way to establish Relationships is to follow Rails Naming Conventions.
Rename column 'agent_id' to 'user_id'.
By doing so, you can use
class User < ActiveRecord::Base
has_many :requests
end
class Request < ActiveRecord::Base
belongs_to :user
end

Recording the date an object is added to a has_many collection

Users on my site each have one list, which consists of a different type of users. I'm using a has_many through relationship to do this as follows:
List.rb:
class List < ActiveRecord::Base
belongs_to :company
has_many :list_applicants
has_many :applicants, through: :list_applicants
end
Applicant.rb:
class Applicant < ActiveRecord::Base
has_many :list_applicants
has_many :lists, through: :list_applicants
end
ListApplicant.rb
class ListApplicant < ActiveRecord::Base
attr_accessible :applicant_id, :list_id
belongs_to :applicant
belongs_to :list
end
Company.rb:
class Company < ActiveRecord::Base
has_one :list
end
When a user adds another user to their list, I'd like to record the date the user is added so they can sort their list of users by date added. What is the best way to do this?
You can use the created_at field of the ListApplicant model if it has one. If not, you may add manually a similar field.
UPDATE:
You can access the field by specifying both applicant and list like this:
#applicant.list_applicants.where(list_id: #list.id).first.created_at

Resources