Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

has_one + validation Object#create_something bug

#516

Odd one I found today:


class Chicken
  has_one :head
end

class Head
  belongs_to :chicken
  validates_uniqueness_of :chicken_id
end


>> @chicken = Chicken.first
>> @chicken.create_head 
=> #<Head id: 10, chicken_id: 1>

# second create should trigger validation error, or at least do nothing. Instead we get this:

>> @chicken.create_head 
=> #<Head id: nil, chicken_id: 1>

For some reason second call to create_head generates SQL similar to this:

SELECT `chicken_id` FROM `heads` WHERE (`heads`.chicken_id = 1)

SELECT `chicken_id` FROM `heads` WHERE (`heads`.chicken_id IS NULL AND `heads`.id <> 10)

UPDATE `heads` SET `chicken_id` = NULL, WHERE `id` = 10

Doesn't make much sense to me

Reported by Oleg · June 30th, 2008 @ 08:11 PM

State: stale
Milestone: 2.x
Assigned to: nobody
Importance: none

Activity

  1. nikz
    nikz

    Hey Oleg,

    It's not to do with scoping at all is it?

    What does your create_head method look like?

    I've run into this one previously I think...

    Cheers,

    Nik

    June 30th, 2008 @ 11:39 PM

  2. Oleg
    Oleg

    create_head is the Rails built-in method. See: http://rails-doc.org/docs/Active...

    I have a feeling that something gets messed due to the validation method. I might dig a bit deeper later, but for now I just threw this bug here.

    In any case create_* method should not generate UPDATE sql. That makes no sense.

    It's Rails 2.1 I didn't see a mention of something like that in tickets so I assume edge has the same issue, probably.

    July 1st, 2008 @ 12:45 AM

  3. nikz
    nikz

    Hey Oleg,

    Ah right :) Nope, I don't think it's to do with the bug I ran into before upon closer inspection.

    I'm still playing with it (as the fact that you get back an unsaved Head is totally a bug).

    The UPDATE query is just to preserve the integrity of the has_one - i.e it's de-associating the old Head. But it looks like it's not saving the new Head.

    Hope that helps :)

    July 5th, 2008 @ 05:15 AM

  4. nikz
    nikz

    It looks like Rails isn't saving the second head - the query:

    SELECT `chicken_id` FROM `heads` WHERE (`heads`.chicken_id IS NULL AND `heads`.id <> 10) 
    

    is the validates_uniqueness_of failing :) If you comment that validation out, you'll see that Rails does two INSERTs.

    I guess the bug here is that it null-ifys the old Head, even though the new one has failed validation.

    The docs say create_whatever should work like;

    h = Head.new("chicken_id" => id); h.save; h
    

    And it is - returning an un-saved Head.

    So, I'll write up a patch to make sure it only runs HasOneAssociation#replace if the validations pass? Does that make sense to everyone?

    July 5th, 2008 @ 05:37 AM

  5. nikz
    nikz
    • Tag changed from activerecord to activerecord, edge, patch, tested

    Patch against edge attached :)

    July 6th, 2008 @ 05:12 AM

  6. josh
    josh
    • State changed from new to stale

    Staling out, please reopen if this is still a problem.

    October 28th, 2008 @ 04:21 PM

  7. Jack Neto
    Jack Neto

    Hey guys,

    It's been a while since this bug showed it's ugly face.
    I'm using Rails 2.3.5 and I still have the same problem.

    January 18th, 2010 @ 07:54 PM

  8. bingbing