I have this in a model
validates_uniqueness_of :aspect, :scope => :user_id
In an instance method of the same model I have “save!” but I don’t touch
the :aspect attribute in that instance method at all.
Whenever that save! command is run however I get this error:
ActiveRecord::RecordInvalid: Validation failed: Aspect has
already been taken
I don’t understand. The validation should not skip the instance from
which the method is called. Please help. Thanks.
Sorry, I meant The validation should SKIP the instance from which the
method is called. It appears that save! tries to validate the object
against itself and fails because of course it is not unique from itself.
Can you please explain why you think the validation should be
“skipped”?
Cheers,
Nicholas
On Jul 4, 5:59 am, Learn by Doing [email protected]
2009/7/4 Learn by Doing [email protected]:
 ActiveRecord::RecordInvalid: Validation failed:  Aspect has
already been taken
Why? Â I am just trying to update an existing object, not create a new
one that would violate the uniqueness validation.
What code are you using for creating and modifying the object before
saving it?
Colin
Hi Nicholas,
Thanks for your response. What I meant was that validation should not
compare the object that is being updated against its own existing copy.
For example, I am editting an existing object:
[:aspect => “Aspect-A”, :user_id => 5, :third_field => “morning”].
Now, the object is unique in the database according to
validates_uniqueness_of :aspect, :scope => :user_id
That’s all good. However, when I update the object with the following
statements:
self.third_field = “night”
save!
Rails returns an error:
ActiveRecord::RecordInvalid: Validation failed: Aspect has
already been taken
Why? I am just trying to update an existing object, not create a new
one that would violate the uniqueness validation.
Thanks.
Hi Colin,
Thanks for asking that question. I had preloaded the database so I
didn’t have to create a new record. So now I just tried doing that and
it wouldn’t let me create a new record although the :aspect value I used
does not exist at all in the database. Console output is below:
stat0 = Stat.new(:user => user0, :aspect => “aspect0”)
=> #<Stat id: nil, user_id: 1000, aspect: “aspect0”, . . .>
stat0.save!
ActiveRecord::RecordInvalid: Validation failed: Aspect has already been
taken
from
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/validations.rb:1021:in
save_without_dirty!' from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/dirty.rb:87:in
save_without_transactions!’
from
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/transactions.rb:200:in
save!' from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in
transaction’
from
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/transactions.rb:182:in
transaction' from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/transactions.rb:200:in
save!’
from
/opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/transactions.rb:208:in
rollback_active_record_state!' from /opt/local/lib/ruby/gems/1.8/gems/activerecord-2.3.2/lib/active_record/transactions.rb:200:in
save!’
from (irb):70
“aspect0” is something I completely made up and does not exist in the
database. :user_id 1000 does exist in different rows with different
values of :aspect however.
On Sat, Jul 4, 2009 at 2:57 AM, Learn by Doing <
[email protected]> wrote:
ActiveRecord::RecordInvalid: Validation failed: Aspect has
already been taken
I don’t understand. The validation should not skip the instance from
which the method is called. Please help. Thanks.
Posted via http://www.ruby-forum.com/.
What version of Rails are you using? Did you try this in Edge Rails?
-Conrad
2009/7/4 Learn by Doing [email protected]:
records. Â Only their :Stat_ids are different. Â Something wrong with my
uniqueness validation?
 validates_uniqueness_of :aspect, :scope => :user_id
Can you post the model code and the db structure for this table please?
Colin
Hi Conrad,
I am using Rails 2.3.2 and Ruby 1.8.7, the latest stable combination
recommended by rubyonrails.org.
I am continuing to troubleshoot this. I have deleted all the sample
data that I preloaded so that my database is empty. Now, the opposite
is happening, it is letting me create records in violation of the
uniqueness validation:
stat0 = Stat.new(:user_id => 1000, :aspect => “aspect0”)
=> #<Stat id: nil, user_id: 1000, aspect: “aspect0”>
stat0.save!
=> true
stat0 = Stat.new(:user_id => 1000, :aspect => “aspect0”)
=> #<Stat id: nil, user_id: 1000, aspect: “aspect0”>
stat0.save!
=> true
I check the database and yes it contains both of the above identical
records. Only their :Stat_ids are different. Something wrong with my
uniqueness validation?
validates_uniqueness_of :aspect, :scope => :user_id
Thanks.
I haven’t been able to reproduce the issue that you have been having
(on 2.3.2). I created a test suite for your model. Can you run that to
see if you get the same problem.
Requires mocha, update references to fixtures and fields as required.
It’s based on the migration and model you provided.
HTH,
Nicholas
On Jul 5, 11:59 pm, Learn by Doing [email protected]
Nicholas,
Thank you very much for working on this.
I ran the test suit but got errors. I modified the test suite:
StatTest.rb · GitHub , using the users.yml:
https://gist.github.com/1c282b5b006326cca497
Note that I changed the attribute name “aspect” to “score_aspect”
Below are the errors. Thank you for your help.
ruby test/unit/stat_test2.rb
Loaded suite test/unit/stat_test2
Started
E…EEEE
Finished in 1.227252 seconds.
-
Error:
test_should_add_error_on_score_aspect_when_validating_with_the_same_score_aspect
(StatTest):
ActiveRecord::RecordInvalid: Validation failed: Score aspect has
already been taken
test/unit/stat_test2.rb:33:in
`test_should_add_error_on_score_aspect_when_validating_with_the_same_score_aspect’
-
Error:
test_should_not_add_error_on_score_aspect_when_validating_an_updated_stat
(StatTest):
ActiveRecord::RecordInvalid: Validation failed: Score aspect has
already been taken
test/unit/stat_test2.rb:41:in
`test_should_not_add_error_on_score_aspect_when_validating_an_updated_stat’
-
Error:
test_should_not_add_error_on_score_aspect_when_validating_with_a_different_score_aspect
(StatTest):
ActiveRecord::RecordInvalid: Validation failed: Score aspect has
already been taken
test/unit/stat_test2.rb:25:in
`test_should_not_add_error_on_score_aspect_when_validating_with_a_different_score_aspect’
-
Error:
test_should_score_aspect_be_read_only(StatTest):
ActiveRecord::RecordInvalid: Validation failed: Score aspect has
already been taken
test/unit/stat_test2.rb:49:in
`test_should_score_aspect_be_read_only’
-
Error:
test_should_user_be_read_only(StatTest):
ActiveRecord::RecordInvalid: Validation failed: Score aspect has
already been taken
test/unit/stat_test2.rb:57:in `test_should_user_be_read_only’
8 tests, 3 assertions, 0 failures, 5 errors
Well you have me stumped, I ran your test suite against mine (after
modifying the data structure) and it works. I’m assuming there is
something in the mix that I’m not aware of. I’m not sure if it’s much
help, but you can download my test app to see if that runs for you.
http://files.me.com/nicholas.henry/0v2drf
Cheers,
Nicholas
Colin:
Below are the relevant code. Thanks for your help.
class CreateStats < ActiveRecord::Migration
def self.up
create_table :stats do |t|
t.integer :user_id, :null => false
t.string :aspect, :null => false, :default => "default
aspect"
t.timestamps
end
end
def self.down
drop_table :stats
end
end
class Stat < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id
validates_associated :user
validates_uniqueness_of :aspect, :scope => :user_id
attr_readonly :user_id, :aspect
end