Validation in Child Model in Nested Attribute - Rails - ruby-on-rails

This are my files:
word.rb
class Word < ActiveRecord::Base
has_many :word_answers, dependent: :destroy
accepts_nested_attributes_for :word_answers, allow_destroy: true,
reject_if: lambda {|attribute| attribute[:word_id].blank?}
end
word_answer.rb
class WordAnswer < ActiveRecord::Base
belongs_to :word
validates :content, uniqueness: true, presence: true
end
Is there any method that I can valiidate validates :content, uniqueness: true, presence: true of word_answer.rb in word.rb?
Rails 4.
I tried validates_associated :word_answers but it doesn't work.
I want to achieve is
(1) If I submit Word, it will give an error if there is no WordAnswer.
(2) If I submit Word, it will give an error if there is a BLANK WordAnswer.
(3) If a validation is WordAnswer is wrong, It will give an error if I submit Word.

I don't think if we can add validates :content, uniqueness: true, presence: true to Word method but you can achieve validation for WordAnswer model in Word Model by adding a before_save & before_update filters.
I guess the validations in WordAnswer model will work fine as you want them to when you use nested attributes in the form and it will validate fine.
In other case kindly explain what you actually want to achieve ?

Try the following
validate :detail
def detail
word_answers.each do |word_answer|
if word_answer.content.blank?
errors.add(:"word_answer.content", "must not be blank")
word_answer.errors.add(:content, "must not be blank")
end
end
end
It should work if your models are associated correctly.

Related

Is there an easy way to remove blank objects from a has-many association?

I got the following models:
class Course < ApplicationRecord
has_many :external_ids, as: :identifiable, dependent: :destroy, validate: true
accepts_nested_attributes_for :external_ids
end
and this one:
class ExternalId < ApplicationRecord
belongs_to :identifiable, polymorphic: true
validates :name, presence: true, uniqueness: true
validates :identifiable, presence: true, on: :create
end
Because of a nested form in the course form-view it's possible that a blank object of an external id will be saved. I'd like to remove these blank objects. With "blank object" I mean an ExternalId which is a new record and has a blank name. Currently I do that as follows:
#course.attributes = course_params
## SNIP - workaround to fix validations
external_ids = []
#course.external_ids.each do |ext_id|
external_ids << ext_id unless(ext_id.new_record? && ext_id.name.blank?)
end
#course.external_ids = external_ids
## SNAP - workaround to fix validations
#course.save
But this is a lot of code for a very simple task. Is there any function for that? destroy_if doesn't exists for an association, only for arrays.
Thanks you!
You can use accepts_nested_attributes_for with such key
accepts_nested_attributes_for :external_ids, reject_if: proc { |attributes| attributes['name'].blank? }
From docs:
You may also set a :reject_if proc to silently ignore any new record hashes if they fail to pass your criteria

validating attribute presence with condition

