Rails: validates uniqueness through - ruby-on-rails

I have the following Models:
Language
Itemtype
Item
belongs_to :itemtype
LocalisedItem
belongs_to :item
belongs_to :language
The LocalisedItem model has an attribute called "title".
I want to validate the uniqueness of said "title" attribute. My problem is the scope: It´s supposed to be unique per language (easy) and itemtype, which I could not figure out how to do until now.
My best try...
validates :title, :uniqueness => { :scope => [:language_id, 'item.itemtype_id'] }
...fails with "NoMethodError: undefined method `item.itemtype_id'".
Is there any way to check for uniqueness in the way described?

You can use this format for validate uniqueness with a scope:
validates_uniqueness_of :title, :scope => :language_id

Related

How to validate_presence_of with condition in a nested attributes?

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.

Rails validations and belongs_to association

In my rails projects I have a lot of association tables. And I have some validations. Nothing really difficult, and it works almost every times.
But from time to time (like tonight), I have to switch from
validates_presence_of :project_id
validates_presence_of :tag_id
validates_uniqueness_of :project_id, :scope => [:tag_id]
to
validates_presence_of :project
validates_presence_of :tag
validates_uniqueness_of :project, :scope => [:tag]
Do you know the difference ? Do you if one is better than the other ?
From the Rails Guides: http://guides.rubyonrails.org/active_record_validations.html#presence
2.9 presence This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either
nil or a blank string, that is, a string that is either empty or
consists of whitespace.
class Person < ActiveRecord::Base
validates :name, :login, :email, presence: true
end
If you want to be sure that an association is present, you'll need to
test whether the associated object itself is present, and not the
foreign key used to map the association.
class LineItem < ActiveRecord::Base
belongs_to :order
validates :order, presence: true
end
So, you should use the second example you gave, which tests if the associated object itself is present, and not the first example, which only tests if the foreign key used to map the association is present.

Rails: limit enum column for already submitted values

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

accepts_nested_attributes_for validation

I'm using Rails 2.3.8 and accepts_nested_attributes_for.
I have a simple category object which uses awesome_nested_set to allow nested categories.
For each category I would like a unique field called code. This would be unique for the category per level. Meaning parent categories will all have unique codes and sub categories will be unique within their own parent category.
EG:
code name
1 cat1
1 sub cat 1
2 cat2
1 sub cat 1
2 sub cat 2
3 cat3
1 sub1
This works without the validation process but when I try and use something like:
validates_uniqueness_of :code, :scope => :parent_id
That will not work because the parent has not been saved yet.
Here is my model:
class Category < ActiveRecord::Base
acts_as_nested_set
accepts_nested_attributes_for :children, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
default_scope :order => "lft"
validates_presence_of :code, :name, :is_child
validates_uniqueness_of :code, :scope => :parent_id
end
I have thought of a different way to do this and it's very close to working, problem is I cannot check for uniqueness between child categories.
In this second example I've embedded a hidden field in the form called 'is_child' to flag if the item is a sub category or not. Here is my example of this model:
class Category < ActiveRecord::Base
acts_as_nested_set
accepts_nested_attributes_for :children, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
default_scope :order => "lft"
validates_presence_of :code, :name, :is_child
#validates_uniqueness_of :code, :scope => :parent_id
validate :has_unique_code
attr_accessor :is_child
private
def has_unique_code
if self.is_child == "1"
# Check here if the code has already been taken this will only work for
# updating existing children.
else
# Check code relating to other parents
result = Category.find_by_code(self.code, :conditions => { :parent_id => nil})
if result.nil?
true
else
errors.add("code", "Duplicate found")
false
end
end
end
end
This is very close. If there was a way to detect duplicate codes in the reject_if syntax under accepts_nested_attributes_for then I would be there. This all seems over complicated though and would like suggestions to make it easier. We would like to keep adding categories and sub categories in the one form as it speeds up data entry.
Update:
Maybe I should be using build or before_save.
Instead of
validates_uniqueness_of :code, :scope => :parent_id
Try
validates_uniqueness_of :code, :scope => :parent
Besides that you'll need to set in the Category class:
has_many :children, :inverse_of => :category # or whatever name the relation is called in Child
The use of inverse_of will make the children variable parent be set before saving, and there is a chance it will work.

Rails AR validates_uniqueness_of against polymorphic relationship

Is it posible to validate the uniqueness of a child model's attribute scoped against a polymorphic relationship?
For example I have a model called field that belongs to fieldable:
class Field < ActiveRecord::Base
belongs_to :fieldable, :polymorphic => :true
validates_uniqueness_of :name, :scope => :fieldable_id
end
I have several other models (Pages, Items) which have many Fields. So what I want is to validate the uniqueness of the field name against the parent model, but the problem is that occasionally a Page and an Item share the same ID number, causing the validations to fail when they shouldn't.
Am I just doing this wrong or is there a better way to do this?
Just widen the scope to include the fieldable type:
class Field < ActiveRecord::Base
belongs_to :fieldable, :polymorphic => :true
validates_uniqueness_of :name, :scope => [:fieldable_id, :fieldable_type]
end
You can also add a message to override the default message, or use scope to add the validation:
class Field < ActiveRecord::Base
belongs_to :fieldable, :polymorphic => :true
validates_uniqueness_of :fieldable_id, :scope => [:fieldable_id, :fieldable_type], :message => 'cannot be duplicated'
end
As a bonus if you go to your en.yml, and enter:
activerecord:
attributes:
field:
fieldable_id: 'Field'
You are going to replace the default 'subject' that rails add to the errors with the one you specify here. So instead of saying: Fieldable Id has been already taken or so, it would say:
Field cannot be duplicated

Resources