So I have two models here:
class Screen < ActiveRecord::Base
belongs_to :user
validates :screen_size, :numericality =>{:less_than_or_equal_to =>100,:greater_than_or_equal_to => 0},:if => "user.access==1"
class User < ActiveRecord::Base
has_many :screens
attr_accessible :access
But this code doesn't work, cause no matter what value the user.access is, it will still execute the validation. What am I doing wrong here?
Thank you
change:
:if => "user.access==1"
with:
:if => lambda { |screen| screen.user.try(:access) ==1 }
Because:
you need to pass a function to evaluate condition on the fly
if your screen doesn't have any user, a mere screen.user.access would throw an exception
You passed in a string to the :if executable proc/function parameter. When this is a string, it tries to find a function with that name. What you actually want is an anonymous function here using a lambda.
class Screen < ActiveRecord::Base
belongs_to :user
validates :screen_size, :numericality => {:less_than_or_equal_to =>100, :greater_than_or_equal_to => 0}, :if => lambda {|s| s.user.access == 1 }
end
class User < ActiveRecord::Base
has_many :screens
attr_accessible :access
end
Related
I have model Question, Answer and AnswerDetail.
Answer:
class Answer < ActiveRecord::Base
has_many :answer_details, :dependent => :destroy
accepts_nested_attributes_for :answer_details, :allow_destroy => true
validates_associated :answer_details
end
AnswerDetail:
class AnswerDetail < ActiveRecord::Base
belongs_to :answer
belongs_to :question
validates_presence_of :answer_field, :if => lambda {isrequired == true}, :message => "This is required field"
end
The isrequired field is from the model Question.
Question:
class Question < ActiveRecord::Base
has_one :answer_detail
end
The AnswerDetal model has the question_id and answer_id field on it. I want to filter the answer_field if the isrequire field from Question model is equal to true? How I will do this? How to access the has_one association's attribute inside model?
I've done this before, although I've not got the code handy right now:
Nested
From what I remember, you can actually put the validation in the nested model:
#app/models/question.rb
class Question < ActiveRecord::Base
validates :answer_field, presence: true, if: lambda {isrequired == true}
end
I highly recommend using the new validates syntax
--
inverse_of
I'm sure I had to use inverse_of somewhere in the code I had (it's locked in a private GitHub repo sorry).
inverse_of basically includes the associated model in your current model. Much like how you've found the effectiveness of self.question.isrequired:
#app/models/answer_detail.rb
class AnswerDetail < ActiveRecord::Base
belongs_to :question, inverse_of: :answer_detail
validates :answer_field, presence: true, if: lambda { question.isrequired == true }
end
#app/models/question.rb
class Question < ActiveRecord::Base
has_one :answer_detail, inverse_of: :question
end
However, good news for you:
This is not the answer, this is how I would troubleshoot
Inspect in:
validates_presence_of :answer_field, :if => lambda {isrequired == true}, :message => "This is required field"
What variables you have available, you can do this with pry-rails gem. Like this:
validates_presence_of :answer_field, :if => lambda {binding.pry; isrequired == true}, :message => "This is required field"
When trying to save a question you will get a interactive console(in the server terminal window) where you can print values like self or do this:
validates_presence_of :answer_field, :if => lambda {|record| binding.pry; isrequired == true}, :message => "This is required field"
And print record.
Then you can try finding where Question is stored with: self.question or record.question.
I tried using self.question.isrequired and it is working. But maybe you have other nice suggestion to do the error validation. Thank you.
I am using the validates_existence_of gem. It works well except when I want to allow my foreign key to be nil.
Here are my models for User and Project. A project belongs_to a user and a contributor (a contributor is also a user), but the contributor can be nil.
Here is my user model:
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name
has_many :projects, :dependent => :destroy
has_many :user_trimester_statuses, :dependent => :destroy
end
And here is my project model:
class Project < ActiveRecord::Base
attr_accessible :added, :amount, :contributor_id, :label, :ref, :trimester_id, :user_id
belongs_to :user
belongs_to :contributor, :class_name => 'User'
belongs_to :trimester
validates :user, :existence => { :both => false }
validates :trimester, :existence => { :both => false }
validates :contributor, :existence => { :allow_nil => true, :both => false }
end
When I try to add a new project, I have an error if the user_id or trimester_id field is blank or invalid. But for the contributor_id field, there is no error thrown if the field is invalid. It goes through either way (valid, invalid, or nil).
What am I doing wrong?
I am using ruby 2.0.0p0 and rails 3.2.13.
It looks like there is an open bug about this in the project.
https://github.com/perfectline/validates_existence/issues/15
You may have to write a simple custom validator for this case until this gets fixed. (or dig in and see if you can fix the issue yourself)
UPDATE:
I just cloned the project and wrote a test to see what the issue was. It seems that when you add the allow_nil, the existence validator doesn't get called at all.
I'm not sure why that is, but in the meantime, you can work around the bug in an easy way, by using a proc.
instead of
validates :contributor, :existence => { :allow_nil => true, :both => false }
this would get the job done
validates_existence_of :contributor, :unless => Proc.new { |obj| obj.contributor_id.blank? }
I was able to prove that in my test case.
(I went with the 'validates_existence_of' method, instead of 'validates', because I thought it was cleaner in this case)
I have a polymorphic association (contact_details) in my Company model and I want to validate the parent model. Note: I am using accepts_nested_attributes_for in my parent model.
The basic rule:
the company must have at least one phone (phone is the kind of
contact_detail)
The problem:
accepts_nested_attributes_for call destroy for child objects AFTER
validation of the parent object
so the user are able to delete a phone. Of course, later, when the user will try to edit a company without a phone, he/she will get an error (The company must have at least one phone).
Company (Parent) model:
class Company < ActiveRecord::Base
PHONES_NUMBER_MIN = 1
attr_accessible :name, :contact_details_attributes, ...
has_many :contact_details, :as => :contactable, :dependent => :destroy
validate do |company|
check_phones_number
end
accepts_nested_attributes_for :contact_details, :allow_destroy => true, :reject_if => :all_blank
private
def phones_number_valid?
kind = ContactDetail::Kind.phone
phones = contact_details.select { |cd| cd.kind_id == kind.id }
phones.size >= PHONES_NUMBER_MIN
end
def check_phones_number
unless phones_number_valid?
errors.add(:base, :phones_too_short, :count => PHONES_NUMBER_MIN)
end
end
...
end
ContactDetail (Child) model:
class ContactDetail < ActiveRecord::Base
attr_accessible :kind_id, :kind_value_source
belongs_to :contactable, :polymorphic => true
belongs_to :kind
validates :kind_value_source, :presence => true, :length => {:maximum => 255}
...
end
Note: I simplified the original version, so objective was clear to you. Here is the gist with the code.
By using the reject_if option I am able to forbid the deletion of all the phones. It is probably the best option by now. But I want to hear your opinions.
I also found this question and tried to apply the answer, but it didn't helped a lot. The same problem, as I described above. I've drawn a flowchart so you can see the trace, as I see it.
How can I validate the parent model in such a case?
I would be grateful for any help.
From the question you referenced, you can get rid of the reject_if and modify the line in phones_number_valid?:
phones = contact_details.select { |cd| cd.kind_id == kind.id && !cd.marked_for_destruction? }
In my rails app I have 2 models: post and post_translations.
class PostTranslation < ActiveRecord::Base
belongs_to :post
LANGUAGES = %w( en fr es de it )
validates_inclusion_of :language, :in => LANGUAGES
end
class Post < ActiveRecord::Base
has_many :post_translations
end
I want to prevent the same language translation from being submitted twice, so I want to limit the enums to the values not listed in the language column of a particular post_id.
I don't know if I should do this in model, controller or helper.
Which is the best practice?
Thanks in advance.
I'd use an attribute on the class instead of defining it on an instance.
class PostTranslation < ActiveRecord::Base
##languages = %w( en fr es de it )
cattr_reader :languages
belongs_to :post
validates :language, :inclusion => { :in => ##languages },
:uniqueness => { :scope => :post_id }
end
Now to fulfill your requirement of showing only the languages without translations, define a method on Post:
class Post < ActiveRecord::Base
has_many :post_translations
def untranslated
PostTranslation.languages - post_translations.map(&:language)
end
end
Then you can build a select menu by getting a post (#post = Post.find(params[:id]) and populating the collection from #post.untranslated.
It's perfectly valid to keep this in the model. Models should hold primary responsibility for ensuring that the data entered into them is correct.
For your particular case, you can use the :uniqueness validator with a scope passed to it. Basically your validation will ensure that languages are unique in the context of a particular post
The following should work:
validates :language, :inclusion => { :in => LANGUAGES },
:uniqueness => { :scope => :post_id }
If you prefer the Rails 2 style syntax, you can use:
validates_uniqueness_of :language, :scope => :post_id
I have some STI structure like following:
class Assignment < ActiveRecord::Base
has_many :criteria, :class_name => "Criterion", :order => :position
end
class Criterion < ActiveRecord::Base
# Represents a criterion used to mark an assignment that
# being the super class for all different criterias
set_table_name "criteria" # set table name correctly
belongs_to :assignment
validates_associated :assignment, :message => 'association is not strong with an assignment'
validates_presence_of :assignment_id
validates_numericality_of :assignment_id, :only_integer => true, :greater_than => 0, :message => "can only be whole number greater than 0"
validates_uniqueness_of :criterion_name, :scope => :assignment_id, :message => 'is already taken'
validates_presence_of :criterion_name,:assignment_id
end
class FlexibleCriterion < Criterion
has_one :flexible_criterion_attribute, :class_name => "FlexibleCriterionAttribute"
accepts_nested_attributes_for :flexible_criterion_attribute
default_scope :include => :flexible_criterion_attribute
end
class FlexibleCriterionAttribute < ActiveRecord::Base
belongs_to :criterion, :class_name => "FlexibleCriterion"
validates_presence_of :max
validates_numericality_of :max, :message => "must be a number greater than 0.0", :greater_than => 0.0
DEFAULT_MAX = 1
end
Alright, I have uploaded my current working codes. So basically the problem is:
When I use a method like criterion = assignment.criteria.find_or_create_by_criterion_name("AAA"), I will get an object of criterion. But I want cast this object to flexiblecriterion so that I can assign the value of criterion.flexible_criterion_attribute
Thx in advance!
If assignment.criteria.find_or_create_by_criterion_name("AAA") returns a record in which type column has value FlexibleCriterion, it will automatically cast the object to FlexibleCriterion.
If you want to ensure that only FlexibleCriterion is returned, modify your finder like this: assignment.criteria.find_or_create_by_criterion_name_and_type("AAA", 'FlexibleCriterion')