Dmitry Suzdalev
2012-Jan-05 13:35 UTC
Exchanging values of two records with unique-validated fields
Hi fellows!
Is there a way to atomically exchange values of records which are validated
using ''validates_uniqueness_of''?
Some details follow.
Imagine I have a model:
class Item < ActiveRecord::Base
validates_uniqueness_of :weight
end
''weight'' is a sorting weight.
I have an index.html view which has a list of "Item" records wrapped
in
jQueryUI''s sortable container.
My goal is to do some ajax request (POST i guess) whenever user changes
items'' order. I.e. i plan to do end up with controller action that
calls
function like:
class Item
def self.exchange_weights(id1, id2)
item1 = Item.find(id1)
item2 = Item.find(id2)
weight1 = Item.find(id1).weight
weight2 = Item.find(id2).weight
item1.weight = some_temp_weight
item2.weight = weight1
item1.weight = weight2
end
end
Is this the right way to do this?
Or can I somehow just do a separate POST requests on these Item''s
instances
by triggering ItemsController#update for each of them and passing weight -
but here validations would not allow me to do this unless I miss something
- that''s why I desided to ask :)
Thanks.
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To view this discussion on the web visit
https://groups.google.com/d/msg/rubyonrails-talk/-/xOuZts86q58J.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.
Peter Vandenabeele
2012-Jan-05 14:09 UTC
Re: Exchanging values of two records with unique-validated fields
On Thu, Jan 5, 2012 at 2:35 PM, Dmitry Suzdalev <dimsuz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Hi fellows! > > Is there a way to atomically exchange values of records which are > validated using ''validates_uniqueness_of''? > Some details follow. > > Imagine I have a model: > > class Item < ActiveRecord::Base > validates_uniqueness_of :weight > end > > ''weight'' is a sorting weight. > > I have an index.html view which has a list of "Item" records wrapped in > jQueryUI''s sortable container. > My goal is to do some ajax request (POST i guess) whenever user changes > items'' order. I.e. i plan to do end up with controller action that calls > function like: > > class Item > def self.exchange_weights(id1, id2) > item1 = Item.find(id1) > item2 = Item.find(id2) > weight1 = Item.find(id1).weight > weight2 = Item.find(id2).weight > item1.weight = some_temp_weight > item2.weight = weight1 > item1.weight = weight2 > end > end > > Is this the right way to do this? > Or can I somehow just do a separate POST requests on these Item''s > instances by triggering ItemsController#update for each of them and passing > weight - but here validations would not allow me to do this unless I miss > something - that''s why I desided to ask :) >If you mean "atomic" on the level of saving to the database (all or nothing), you would need a database transaction. http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html Second, to circumvent the problem of the uniqueness validation, I see 2 solutions: 1) what you propose above: UNTESTED: def self.exchange_weights(id1, id2) item1 = Item.find(id1) item2 = Item.find(id2) weight1 = item1.weight weight2 = item2.weight Item.transaction do item1.weight = some_temp_weight item1.save! item2.weight = weight1 item2.save! item1.weight = weight2 item1.save! end # you still need to catch the exception here end This could fail ... e.g. if 2 parallel processed each write the same some_temp_weight exactly at the same time ... 2) Alternative: Make the validation dependent on an instance variable UNTESTED class Item attr_accessor :no_weight_uniqueness_validation validates :weight, :uniqueness => true, :unless => @no_weight_uniqueness_validation end def self.exchange_weights(id1, id2) item1 = Item.find(id1) item2 = Item.find(id2) weight1 = item1.weight weight2 = item2.weight Item.transaction do item2.weight = weight1 item2.no_weight_uniqueness_validation = true item2.save! item1.weight = weight2 item1.save! end # you still need to catch the exception here end This will NOT work if you also have the uniqueness validation in the database itself ... which you should, since the uniqueness validation in Rails is not automatically protected against race conditions when multiple parallell processes write to the same database. HTH, Peter -- Peter Vandenabeele http://twitter.com/peter_v http://rails.vandenabeele.com -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Dmitry Suzdalev
2012-Jan-05 14:16 UTC
Re: Exchanging values of two records with unique-validated fields
On 5 January 2012 18:09, Peter Vandenabeele <peter-jNuWw7i2w7syMbTcgqFhxg@public.gmane.org> wrote:> If you mean "atomic" on the level of saving to the database > (all or nothing), you would need a database transaction. > > http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html > > Second, to circumvent the problem of the uniqueness validation, > I see 2 solutions: > <snip> >Oh, it''s clearer now. And, yes I have *just* saw in rails'' apidocs that I should do add_index on the DB level too - to prevent possible clashes. Didn''t know that until now, thanks. I guess that I will start with the first solution then. And maybe will improve it to be the latter if find the need too. Thanks, Peter! -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.