DataMapper – 单表inheritance

有人可以向我解释这里发生了什么吗?

这是一个我放在一起展示你们最新情况的例子:

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 => [#, #, #<Father @id=3 @type=Father @name="Robert" @age=55 @job=>, #<Mother @id=4 @type=Mother @name="Wanda" @age=47 @status=nil @favorite_song=>, #] 

如果我调用Person.get(3).type我得到:

 Person.get(3).type => Father 

Male.all给了我这个:

 Male.all => [#, #, #<Father @id=3 @type=Father @name="Robert" @age=55 @job=>] 

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) => [#, #] 

如果我尝试做类似@person = People.all事情,我会完全按照您的预期获得@person中的所有条目。 但我不能做像@men = @person.all(:type => Male)我得到一个空数组。

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

我会和Sinatra一起使用它。 我想要的是能够例如从数据库中抓取一克中的@people并将它们保存在@people但仍然能够在我的视图中对它们进行排序/过滤以用于各种用途。 我之前做过类似的事情,并且效果很好。 但我想尝试STI,因为它似乎是一种更简洁的方式来处理我的数据。

我也注意到了如果我做了@women = Female.all这样的事情,我会得到所有的女人,但如果我做了像@woman.each do |woman| 我无法在我的视图中访问包含属性(即woman.favorite_song返回任何内容)。

我错过了一些关于它如何工作的东西吗? 我不明白这里发生了什么,任何帮助将不胜感激。

如果您查看正在生成的SQL,它会为您提供有关正在发生的事情的线索(如果您不知道,可以调用DataMapper::setup 之前使用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) 使用您指定的类型Person.all(:type => Male)使用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)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日志):

 #> ~ (0.000069) SELECT "id", "type", "favorite_song" FROM "people" WHERE "id" = 4 ORDER BY "id" 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):

 #> ~ (0.000052) SELECT "id", "type" FROM "people" WHERE "type" IN ('Female', 'Mother', 'Daughter') ORDER BY "id" # 

我不知道我在这里遗失了什么,或者这确实是一个错误。