2

ここで何が起こっているのか誰か説明してくれませんか?

これは、どうしたのかを示すためにまとめた例です。

class Person
  include DataMapper::Resource
  property :id, Serial
  property :type, Discriminator
  property :name, String
  property :age, Integer
end

class Male < Person
end

class Father < Male
  property :job, String
end

class Son < Male
end

class Female < Person
end

class Mother < Female
  property :favorite_song, String
end

class Daughter < Female
end

DataMapper.auto_upgrade!

電話すると、次のようPerson.allになります。

Person.all
=> [#<Son @id=1 @type=Son @name="Mike" @age=23 @status=nil>, 
#<Son @id=2 @type=Son @name="Carlos" @age=12 @status=nil>, 
#<Father @id=3 @type=Father @name="Robert" @age=55 @job=<not loaded>>, 
#<Mother @id=4 @type=Mother @name="Wanda" @age=47 @status=nil @favorite_song=<not loaded>>, 
#<Daughter @id=5 @type=Daughter @name="Meg" @age=16 @status=nil>]

そして、私が呼び出すと、次のようPerson.get(3).typeになります:

Person.get(3).type
=> Father

そしてMale.all私にこれを与えます:

Male.all
=> [#<Son @id=1 @type=Son @name="Mike" @age=23 @status=nil>, 
#<Son @id=2 @type=Son @name="Carlos" @age=12 @status=nil>, 
#<Father @id=3 @type=Father @name="Robert" @age=55 @job=<not loaded>>]

そしてMale.get(3).typeこれを与える:

Male.get(3).type
=> Father

しかしPerson.all(:type => Male)、空の配列を返します: (?)

Person.all(:type => Male)
=> []

ただし、すべてのタイプ エントリ (=/)Person.all(:type => Son)を返します。Son

Person.all(:type => Son)
=> [#<Son @id=1 @type=Son @name="Mike" @age=23 @status=nil>,
#<Son @id=2 @type=Son @name="Carlos" @age=12 @status=nil>]

@person = People.all@person のすべてのエントリを期待どおりに取得するようなことをしようとすると、. @men = @person.all(:type => Male)しかし、空の配列を取得するようなことはできません。

@men = @people.all( :type => Male)
=> []

私はこれをシナトラと一緒に使用します。私が望むのは、たとえば、DB からすべての人を 1 グラムで取得して保持@peopleできるようにすることですが、ビューでさまざまな用途に合わせて並べ替え/フィルター処理できるようにすることです。以前に関連付けで同様のことを行ったことがありますが、かなりうまくいきました。しかし、自分のデータを処理するより簡潔な方法と思われる STI を試してみたいと思います。

@women = Female.all私は、私がすべての女性を取得するような@woman.each do |woman|ことをすると、ビューに含まれるプロパティにアクセスできない (つまり、woman.favorite_song何も返さない)ことにも気付きました。

これがどのように機能するかについて何か不足していますか?ここで何が起こっているのかまったくわかりません。助けていただければ幸いです。

4

1 に答える 1

10

生成されている SQL を見ると、何が起こっているかの手がかりが得られます (わからない場合は、 を呼び出すDataMapper::Logger.new(STDOUT, :debug) 前にDataMapper::setupこれを行うことができます)。

Person.all単に生成します:

SELECT "id", "type", "name", "age" FROM "people" ORDER BY "id"

あなたが期待するように。

Male.all生成:

SELECT "id", "type", "name", "age" FROM "people"
  WHERE "type" IN ('Male', 'Father', 'Son') ORDER BY "id"

そして以下をPerson.all(:type => Male)生成します:

SELECT "id", "type", "name", "age" FROM "people"
  WHERE "type" = 'Male' ORDER BY "id"

そのMale.allため、Datamapperを使用するINと、適切なすべてのクラスの名前を含む SQL 句を作成することが認識されますが、使用すると、指定した型だけPerson.all(:type => Male)が使用され、サブクラスは使用されません。

を使用してデータベースではなくコレクションをクエリする場合、同様の直接比較が行われている必要があります@people.all(:type => Male)

クエリでタイプのすべてのサブクラスを正しく取得するには、descendantsメソッドを使用できます。

People.all(:type => Male.descendants)次の SQL を生成します。

SELECT "id", "type", "name", "age" FROM "people"
  WHERE "type" IN ('Father', 'Son') ORDER BY "id"

この場合、これはあなたが望むものを返しますが、IN句には が含まれていないことに注意してくださいMale。これはモデルの子孫のみであり、問​​題のサブツリーの親は含まれていません。

「トップ」クラスも取得するには、次を使用できます。

Person.all(:type => Male.descendants.dup << Male)

MaleクラスをIN句に追加します。dupが必要であることに注意してください stack level too deep (SystemStackError)

これはコレクションでも期待どおりに機能します。

@people.all(:type => Male.descendants.dup << Male)

@peopleデータベースにアクセスせずにすべての男性を返します (すべての人が既に含まれていると仮定します)。

このメソッドを使用する場合はdescendants、ドキュメントでは非公開としてマークされていませんが@api semipublic、ソースではマークされているため、Datamapper をアップグレードするときに注意してください。の<<メソッドはプライベートMale.descendants.dup << Male としてマークされているため、ここではさらに警告が適用されます。ソースから Discriminatorまでこの使用法を取得しました。


プロパティがありません

特定のプロパティを取得できないという他の問題は、遅延読み込みに関係しているように見えますが、何が起こっているのか正確にはわかりません。バグかもしれません。

@women = Female.all生成された SQLですべての女性をロードすると、次のようになります。

SELECT "id", "type", "name", "age" FROM "people"
  WHERE "type" IN ('Female', 'Mother', 'Daughter') ORDER BY "id"

そのため、すべてのメンバーが所有する属性のみがフェッチされます。母親のfavorite_songは、最初に参照したときに取得されます。台詞:

puts women.first.inspect
puts women.first.favorite_song
puts women.first.inspect

与えます(欠損値がいつフェッチされたかを示すSQLログを含む):

#<Mother @id=4 @type=Mother @name="Wanda" @age=47 @favorite_song=<not loaded>>
 ~ (0.000069) SELECT "id", "type", "favorite_song" FROM "people" WHERE "id" = 4 ORDER BY "id"
Suspicious minds
#<Mother @id=4 @type=Mother @name="Wanda" @age=47 @favorite_song="Suspicious minds">

ただし、eachブロックで同様のことを行うと、欠落している値を取得するクエリに が含まれずfavorite_song、モデルで値が nil に設定されます。

women.each do |w|
  next unless w.respond_to? :favorite_song
  puts w.inspect
  puts w.favorite_song
  puts w.inspect
end

出力が得られます (ここでも SQL を含みます):

#<Mother @id=4 @type=Mother @name="Wanda" @age=47 @favorite_song=<not loaded>>
 ~ (0.000052) SELECT "id", "type" FROM "people" WHERE "type" IN ('Female', 'Mother', 'Daughter') ORDER BY "id"

#<Mother @id=4 @type=Mother @name="Wanda" @age=47 @favorite_song=nil>

ここで何かが欠けているのか、それとも本当にバグなのかはわかりません。

于 2012-01-15T01:09:04.257 に答える