I am trying to introduce a functionality in my application in which the users can suggest an event to the organizers.To suggest an event the user will have to fill a form. I want to validate the presence of few fields(the attribute of the events) with a condition to be true, i.e. the fields should not be left blank if the user(the submitter of the form) is not an admin.However if the submitter of the form is an admin, the fields can be left blank.
validates :attribute_name, presence: { :if => :user.is_admin? }
undefined method `is_admin?' for :user:Symbol Did you mean? is_haml?
I have also tried :user.is_admin()==True , It also throws error:
undefined method `is_admin' for :user:Symbol Did you mean? is_a?
I have this attribute in the users table:
t.boolean "is_admin", default: false
I have the following associations defined in my events model:
has_many :event_users, dependent: :destroy
has_many :users, through: :event_users
has_one :submitter_event_user, -> { where(event_role: 'submitter') }, class_name: 'EventUser'
has_one :submitter, through: :submitter_event_user, source: :user
In the controllers I have this code:
#event.submitter = current_user
The issue is you are trying to call is_admin? on the Symbol :user as the error suggests.
If I understand correctly this attribute should be present unless user.is_admin? returns true
This can be done in multiple ways:
validates :attribute_name, presence: true, unless: -> {|u| u.is_admin?}
#Or
validates :attribute_name, presence: true, unless: :is_admin?
# or using presence directly as you tried to originally
validates :attribute_name, presence: {unless: -> {|u| u.is_admin?} }
# or
validates :attribute_name, presence: {unless: :is_admin? }
I generally prefer the first option as, IMO, it is the least ambiguous and the most readable but all of them should result in the same function so choose the one you prefer as long as you remain consistent.
In the block form the instance is yielded to the block and the block's return value is used to determine whether or not the validation should run.
When using the symbol the symbol is sent to the instance via the send message transmission e.g. self.send(:is_admin? and again the return value is used to determine if the validation should be applied
ActiveModel::Validations::ClassMethods#validates
Update based on revised question:
Since the Event is related to the User via submitter and this is already being set to the instance of a User you can validate in a very similar fashion via
validates :attribute_name, presence: true,
unless: ->(event) { event.submitter&.is_admin?}
Or make a separate method such as
def admin_submitter?
self.submitter&.is_admin?
end
validates :attribute_name, presence: {unless: :admin_submitter?}

Ruby on Rails - ActiveRecord validates presence if 'self.preference.present?' doesnt work

I'd like to write a validation for preference. It should validate the presence of :city (which is associated with belongs_to) in the case if a preference record for this user exists.
user.rb
# attributes
# :city, :string
has_one :preference
preference.rb
# attributes
# preferred_car_brand
belongs_to :user
I tried this, but records get saved without an error.
user.rb
validates :city, presence: true, if: :user_preference_exists
def user_preference_exists
self.preference.present?
end
You can use this to validate the presence of a field.
class User < ActiveRecord::Base
validates :city, presence: true
end
It won't let active record save user model with empty value for :city.

Rails validations if condition

I need your help validating a simple Rails model.
First i want to check if the user filled all input-fields:
class Test < ActiveRecord::Base
validates :firstname, :lastname, :firstvalue, :secondvalue, presence: true
[...]
I also want to check if the :secondvalue param is bigger than :firstvalue.
Adding
validate :biggerThan
def biggerThan
if self.secondvalue < self.firstvalue
errors.add(:secondvalue, "must be bigger than First")
end
end
Well this works, but only if all other fields are filled! Creating a new Entry leaving all fields blank i am getting an undefined method <' for nil:NilClass.
Could you help me?
You can do this
validate :biggerThan, if: Proc.new{ |test| test.firstvalue.present? and test.secondvalue.present? }
It would be good if you add numericality validations also
validates :firstvalue, numericality: true
validates :secondvalue, numericality: true

Model has_many OtherModel but localized, so different OtherModel for same Model based on locale: how?

I'm having hard time with this, it's not a direct problem of implementation but I don't understand which is the right way to do it, I have two options, but first, these are my models:
class Boat < ActiveRecord::Base
validates :name, presence: true, uniqueness: { case_sensitive: false }
has_many :tech_specs, order: 'position'
def visible?
self.visible
end
end
class TechSpec < ActiveRecord::Base
validates :boat_id, presence: true
validates :tech_spec_name_id, presence: true, uniqueness: { scope: :boat_id }
belongs_to :boat
belongs_to :tech_spec_name
before_destroy :destroy_name_if_required
acts_as_list scope: :boat
def as_json(options = {})
super(options.except!(:tech_spec_name_id).merge!(methods: [self.name]))
end
def name
self.tech_spec_name.try(:name) || ''
end
def name=(value)
self.tech_spec_name = TechSpecName.find_or_create_by_name(value)
end
def destroy_name_if_required
self.tech_spec_name.destroy if self.tech_spec_name.tech_specs.size <= 1
end
end
class TechSpecName < ActiveRecord::Base
validates :name, presence: true, uniqueness: { case_sensitive: false }
has_many :tech_specs
def self.with_name_like(str)
where('lower(name) LIKE lower(?)', "%#{ str }%")
end
end
The problem is that I want a page for a boat showing some tech specs when with a locale and when on a different locale, showing other tech specs.
Idea #1
My basic idea is to add to TechSpec globalize3 on tech_spec.value and on TechSpecName for field tech_spec_name.name
Idea #2
The other idea is to remove TechSpecName and, instead, use a field (tech_spec.name) that will "replace" completely TechSpecName. Notice that in this case, I'll still need to fetch names for autocomplete, but I will filter them in TechSpec instead of fetching all from TechSpecName. This field will use globalize3 again obviusly.
I don't know the downside of both approaches, so I need a suggestion.
Seems like idea #1 is ok, it works correctly and reduce the amount of repeated text inside Db.
I18n.with_locale helps a lot too, also Globalize.with_locale is helpful

Resources