DataMapper - 单表继承

Don*_*ano 2 ruby single-table-inheritance sinatra ruby-datamapper

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

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

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!
Run Code Online (Sandbox Code Playgroud)

如果我打电话给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>]
Run Code Online (Sandbox Code Playgroud)

如果我打电话给Person.get(3).type我:

Person.get(3).type
=> Father
Run Code Online (Sandbox Code Playgroud)

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>>]
Run Code Online (Sandbox Code Playgroud)

Male.get(3).type给出这个:

Male.get(3).type
=> Father
Run Code Online (Sandbox Code Playgroud)

Person.all(:type => Male)返回一个空数组:(?)

Person.all(:type => Male)
=> []
Run Code Online (Sandbox Code Playgroud)

但是,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>]
Run Code Online (Sandbox Code Playgroud)

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

@men = @people.all( :type => Male)
=> []
Run Code Online (Sandbox Code Playgroud)

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

我也注意到了一些事情,如果我做的事情就像@women = Female.all我得到所有的女人一样,但如果我做的事情就像@woman.each do |woman|我无法访问我的视图中包含属性(即woman.favorite_song什么都不返回).

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

mat*_*att 10

如果您查看正在生成的SQL,它会为您提供有关正在发生的事情的线索(如果您不知道,可以DataMapper::Logger.new(STDOUT, :debug) 调用之前执行此操作DataMapper::setup).

Person.all 简单地生成:

SELECT "id", "type", "name", "age" FROM "people" ORDER BY "id"
Run Code Online (Sandbox Code Playgroud)

正如你所料.

Male.all 产生:

SELECT "id", "type", "name", "age" FROM "people"
  WHERE "type" IN ('Male', 'Father', 'Son') ORDER BY "id"
Run Code Online (Sandbox Code Playgroud)

Person.all(:type => Male)生成:

SELECT "id", "type", "name", "age" FROM "people"
  WHERE "type" = 'Male' ORDER BY "id"
Run Code Online (Sandbox Code Playgroud)

所以,当你使用Male.all的DataMapper知道要创建一个SQL IN包含所有适当类的名称条款,但是当你使用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"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,这将返回您想要的内容,但请注意该IN子句不包含Male,它只是模型的后代,不包括相关子树的父级.

要获得"顶级"课程,您可以使用:

Person.all(:type => Male.descendants.dup << Male)
Run Code Online (Sandbox Code Playgroud)

Male类添加到IN子句中.请注意,这dup是必要的,否则你会得到stack level too deep (SystemStackError).

这也将按预期对集合起作用:

@people.all(:type => Male.descendants.dup << Male)
Run Code Online (Sandbox Code Playgroud)

将返回所有男性而不会访问数据库(假设@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"
Run Code Online (Sandbox Code Playgroud)

所以只提取所有成员拥有的属性.在favorite_song随后的母亲被取出就引用它的第一次.线条:

puts women.first.inspect
puts women.first.favorite_song
puts women.first.inspect
Run Code Online (Sandbox Code Playgroud)

给(包括显示何时获取缺失值的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">
Run Code Online (Sandbox Code Playgroud)

但是,当我在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
Run Code Online (Sandbox Code Playgroud)

给出输出(再次包括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>
Run Code Online (Sandbox Code Playgroud)

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