find_or_initialize_by в ассоциации has_many вызывает ошибку дублирования - PullRequest
5 голосов
/ 10 января 2012

Я вижу странную ошибку, так как я перешел с Rails 3.0.11 на 3.1.3. Вот отдельный код для воспроизведения ошибки:

require 'active_record'

ActiveRecord::Base.establish_connection(
  :adapter  => 'mysql2',
  :username => 'root',
  :database => "some_development"
)

class User < ActiveRecord::Base
  has_many :favorites
end

class Favorite < ActiveRecord::Base
  belongs_to :user
end

u = User.create

# f = u.favorites.find_or_create_by_site_id(123)      #=> pass
f = u.favorites.find_or_initialize_by_site_id(123)    #=> fail
f.some_attr = 'foo'
f.save!

u.name = 'bar'
u.save!                # ActiveRecord::RecordNotUnique will be thrown here!
В результате

ActiveRecord::RecordNotUnique попытается INSERT сохранить ту же запись в таблице favorites. (Обратите внимание, что в этом примере пара (user_id, site_id) должна быть уникальной в избранном)

Интересно, что если я использую find_or_create вместо find_or_initialize, исключений не будет.

В трассировке стека, которую я заметил, вызывается autosave_association, не знаю почему, но на самом деле has_many :favorites, :autosave => false вместо has_many :favorites также устраняет ошибку. Поскольку я никогда не заботился о autosave, я даже не уверен, является ли :autosave => false хорошей идеей или нет.

Что я делаю не так, или это ошибка в Rails? Кто-нибудь может дать мне указатель на что посмотреть?

1 Ответ

5 голосов
/ 10 января 2012

Вы пытались не звонить f.save!? u.save! должен сохранять как избранных, так и пользователей.

> f = u.favorites.find_or_initialize_by_site_id(123)

> u.favorites.include?(f)
==> false

> f2 = u.favorites.build(:site_id => 123)

> u.favorites.include?(f2)
==> true

Я думаю, вы обнаружите, что созданный вами новый фаворит f - это отдельный объект. Следовательно, вы будете сохранять f, тогда как в u.favourites есть еще один не сохраненный фаворит. Следовательно, при сохранении u возникает неуникальная ошибка (которая также сохраняет избранное)

Я не уверен, что это ошибка, недавно появившаяся в Rails 3.1. Это может быть преднамеренным.

В Rails 3.0 find_or_initialize_by не заполнял массив

> f = u.favorites.find_or_initialize_by_site_id(123)

> u.favorites
==> []

Похоже на ошибку - см. https://github.com/rails/rails/pull/3610

...