Validate_uniqueness_of question

Good day everyone,

I am trying to validate the uniqueness of my indexes (2 indexes) in a
table before an insert. Can I use validate_uniqueness_of to do this? It
seems it can only validate uniqueness for a column, but not for a
combination of 2 or more.

Example: a table has a PK of column#1 and column#2.

Thanks in advance,

Will T.

Make a unique index in the database, and write this:

begin
ModelName.create
rescue Exception => e

interrogate e to determine what sort of failure it was

and take the appropriate action.

end

This is the right way to do this. Testing before insert doesn’t
work because it’s possible that after you test, but before you
insert, another process may have inserted a duplicate!


– Tom M., CTO
– Engine Y., Ruby on Rails Hosting
– Reliability, Ease of Use, Scalability
– (866) 518-YARD (9273)

On 11/17/06, Will T. [email protected] wrote:

Thanks in advance,

Will T.


Posted via http://www.ruby-forum.com/.

Will,

I’ve only used multiple column in indexes when scoping relationships
like the following:

create_table :accounts, :force => true do |t|
  t.column :name,  :string, :null => false, :limit => 50
  ...
end

create_table :users, :force => true do |t|
  t.column :account_id,            :integer,   :null => false
  t.column :email_address,       :string
  ...
end

add_index :users, [ :account_id, :email_address ], :unique => true

Then you can use:

validates_uniqueness_of :email_address, :scope => :account_id,

:message => ‘There is already another user with this email address.’

It sounds like if your PK is made of two columns you might want to
just override before_validation(). Rails has never had great
composite key functionality although I think there are some plugins
out there that might ease the pain…

Hope this helps.


Zack C.
http://depixelate.com

Tom M. wrote:

Make a unique index in the database, and write this:

begin
ModelName.create
rescue Exception => e

interrogate e to determine what sort of failure it was

and take the appropriate action.

end

This is the right way to do this. Testing before insert doesn’t
work because it’s possible that after you test, but before you
insert, another process may have inserted a duplicate!


– Tom M., CTO
– Engine Y., Ruby on Rails Hosting
– Reliability, Ease of Use, Scalability
– (866) 518-YARD (9273)

I agree with Tom that defining a unique key in your DB and letting the
DB enforce it is the best way to go.

I question the usefulness of validates_uniqueness_of in general because:

  1. validates_uniqueness_of can’t guarantee that the field you are about
    to save to the DB is indeed unique (as Tom explained)

  2. validates_uniqueness_of needs to query the DB in order to validate
    your model, which defeats the whole purpose of an application-side
    validation (IMO)

I started a discussion about this on my blog, and no one has
successfully made a case for using validates_uniqueness_of yet. If
anybody out there does have a scenario where it makes sense to use this
validation, please let me know so I can stop criticizing it :slight_smile:
http://allyourdatabase.blogspot.com/2006/11/generating-activerecord-data.html

Cheers.

On 11/17/06, Bryan E. [email protected] wrote:

I agree with Tom that defining a unique key in your DB and letting the

Bryan,

I think validates_uniqueness_of does have a “valid” place. It lets
you keep your code DRY and works in 99.999% of the cases. It only
fails in the very rare case where another insert occurs after one
thread (or mongrel process) has checked for uniqueness. In that very
rare case the db’s unique index would catch it and the database would
stay valid. The only side effect is that the app would render an
error page to the user. I’m willing to bet that this would likely
never happen until a site got really big, and in even then it would
have to be the perfect storm.


Zack C.
http://depixelate.com

I tried using drysql for my application (http://drysql.rubyforge.org/
for my guide) and kept getting this NameError message.

I have a table called “room_types” and a class called “RoomType”

My controller looks like:

def new
require_gem ‘drysql’
@room_type = RoomType.new
end

My model looks like:

class RoomType < ActiveRecord::Base
set_table_name “room_types”
end

Any insight to this matter is much appreciated,

Will T.

Bryan E. wrote:

Tom M. wrote:

Make a unique index in the database, and write this:

begin
ModelName.create
rescue Exception => e

interrogate e to determine what sort of failure it was

and take the appropriate action.

end

This is the right way to do this. Testing before insert doesn’t
work because it’s possible that after you test, but before you
insert, another process may have inserted a duplicate!


– Tom M., CTO
– Engine Y., Ruby on Rails Hosting
– Reliability, Ease of Use, Scalability
– (866) 518-YARD (9273)

I agree with Tom that defining a unique key in your DB and letting the
DB enforce it is the best way to go.

