This project is archived and is in readonly mode.

#2160 ✓invalid
mo

nested_attributes: validates_uniqueness_of fails

Reported by mo | March 7th, 2009 @ 12:36 AM | in 2.x


class Product < ActiveRecord::Base
  has_many :categorizations
  accepts_nested_attributes_for :categorizations
end

class Categorization < ActiveRecord::Base
  belongs_to :product
  belongs_to :category
  validates_uniqueness_of :product_id, :scope => [:category_id]
end

If I create/edit a product and add two new (new record) "categorizations" with the same category_id, it will validate and save.

In 2.3-RC1 double new records were ignored and not saved.

Comments and changes to this ticket

  • mo

    mo March 7th, 2009 @ 01:05 AM

    hmm.. this only happens with "@product.categorizations.build" in the products_controller.

    I noticed something else. If I create/edit a product, choose a category and, lets say, type in an invalid product title, i get an "missing product_id" error for the chosen category.

    All these bugs were not in 2.3-RC1.. that's really annoying. :/

  • Eloy Duran

    Eloy Duran March 7th, 2009 @ 11:51 AM

    • Assigned user set to “Eloy Duran”
    • State changed from “new” to “open”

    Could you please try to distill a failing test for the exact problem you are having? That would be greatly appreciated.

    Thanks

  • Eloy Duran
  • Eloy Duran

    Eloy Duran March 13th, 2009 @ 08:09 AM

    • State changed from “open” to “incomplete”
  • Ed Lebert

    Ed Lebert May 22nd, 2009 @ 02:46 PM

    This is really a problem with a core rails philosophy. It assumes that, no matter what, if "valid?" returns true, then it will save successfully. That is a false presumption.

    I think rails needs to completely rethink validation and incorporate database constraint detection. And it should go on the assumption that it might return true for "valid?", but not for "save". This is obviously beyond the scope of this ticket, but this is a pretty big problem.

  • oleg dashevskii

    oleg dashevskii July 6th, 2009 @ 08:29 AM

    • Tag changed from 2.3-rc2 to 2.3.x

    I've run into same problem. Test is attached.

  • bterkuile

    bterkuile July 29th, 2009 @ 04:24 PM

    I have the same problems. Shortly said: validations are not taken into account when accept_nested_attributes_for is used. I will give my problem, since I use a dirty trick on the column. I think it is not the cause, but good to add anyway: My 'short' situation:

    class Employee < ActiveRecord::Base
    require 'digest/sha1' attr_protected :id, :salt attr_accessor :password, :password_confirmation

    belongs_to :address accepts_nested_attributes_for :address # Always have an address as employee alias :old_address :address def address

    {mkd-extraction-e5528c92a92dba8e9a7c761cf9e2a0a4}

    end

    def validate_on_create

    {mkd-extraction-97fed57be83556b54a4f5d7e75b7a4a0}

    end ... end

    When a hash containing :address_attributes => {.....} is given to Employee.new, and no password or password_confirmation, it will save => true. Without the :address_attri butes the validations work.

  • Andrew France

    Andrew France October 8th, 2009 @ 05:22 AM

    • Tag changed from 2.3.x to nested attributes, 2.3.x, accepts_nested_attributes_for, validates_uniqueness_of

    No one seems to have said this explicitly but I believe the problem is simply that validates_uniqueness_of cannot work with nested attributes because the validation only checks against saved records. I am currently handling this problem by doing an in-memory uniqueness check during the validation phase on the parent object.

  • Eloy Duran

    Eloy Duran October 8th, 2009 @ 07:17 PM

    @Andrew Thanks for your comment. I'm still thinking about if and how we could fix this as simple as possible, but it might also be handy for people if you could document your workaround in either the NestedAttributes or validates_uniqueness_of docs.

    Care to write such a doc patch and do you think it's a valid approach?

  • Andrew France

    Andrew France October 9th, 2009 @ 01:05 AM

    Hi Eloy, I think my workaround is a bit of a kludge:

    class Author
      has_many :books
    
      # Could easily be made a validation-style class method of course
      validate :validate_unique_books
    
      def validate_unique_books
        validate_uniqueness_of_in_memory(
          books, [:title, :isbn], 'Duplicate book.')
      end
    end
    
    module ActiveRecord
      class Base
        # Validate that the the objects in +collection+ are unique
        # when compared against all their non-blank +attrs+. If not
        # add +message+ to the base errors.
        def validate_uniqueness_of_in_memory(collection, attrs, message)
          hashes = collection.inject({}) do |hash, record|
            key = attrs.map {|a| record.send(a).to_s }.join
            if key.blank? || record.marked_for_destruction?
              key = record.object_id
            end
            hash[key] = record unless hash[key]
            hash
          end
          if collection.length > hashes.length
            self.errors.add_to_base(message)
          end
        end
      end
    end
    

    If this is useful to people I'd be happy to tidy it up and document it somewhere, not really worth making a plugin out of it, I just put it in my initializers. It moves the validation responsibility to the parent object, which is probably bad too, but I don't think there's any way for a new instance to find out about its other unsaved instances.

    Best of luck finding an elegant solution though, its got me stumped!

  • Andrew France

    Andrew France October 9th, 2009 @ 01:11 AM

    OMG Lighthouse is so shit, sorry about the mess.

  • Pratik

    Pratik October 9th, 2009 @ 01:38 AM

    I've fixed the formatting there.

  • Eloy Duran

    Eloy Duran January 8th, 2010 @ 02:45 PM

    • State changed from “incomplete” to “invalid”

    Going to close this ticket now. Though, if people keep running into this, I'd love to see Andrew's solution integrated in ActiveRecord.

  • Hervé Tatche

    Hervé Tatche January 8th, 2010 @ 04:14 PM

    Same problem on rails (2.3.2), validates_uniqueness_of is bypassed.

  • Tamer Salama

    Tamer Salama March 3rd, 2010 @ 06:45 AM

    Yes! People are still running into problem. Why would the status be "invalid" if a validation is not working as it is supposed to be?

  • Eloy Duran

    Eloy Duran March 3rd, 2010 @ 10:17 AM

    Why would the status be "invalid" if a validation is not working as it is supposed to be?

    How about reading the comments? (that's rhetorical btw.)

    It works as it currently should:

    “the problem is simply that validates_uniqueness_of cannot work with nested attributes because the validation only checks against saved records”

    If you have a good solution/patch to your own problem, please share it with us. But your message as is, doesn't add much, sorry.

  • Piotr Mąsior

    Piotr Mąsior April 13th, 2010 @ 08:35 AM

    I run into similar BUT not SAME problem. Fully explained here: http://railsforum.com/viewtopic.php?id=38343 , I attached there quick fix, but I think it is not elegant. Basing on Eloy summary I assume that is not already any "built in" solution, to use validates_uniqueness_of at model which is used like "dictionary" in context of accepts_nested_attributes_for? I think that filter could somehow discover from what context is it fired up and then check given data against DB and decide is @new_record or not.

  • Piotr Mąsior
  • Roel van der Hoorn

    Roel van der Hoorn May 7th, 2010 @ 12:40 PM

    Ran into exactly the same problem. Had a uniqueness constraint on a date of a ProductRate, scoped for a Product, but it wasn't validated. Andrew's patch works like a charm.

  • Ryan Bigg

    Ryan Bigg October 9th, 2010 @ 09:57 PM

    • Tag cleared.

    Automatic cleanup of spam.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Attachments

Pages