2015-10-02 129 views
1

好的,所以我寫了任何ruby代碼已經有好幾年了,而且我的設計可能不正確。考慮到這一點,我正在編寫一個小工具來通過REST克隆TargetProcess中的項目實體。目標進程的數據模型,允許多種類型的父:子關係:Rails ActiveRecord關係,STI和繼承

project:epic:feature:user_story 
project:feature:user_story 
project:user_story 

然而,所有的實體是從數據結構的角度幾乎相同,因此它似乎是有道理的使用STI和使用模式定義關係和繼承。我創建了一個新的Rails應用程序只用這些模型來驗證我得到的錯誤,當我嘗試史詩與功能相關聯:

ActiveModel::MissingAttributeError: can't write unknown attribute `epic_id` 

下面是型號:

class TargetProcessEntity < ActiveRecord::Base 
end 

class Project < TargetProcessEntity 
    has_many :epics 
    has_many :features 
    has_many :user_stories 
end 

class Project < TargetProcessEntity 
    has_many :epics 
    has_many :features 
end 

class Epic < TargetProcessEntity 
    belongs_to :project 
    has_many :features 
end 

class Feature < TargetProcessEntity 
    belongs_to :project 
    belongs_to :epic 
    has_many :user_stories 
end 

class UserStory < TargetProcessEntity 
    belongs_to :feature 
    belongs_to :project 
end 

下面是模式:

ActiveRecord::Schema.define(version: 20150929122254) do 

    create_table "epics", force: :cascade do |t| 
    t.datetime "created_at",    null: false 
    t.datetime "updated_at",    null: false 
    t.integer "target_process_entity_id" 
    t.integer "project_id" 
    end 

    add_index "epics", ["project_id"], name: "index_epics_on_project_id" 
    add_index "epics", ["target_process_entity_id"], name: "index_epics_on_target_process_entity_id" 

    create_table "features", force: :cascade do |t| 
    t.integer "project_id" 
    t.integer "epic_id" 
    t.integer "target_process_entity_id" 
    end 

    add_index "features", ["epic_id"], name: "index_features_on_epic_id" 
    add_index "features", ["project_id"], name: "index_features_on_project_id" 
    add_index "features", ["target_process_entity_id"], name: "index_features_on_target_process_entity_id" 

    create_table "projects", force: :cascade do |t| 
    t.datetime "created_at",    null: false 
    t.datetime "updated_at",    null: false 
    t.integer "target_process_entity_id" 
    end 

    add_index "projects", ["id"], name: "index_projects_on_id" 
    add_index "projects", ["target_process_entity_id"], name: "index_projects_on_target_process_entity_id" 

    create_table "target_process_entities", force: :cascade do |t| 
    t.string "type",    null: false 
    t.string "name" 
    t.text  "description" 
    t.integer "source_remote_id" 
    t.float "numeric_priority" 
    t.integer "owner" 
    t.datetime "created_at",  null: false 
    t.datetime "updated_at",  null: false 
    t.integer "cloned_remote_id" 
    t.string "resource_type" 
    t.integer "project_id" 
    end 

    create_table "user_stories", force: :cascade do |t| 
    t.integer "project_id" 
    t.integer "feature_id" 
    t.datetime "created_at",    null: false 
    t.datetime "updated_at",    null: false 
    t.integer "target_process_entity_id" 
    end 

    add_index "user_stories", ["feature_id"], name: "index_user_stories_on_feature_id" 
    add_index "user_stories", ["project_id"], name: "index_user_stories_on_project_id" 
    add_index "user_stories", ["target_process_entity_id"], name: "index_user_stories_on_target_process_entity_id" 

end 

雖然史詩和特點都有一個PROJECT_ID,功能的實​​例不具有epic_id屬性;嘗試分配史詩爲特色炸燬:

[20] pry(main)> epic = Epic.new 
=> #<Epic:0x007fcab6c80590 
id: nil, 
type: "Epic", 
name: nil, 
description: nil, 
source_remote_id: nil, 
numeric_priority: nil, 
owner: nil, 
created_at: nil, 
updated_at: nil, 
cloned_remote_id: nil, 
resource_type: "Epic", 
project_id: nil> 
[21] pry(main)> feature = Feature.new 
=> #<Feature:0x007fcab6d3ba48 
id: nil, 
type: "Feature", 
name: nil, 
description: nil, 
source_remote_id: nil, 
numeric_priority: nil, 
owner: nil, 
created_at: nil, 
updated_at: nil, 
cloned_remote_id: nil, 
resource_type: "Feature", 
project_id: nil> 
[22] pry(main)> epic.save 
    (0.1ms) begin transaction 
    SQL (0.3ms) INSERT INTO "target_process_entities" ("type", "resource_type", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["type", "Epic"], ["resource_type", "Epic"], ["created_at", "2015-10-02 15:18:13.351578"], ["updated_at", "2015-10-02 15:18:13.351578"]] 
    (4.6ms) commit transaction 
=> true 
[23] pry(main)> feature.epic = epic 
ActiveModel::MissingAttributeError: can't write unknown attribute `epic_id` 
from /Users/kcallahan/.rbenv/versions/2.0.0-p647/lib/ruby/gems/2.0.0/gems/activerecord-4.2.4/lib/active_record/attribute.rb:138:in `with_value_from_database' 
[24] pry(main)> 

我意識到這是極有可能,我要麼做錯事或已經做出了糟糕的設計決定;任何輸入都非常感謝,因爲我一直無法找到任何東西,並且幾天來一直在抨擊我的頭!

+0

我注意到target_process_entities.project_id可能會造成混亂的源頭是隱含的;這將映射到目標進程項目標識,而不是本地標識。改變了這一點,並突然分配項目打破。再次更改了模式以在主表上具有所有查找ID並且它可以工作:/ –

回答

0

好吧,我幾乎是錯誤地工作了!我將xxx_id列添加到了target_process_entities表。我認爲STI表格能夠對關係定義做出反應,儘管我對STI和關係的內部工作原理的理解充其量是生鏽的和不完整的...

+0

新模式: 'create_table「target_process_entities」,force::cascade do | t | t.string 「類型」,空:假 t.string 「名」 t.text 「說明」 t.integer 「source_remote_id」 t.float 「numeric_priority」 t.integer 「所有者」 t.datetime「 created_at 「空:假 t.datetime 」的updated_at「,空:假 t.integer 」cloned_remote_id「 t.string 」RESOURCE_TYPE「 t.integer 」target_process_project_id「 t.integer 」PROJECT_ID「 t.integer」 epic_id「 t.integer」feature_id「 t。整數「user_story_id」 end' –

-1

我可能是錯的,但它看起來像您的功能表是Project和Epic之間多對多關係的連接表。

如果是這樣的話,你的模型可能看起來像這樣

class Project < TargetProcessEntity 
    has_many :features 
    has_many :epics, through: :features 
end 

class Epic < TargetProcessEntity 
    has_many :features 
    has_many :projects, through: :features 
end 

class Feature < TargetProcessEntity 
    belongs_to :project 
    belongs_to :epic 
    has_many :user_stories 
end 

如果使用相同的名稱

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

+0

有趣的;我沒有這樣想過。一個史詩雖然只能屬於一個單一的項目。另外,雖然一個特徵必須屬於一個項目,但如果該層次已經建立,它也可能屬於一個史詩。一對多項目:Epics是否會通過功能取消多對多的資格? –

+0

我想如果你拿出has_many:項目,通過:: features,並用Epic中的belongs_to:項目代替,應該沒問題。你只是不會通過功能訪問史詩,因爲它有它自己的史詩關係 – kswang2400