2013-04-08 47 views
0

我无法轻松地找到相关主题,所以在这里我们去 - 一个经典问题:我有一个用户或人模型,我想(< 8小时,〜8小时,> 8小时),吸烟,日常暴晒等,以模拟该人的身体特性/属性(眼睛颜色,头发颜色,肤色,性别,某些生活习惯属性,如睡眠时间,夜间暴露等等)模型人(用户)配置文件属性/属性 - 最佳实践,静态与动态属性

我通常通过为每个属性创建单独的导轨模型(数据库表)来解决该问题,因为之后很容易在稍后添加更多选项,编辑它们,将其用作<select>的源,即

class Person 
    belongs_to :eye_color 
    belongs_to :skin_color 
    belongs_to :hair_color 
    belongs_to :sleep_per_night 
    belongs_to :sun_exposure 

    attr_accessible :gender # boolean, m/f 
end 

class HairColor 
    has_many :people 

    attr_accessible :value 
end 

class EyeColor 
    has_many :people 

    attr_accessible :value 
end 

Person.last.eye_color 
... 
EyeColor.first.people 
class Person 
    belongs_to :eye_color 
    belongs_to :skin_color 
    belongs_to :hair_color 
    belongs_to :sleep_per_night 
    belongs_to :sun_exposure 

    attr_accessible :gender # boolean, m/f 
end 

class HairColor 
    has_many :people 

    attr_accessible :value 
end 

class EyeColor 
    has_many :people 

    attr_accessible :value 
end 

Person.last.eye_color 
... 
EyeColor.first.people 

但是,如果有很多这些属性(即10-15种不同的生态属性)。对我来说,似乎违反了DRY规则,也就是我留下了许多小表,如eye_colors,其中有3-5个记录。每个表只有一个有意义的列值。

,我想你们是如何解决这些问题,也许通过创建一个单一的模式,即PersonProperty具有以下结构

person_properties[type, value] 

所以用独立的模型以前的解决方案,即对eye_color和hair_color会看这样的(类型/类和值):

# PersonProperty/person_properties: 
1. type: 'HairColor', value: 'red' 
2. type: 'HairColor', value: 'blond' 
3. type: 'SkinColor', value: 'white' 
4. type: 'EyeColor', value: 'green' 
5. type: 'HairColor', value: 'black' 
6. type: 'SkinColor', value: 'yellow' 
7. type: 'SleepPerNight', value: 'less than 8h' 
8. type: 'SleepPerNight', value: 'more than 8h' 
9. type: 'DailySunExposure', value: 'more than 1h' 
... 
19. type: 'EyeColor', value: 'blue' 
... 

上面的例子可以是通过分割PersonProperty模型分成两个归一化的。或者,也许你建议别的?

回答

2

我不会在这种情况下建议has_one关系。用户可以belongs_to :eye_color,因此您可以在用户中映射眼线。 EyeColor has_many :users,以便您可以执行@eye_color.users并获取具有特定EyeColor的所有用户。否则,你将不得不为每个用户(或至少有眼睛)创建一个EyeColor。

我建议通过您的PersonProperty解决方案的原因是因为它更容易维护,并且因为将这些类型的关系委托给您的数据库所带来的性能收益。

UPDATE:如果动态属性是你想要什么,我建议设置你的模型是这样的:

class Person < ActiveRecord::Base 
    has_many :person_attributes 

    attr_accessible :gender 
    accepts_nested_attributes_for :person_attributes 
end 

class PersonAttribute < ActiveRecord::Base 
    belongs_to :person_attribute_type 
    belongs_to :person_attribute_value 
    belongs_to :person 

    attr_accessible :person_id, :person_attribute_value_id 
end 

class PersonAttributeValue < ActiveRecord::Base 
    has_many :person_attributes 
    belongs_to :person_attribute_type 

    attr_accessible :value, :person_attribute_type_id 
end 

class PersonAttributeType < ActiveRecord::Base 
    has_many :person_attribute_values 

    attr_accessible :name, :type 
end 

这样你就可以做到以下几点:

@person_attribute_type = PersonAttributeType.create(:name => 'Eye color', :type => 'string') 

['green', 'blue', 'brown'].each do |color| 
    @person_attribute_type.person_attribute.values.build(:value => color) 
end 

@person_attribute_type.save 

@person = Person.new 
@person_attribute = @person.person_attributes.build 
@person_attribute.person_attribute_value = @person_attribute_type.person_attribute_values.find(:value => 'green') 

中当然,你可能不会通过命令行来填充你的数据库。你可能会为如何做到这一点的形式工作很好奇:

class PersonController 
    # ... 
    def new 
    @person = Person.new 
    PersonAttributeType.all.each do |type| 
     @person.person_attributes.build(:person_attribute_type = type) 
    end 
    end 

    def create 
    @person = Person.new(params[:person]) 
    if @person.save 
     # ... 
    else 
     # ... 
    end 
    end 

    def edit 
    @person = Person.find(params[:id]) 
    PersonAttributeType.where('id NOT IN (?)', @person.person_attributes.map(&:person_attribute_type_id)).each do |type| 
     @person.person_attributes.build(:person_attribute_type = type) 
    end 
    end 
    # ... 

现在的形式,基于Formtastic:

semantic_form_for @person do |f| 
    f.input :gender, :as => :select, :collection => ['Male', 'Female'] 
    f.semantic_fields_for :person_attributes do |paf| 
    f.input :person_attribute_value, :as => :select, :collection => paf.object.person_attribute_type.person_attributes_values, :label => paf.object.person_attribute_type.name 
    end 
    f.buttons 
end 

记住,这一切都是未经检验的,所以只是尝试了解我在这里要做的事情。

BTW,我现在认识到的类名的人属性可能是有点不走运,因为你将不得不accepts_nested_attributes_for :person_attributes这意味着你将不得不attr_accessible :person_attributes_attributes,但你明白我的意思,我希望。

+0

当然,我的错误。我已将has_one更改为belongs_to。 – januszm 2013-04-08 14:21:57

+0

你也可以考虑像你所建议的动态属性。不过,如果你这样做,我会考虑为你的'property_types'和''property_values'添加一个模型。所以不仅仅是一个'PersonProperty'表。最重要的收获是您将能够动态创建属性,而无需编写任何代码。 – 2013-04-08 14:24:30

+0

您能否提供一些简单的代码示例,让我们来说说这两个表将如何与人员表和一些示例记录相关?我担心这种方法在性能方面可能效率不高,但我会对如何实现它感兴趣。 – januszm 2013-04-08 16:50:14