有人可以向我解释这里发生了什么吗?
这是一个例子,我把它们放在一起显示你们的最新情况:
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)
=> []
我会和Sinatra一起使用它。我想要的是能够例如从数据库中抓取一克中的所有人并将其保存在@people
中,但仍能够在我的视图中对它们进行排序/过滤以用于各种用途。我之前做过类似的事情,并且工作得很好。但我想尝试STI,因为它似乎是一种更简洁的方式来处理我的数据。
我已经注意到,如果我做@women = Female.all
这样的事情,我会得到所有的女人,但如果我做@woman.each do |woman|
之类的事情,我无法在我的观点中访问包含属性(即woman.favorite_song
不返回任何内容。
我错过了一些关于它是如何工作的东西吗?我完全不明白这里发生了什么,我们将不胜感激。
答案 0 :(得分: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知道创建包含所有相应类的名称的SQL IN
子句时,但是当您使用Person.all(:type => Male)
时使用只是您指定的类型,没有子类。
当您使用@people.all(:type => Male)
查询集合而不是数据库时,必须进行类似的直接比较。
为了在查询中正确获取某个类型的所有子类,您可以使用descendants
method。
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
中的<<
method 标记为私有,因此警告在此处应用更多。我从source to 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>
我不知道我在这里遗失了什么,或者这确实是一个错误。