5

Я не ожидаю, что модель с NULL как внешним ключом будет принадлежать чему угодно!Активная запись has_many генерирует sql с внешним ключом IS NULL

У меня есть следующее приложение для рельсов, моделирование муравьев и муравейников (вдохновлено Jozef).

$ rails -v 
Rails 3.2.8 
$ rails new ant_hill 
$ cd ant_hill 

Создайте муравейник и муравьиные модели. Муравей может принадлежать муравейнику, а муравейник может иметь много муравьев.

$ rails generate model AntHill name:string 
$ rails generate model Ant name:string ant_hill_id:integer 
$ vim app/models/ant.rb 
$ cat app/models/ant.rb 
class Ant < ActiveRecord::Base 
    belongs_to :ant_hill 
end 
$ vim app/models/ant_hill.rb 
$ cat app/models/ant_hill.rb 
class AntHill < ActiveRecord::Base 
    has_many :ants 
end 
$ rake db:migrate 
== CreateAntHills: migrating ================================================= 
-- create_table(:ant_hills) 
    -> 0.0013s 
== CreateAntHills: migrated (0.0016s) ======================================== 

== CreateAnts: migrating ===================================================== 
-- create_table(:ants) 
    -> 0.0035s 
== CreateAnts: migrated (0.0037s) ============================================ 

Запустите следующий код в консоли.

$ rails c 
Loading development environment (Rails 3.2.8) 

Создайте пару муравьев, сохранившихся, которые не принадлежат ни одному муравейнику.

1.9.2-p290 :001 > Ant.create! name: "August" 
=> #<Ant id: 1, name: "August", ant_hill_id: nil, created_at: "2012-09-27 12:01:06", updated_at: "2012-09-27 12:01:06"> 
1.9.2-p290 :002 > Ant.create! name: "Bertil" 
=> #<Ant id: 2, name: "Bertil", ant_hill_id: nil, created_at: "2012-09-27 12:01:13", updated_at: "2012-09-27 12:01:13"> 

Теперь создайте экземпляр муравейника, но пока не сохраняйте его.

1.9.2-p290 :003 > ant_hill = AntHill.new name: "Storkullen" 
=> #<AntHill id: nil, name: "Storkullen", created_at: nil, updated_at: nil> 

Я ожидаю, что этот муравейник не будет иметь муравьев, а это не так.

1.9.2-p290 :004 > ant_hill.ants 
=> [] 

Я все еще ожидаю, что муравейник не будет иметь муравьев, но теперь он имеет два.

1.9.2-p290 :005 > ant_hill.ants.count 
    (0.1ms) SELECT COUNT(*) FROM "ants" WHERE "ants"."ant_hill_id" IS NULL 
=> 2 

То же самое здесь он никогда не должен генерировать запрос, содержащий «IS NULL» при работе с внешними ключами. Я имею в виду, что «belongs_to NULL» не может принадлежать ни к чему, не так ли?

1.9.2-p290 :006 > ant_hill.ants.all 
    Ant Load (0.4ms) SELECT "ants".* FROM "ants" WHERE "ants"."ant_hill_id" IS NULL 
=> [#<Ant id: 1, name: "August", ant_hill_id: nil, created_at: "2012-09-27 12:01:06", updated_at: "2012-09-27 12:01:06">, #<Ant id: 2, name: "Bertil", ant_hill_id: nil, created_at: "2012-09-27 12:01:13", updated_at: "2012-09-27 12:01:13">] 

После того, как он сохраняется, он ведет себя так, как ожидалось.

1.9.2-p290 :007 > ant_hill.save! 
=> true 
1.9.2-p290 :008 > ant_hill.ants.count 
    (0.4ms) SELECT COUNT(*) FROM "ants" WHERE "ants"."ant_hill_id" = 1 
=> 0 
1.9.2-p290 :009 > ant_hill.ants.all 
    Ant Load (0.4ms) SELECT "ants".* FROM "ants" WHERE "ants"."ant_hill_id" = 1 
=> [] 

Любое понимание? Это ожидаемое поведение?

ответ

1

Хотя это кажется противоречивым, я думаю, что это поведение имеет смысл с учетом ваших примеров. Например, возьмите ant_hill.ants.count. Count - это метод запроса ActiveRecord, который попадает в базу данных, и вы, по сути, просите ActiveRecord дать вам всех муравьев, которые не принадлежат муравейнику. Rails просто позволяет вам делать то, что вы не можете сделать, и не жалуетесь на это. Должно ли это быть причиной исключения? Возможно.

Если вы действительно хотите знать, сколько муравьев принадлежит этому объекту ant_hill, вы должны использовать размер. Он запрашивает объект, если он не сохраняется или когда ассоциация уже загружена, и запрашивает базу данных в противном случае.

ant_hill.ants.size 

Один из способов обойти эту странность, чтобы сделать ant_hill_id обязательное поле путем проверки его присутствия.

TL; DR Избегайте использования интерфейса запроса ActiveRecord, если родительский объект не сохраняется в базе данных.

+0

В этом случае я не могу сделать ant_hill_id, потому что мое приложение позволяет муравьям, которые не принадлежат к муравейнику. – ludde

+0

с использованием '.size' должен работать в вашем случае – PinnyM

+1

Чтобы быть ясным, я уже работаю над этой проблемой в своем приложении, и я признаю, что есть много способов обойти это. Я просто не думаю, что это ожидаемое поведение.Когда что-то имеет внешний ключ NULL, это означает, что он не принадлежит ни к чему. Думаю, вы могли бы утверждать, что несохраненный холм муравья ничем не отличается от перспективы базы данных, но на самом деле. – ludde

Смежные вопросы