Skip children validations with condition - ruby-on-rails

I have two ActiveRecords Author and Book.
class Author < ActiveRecord::Base
has_many :books
enum author_type: {
musician: 0,
scientist: 1
}
accepts_nested_attributes_for :books
end
class Book < ActiveRecord::Base
belongs_to :author
validates :name, presence: true
validates :score_url, presence: true
end
Now Book validates presence for both name and score_url,
but I want skip validation for score_url when author.author_type is scientist.
I tried this way, but author can not be found during creation.
class Book < ActiveRecord::Base
belongs_to :author
validates :name, presence: true
validates :score_url, presence: true, if: "author.scientist?"
end
What is the best solution here?

You need to provide a Proc to the conditional validation
validates :score_url, presence: true, if: Proc.new { |book| book.author.scientist? }
if your validation gets any more complex, you should extract the logic to a new method.
validates :score_url, presence: true, if: :author_scientist?
private
def author_scientist?
author.present? && author.scientist?
end

Related

Rails use model in the same namespace for belongs_to reference, how to reference model from outside

I working on a Rails application, currently we structure the app by modules. Right now we have 2 separate model for users: User and Freight::Customer::User.
I have a new model Freight::Customer::MembershipStatus looks like this:
class Freight::Customer::MembershipStatus < ActiveRecord::Base
belongs_to :customer, class_name: 'Freight::Customer'
belongs_to :created_by, class_name: 'User'
validates :from, presence: true
validates :to, presence: true
validates :customer, presence: true
validates :status, presence: true
end
In this case, the created_by is reference to User. But when the code run membership_status.created_by, rails try to look for the Freight::Customer::User, I think it because Rails try to look for model within the same module first.
Is there a way to config this model to use the outer User model class?
You can get user class using this type, try this.
class Freight::Customer::MembershipStatus < ActiveRecord::Base
belongs_to :customer, class_name: 'Freight::Customer'
belongs_to :created_by, class_name: '::User'
validates :from, presence: true
validates :to, presence: true
validates :customer, presence: true
validates :status, presence: true
end

Using has_many through inside a module

I have a model Evaluation with a has_many with model Tag through evaluation_tags
I need to add this relationship in this Module, but i don't know how can i do this
class Evaluation < ApplicationRecord
belongs_to :user
belongs_to :teacher
belongs_to :school
belongs_to :subject
has_many :evaluation_tags
has_many :tags, through: :evaluation_tags
accepts_nested_attributes_for :evaluation_tags
validates :teacher_id, presence: true
validates :subject_id, presence: true
validates :school_id, presence: true
validates :user_id, presence: true
validates :rating, presence: true
end
module Wizard
module Evaluation
STEPS = %w(step1 step2 step3).freeze
class Base
include ActiveModel::Model
attr_accessor :evaluation
delegate *::Evaluation.attribute_names.map { |attr| [attr, "#{attr}="] }.flatten, to: :evaluation
def initialize(evaluation_attributes)
#evaluation = ::Evaluation.new(evaluation_attributes)
end
end
class Step1 < Base
validates :teacher_id, presence: true
end
class Step2 < Step1
validates :subject_id, presence: true
end
class Step3 < Step2
validates :school, presence: true
validates :user, presence: true
validates :rating, presence: true
end
end
end
When i access the step3 page, this error appears
undefined method `tag_ids' for #
Can anyone help me?
I think you can use ActiveSupport::Concern, like this:
module Wizard
module Evaluation
extend ActiveSupport::Concern
included do
has_many :tags, through: :evaluation_tags
end
end
end
then your class need to include it:
class Evaluation
include Wizard::Evaluation
end

How to created new record on has_one for two model on the same time on Rails?

I need to create new record for two models that belong to the same model on one method.
This is my Model
class Promotion < ApplicationRecord
has_one :promotion_thai ,dependent: :destroy
has_one :promotion_eng ,dependent: :destroy
end
class PromotionThai < ApplicationRecord
belongs_to :promotion
mount_uploader :long_banner, PromotionImageUploader
mount_uploader :square_banner, PromotionImageUploader
mount_uploader :details_image, PromotionImageUploader
validates :promotion_id, presence: true
validates :title, presence: true
validates :description, presence: true
#validates :long_banner, presence: true
#validates :square_banner, presence: true
end
class PromotionEng < ApplicationRecord
belongs_to :promotion
mount_uploader :long_banner, PromotionImageUploader
mount_uploader :square_banner, PromotionImageUploader
mount_uploader :details_image, PromotionImageUploader
validates :promotion_id, presence: true
validates :title, presence: true
validates :description, presence: true
validates :long_banner, presence: true
validates :square_banner, presence: true
end
This is my controller method
def create
promotion = Promotion.new
promotion.build_promotion_eng(promotion_eng_params).build_promotion_thai(promotion_thai_params)
if promotion.save
flash[:success] = 'Success Created Promotion'
redirect_to admins_promotions_path
else
errors_message = promotion.errors.full_messages.join(', ')
redirect_to admins_promotion_new_path, :flash => { :error => errors_message }
end
end
Then when i submit the form i always got this error
undefined method `build_promotion_thai' for #<PromotionEng:0x007f9fdbcb0250> Did you mean? build_promotion
On this line
promotion.build_promotion_eng(promotion_eng_params).build_promotion_thai(promotion_thai_params)
How can i fix this kind of problem?
Thanks!
That's because build_promotion_eng(promotion_eng_params) returns an PromotionEng instance.
This should work fine.
promotion.build_promotion_eng(promotion_eng_params)
promotion.build_promotion_thai(promotion_thai_params)

Validates uniqueness in a one-to-many association

I have an Album model and Track model. I want to make sure that the name of each track on the album they belong to are unique. I tried using this validation in my Track model
validates :name, presence: true, uniqueness: true, scope: :album_id
but get the error: Unknown validator: 'ScopeValidator'
What am I doing wrong?
class Album < ActiveRecord::Base
has_many :tracks
accepts_nested_attributes_for :tracks, :reject_if => :all_blank, :allow_destroy => true
validates :name, presence: true, uniqueness: true
end
class Track < ActiveRecord::Base
belongs_to :album
validates :name, presence: true, uniqueness: true, scope: :album_id
end
You need to scope the uniqueness, not put it as a separate argument.
class Track < ActiveRecord::Base
belongs_to :album
validates :name, presence: true, uniqueness: { scope: :album_id }
end

ActiveRecord Validate Uniqueness with Scope Allowing Nil on Scope

I have a validation that looks like this:
class Book < ActiveRecord::Base
belongs_to :author
validates :name, uniqueness: { scope: :author_id }
end
The problem is that I want to allow duplicate names where the author id is nil. Is there a way to do this using the validates method (and not a custom validation)?
Yes, with a Proc and :unless on the validator.
class Book < ActiveRecord::Base
belongs_to :author
validates :name, uniqueness: { scope: :author_id }, unless: Proc.new { |b| b.author_id.blank? }
end
Recommended Reading: http://guides.rubyonrails.org/active_record_validations.html#using-a-proc-with-if-and-unless
Make it conditional:
validates :name, uniqueness: { scope: :author_id }, if: :author_id?

Resources