2013-04-08 49 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