So I'm a little stumped on a validation.
A client has many gyms and has many memberships
A gym has many clients and has many memberships
A membership belongs to a gym and belongs to a client
For one of my validations I'd like to set it up as so:
Validation: A client can have only one membership with gym
My thought so far being, I need to do something like, Membership.all and check if a client_id == self[client_id] or something of that nature. If so then render json: error "client has membership" else Membership.create!(member_params)
I feel like I'm overthinking at the moment and there must be a validates shorthand for such a situation
I'm a little unsure of how to go about this. Any help would be greatly appreciated!
I expect your models to look like this:
class Client < ApplicationRecord
has_many :memberships
has_many :gyms, through: :memberships
end
class Gym < ApplicationRecord
has_many :memberships
has_many :clients, through: :memberships
end
class Membership < ApplicationRecord
belongs_to :client
belongs_to :gym
end
To add the unique validation that one client can only have one membership with a gym at the same time add the following line to Membership model:
validates :client_id, uniqueness: { scope: :gym_id }
Additionally, I suggest adding a unique index to those columns in the database:
add_index :memberships, [:client_id, :gym_id], unique: true
You need uniqueness in the membership class, like this:
class Membership < ApplicationRecord
belongs_to :client
belongs_to :membership
validates :gym_id, uniqueness: {scope: :client_id}
end
Related
Rails newbie here. I am having trouble with ensuring the presence of at least one "has_many :through" relationship in my users model.
I have:
class User < ApplicationRecord
has_many :company_users, dependent: :destroy
has_many :companies, through: :company_users
#validates :company_users, presence: true
end
class Company < ApplicationRecord
has_many :company_users, dependent: :destroy
has_many :users, through: :company_users
end
class CompanyUser < ApplicationRecord
belongs_to :user
belongs_to :company
end
I got the commented validates line from the excellent answers at:
Validate that an object has one or more associated objects
Rails 3: validate presence of at least one has many through association item
However when I implement this line, then doing the following in rails console:
c = Company.create
c.users.create!(email: "test#example.com")
gives
`raise_validation_error': Validation failed: Company users can't be blank (ActiveRecord::RecordInvalid)
From my newbie perspective, it seems like the HMT entry isn't created until after the user is created, which creates a validation error preventing the user in the first place! It's probably a very simple error, but how do I get the above behaviour to work with that validation in place?
I have tried setting inverse_of in a couple place, without any success.
And before it's mentioned, I'm purposefully using HMT instead of HABTM because I have additional attributes on the company_users model that will be set.
One option would be to change the validation to
validates :companies, presence: true
and then create the user with
User.create(companies: [Company.create])
Edit:
Adding inverse_of to the CompanyUser model should also work. The validation would be left as validate :company_users, presence: true:
class CompanyUser
belongs_to :user, inverse_of: :company_users
belongs_to :company
end
I have a model for User, Link and Report. A user can create multiple links. And a user can report multiple links. But a user cannot report one link multiple times.
Here's my models:-
class User < ApplicationRecord
has_many :reports
has_many :links
class Link < ApplicationRecord
belongs_to :user
has_many :reports
class Report < ApplicationRecord
belongs_to :link
belongs_to :user
I've tried the following without luck.
has_one :user, through: :link
validates_uniqueness_of :user_id
validates_uniqueness_of :link_id, scope: :user_id
I thought it would be quite simple. but seems not. Help would be appreciated.
I'm on Rails 5.
Nevermind. It works, just my test spec failed. So I thought it didn't work. What worked was validates_uniqueness_of :user_id, scope: :link_id
I'm trying to verify the uniqueness of the rows in the join table Employee by user_id & restaurant_id.
Other posts suggest something like this, but it doesn't work with ":id" as attribute, because it still applies duplicates when I test it.
validates_uniqueness_of :id, :scope => [:user_id, :restaurant_id]
Maybe the shovel assignment #restaurant.users << #user does not verify for uniqueness?
Models:
class Restaurant < ActiveRecord::Base
has_many :employees
has_many :users, through: :employees
end
class User < ActiveRecord::Base
has_many :employees
has_many :restaurants, through: :employees
end
class Employee < ActiveRecord::Base
belongs_to :restaurant
belongs_to :user
end
Best work around I could find was to verify within the controller like so:
if #restaurant.users.include?(#user)
Which in my case is fine because I only perform that action in one place in my app, but I wonder if there is a better way to make it more DRY in other cases.
I'm having the following models:
class Price < ActiveRecord::Base
belongs_to :article, inverse_of: :prices
validates :article_id, presence: true
end
class Article < ActiveRecord::Base
has_many :prices, dependent: :destroy, inverse_of: :article
end
The code when creating them raises validation error when saving (Prices is invalid):
article = Article.new
article.prices.build( { amount: 55.0 } )
article.save! #=> Validation failed: Prices is invalid
So Rails isn't smart enough to save the parent object (Article) before the child objects (Prices) so article_id can be assigned to the price before it is saved.
How do you use validations on the foreign key when using the build function?
It seems like a pretty standard scenario that should work?
(I know you can use database level constraints, but I'm asking about application level validations here)
In Rails way you can do like this
class Article < ActiveRecord::Base
has_many :prices, dependent: :destroy, inverse_of: :article
validates_associated :prices
end
but this is not 100% solution to this.
You can try this gem https://github.com/perfectline/validates_existence
If I have a has_and_belongs_to_many relationship between two models, let's say Users and Accounts, can I require that a User have at least one Account, and how?
Also, using the has_and_belongs_to_many relationship, is it possible for an Account not to have a User?
What I need is a relationship where Accounts can live on their own, and belong to Billers, but they can also belong to Users if a User signed up with one. Is this possible, and how?
I personally would drop the the HABTM. Instead I would use has_many :though=>
You would need to create two new models, account_users, and account_billers. You likely already have join tables for the HABTM, but this will expose them as models so they will need ID fields.
So you would end up with something like the following:
class Account < ActiveRecord::Base
has_many :account_billers
has_many :account_users
has_many :billers, :through=> :account_billers
has_many :users, :through=> :account_users
end
class User < ActiveRecord::Base
has_many :account_users
has_many :accounts, :through=>:account_users
validates :accounts, :length => { :minimum => 1}
end
class Biller < ActiveRecord::Base
has_many :account_billers
has_many :accounts, :through=>:account_billers
validates :accounts, :length => { :minimum => 1}
end
class AccountUser < ActiveRecord::Base
belongs_to :user
belongs_to :account
end
class AccountBiller < ActiveRecord::Base
belongs_to :biller
belongs_to :account
end
To validate presence of at least one association, you might want to use a custom validation method, something like
class User < ActiveRecord::Base
has_and_belongs_to_many :accounts
validate :require_at_least_one_account
private
def require_at_least_one_account
errors.add(:accounts, "must amount to at least one") if accounts.size < 1
end
end
(Although this brings a question of how an account is shared between users)
For your second question, looks like polymorphic associations are what you're looking for, but you can't do this straight with a HABTM relationship, you'll have to change it to a has_many :through and introduce a join model.