I question the usefulness of validates_uniqueness_of in general because:

  1. validates_uniqueness_of can’t guarantee that the field you are about
    to save to the DB is indeed unique (as Tom explained)

  2. validates_uniqueness_of needs to query the DB in order to validate
    your model, which defeats the whole purpose of an application-side
    validation (IMO)

I started a discussion about this on my blog, and no one has
successfully made a case for using validates_uniqueness_of yet. If
anybody out there does have a scenario where it makes sense to use this
validation, please let me know so I can stop criticizing it :slight_smile:
All Your Database Are Belong To Me: Generating ActiveRecord Data Validations Automatically

Cheers.

On 11/17/06, Bryan E. [email protected] wrote:

Evaluating the validation on a more subjective criterion, I don’t
believe that using validates_uniqueness_of provides any DRYness to my
application code that can’t be achieved by extending ActiveRecord::Base
to handle a duplicate key error returned from the DB. Given that
validates_uniqueness_of needs to be defined multiple times in your
application code (once for each unique key), I think that defining a
generic duplicate key error handler in ActiveRecord::Base is actually a
more DRY approach, but I suppose that it is a matter of personal taste.

I agree. If only the databases and their drivers provided these info in
a
more standard, accessible way. I tried parsing postgres contraint
violation
errors and it’s a real pain to deduce which table and columns were
affected,
especially since these messages are locale dependent and change between
versions.

That said, a common way to introspect database errors in addition to
tables + columns would be wonderful.

jeremy

Jeremy K. wrote:

On 11/17/06, Bryan E. [email protected] wrote:

Evaluating the validation on a more subjective criterion, I don’t
believe that using validates_uniqueness_of provides any DRYness to my
application code that can’t be achieved by extending ActiveRecord::Base
to handle a duplicate key error returned from the DB. Given that
validates_uniqueness_of needs to be defined multiple times in your
application code (once for each unique key), I think that defining a
generic duplicate key error handler in ActiveRecord::Base is actually a
more DRY approach, but I suppose that it is a matter of personal taste.

I agree. If only the databases and their drivers provided these info in
a
more standard, accessible way. I tried parsing postgres contraint
violation
errors and it’s a real pain to deduce which table and columns were
affected,
especially since these messages are locale dependent and change between
versions.

That said, a common way to introspect database errors in addition to
tables + columns would be wonderful.

jeremy

Good point, Jeremy.
I think you’ve identified a hole in my argument

…and you’ve also given me an excellent idea for a future enhancement
to DrySQL: http://drysql.rubyforge.org/

Zack C. wrote:

On 11/17/06, Bryan E. [email protected] wrote:

I agree with Tom that defining a unique key in your DB and letting the

Bryan,

I think validates_uniqueness_of does have a “valid” place. It lets
you keep your code DRY and works in 99.999% of the cases. It only
fails in the very rare case where another insert occurs after one
thread (or mongrel process) has checked for uniqueness. In that very
rare case the db’s unique index would catch it and the database would
stay valid. The only side effect is that the app would render an
error page to the user. I’m willing to bet that this would likely
never happen until a site got really big, and in even then it would
have to be the perfect storm.


Zack C.
http://depixelate.com

Hi Zack

I agree with you that it would be a rare occurrance for a duplicate
value to be inserted into the DB after ActiveRecord does the uniqueness
check and before it actually saves your record.

However, it is possible. As well, your DB already does a duplicate key
check on insert/update (assuming that you defined a unique key on the
DB), so the uniqueness check done by the validation is redundant
database I/O and can’t be guaranteed to be accurate. In terms of
functionality and performance, using the validation actually seems to me
to be detrimental to an application, rather than beneficial.

Evaluating the validation on a more subjective criterion, I don’t
believe that using validates_uniqueness_of provides any DRYness to my
application code that can’t be achieved by extending ActiveRecord::Base
to handle a duplicate key error returned from the DB. Given that
validates_uniqueness_of needs to be defined multiple times in your
application code (once for each unique key), I think that defining a
generic duplicate key error handler in ActiveRecord::Base is actually a
more DRY approach, but I suppose that it is a matter of personal taste.

From my perspective, the application-side validations’ prime purpose is
to provide “fast” model validation by avoiding DB I/O. If an
application-side validation needs to perform redundant DB I/O in order
to validate an object, I can’t see the advantage of using it, especially
if it can’t even guarantee model validity.

This is just my opinion, and I respect yours as well.

Cheers.