2015-10-02 2 views
1

ОК, так прошло много лет с тех пор, как я написал любой код ruby, и мой дизайн может быть неправильным. Имея это в виду, я пишу небольшую утилиту для клонирования объектов проекта в TargetProcess через REST. Целевой процесс имеет модель данных, которая позволяет в течение нескольких типов родителей: детские отношения:Rails ActiveRecord отношения, STI и наследование

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

Однако все объекты практически идентичны с структурой данных точки зрения, так что казалось, имеет смысл использовать STI и использовать модели для определения отношений и наследования. Я создал новое приложение Rails только с этими моделями для проверки ошибки я получаю, когда я пытаюсь связать Эпос с Feature:

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, экземпляр Feature не имеет 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 может сбивать с толку; это сопоставляется идентификатору проекта целевого процесса, а не локальному. Изменено это, и внезапно назначаемые проекты сломались. Изменена схема снова, чтобы иметь все идентификаторы поиска в главной таблице, и она работает:/ –

ответ

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.integer "user_story_id" end' –

-1

Возможно, я ошибаюсь, но похоже, что ваша функция table - таблица соединений для многих-многих отношений между 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

Interesting; Я так не думал об этом. Эпический, хотя может принадлежать только одному проекту. Кроме того, хотя функция должна принадлежать проекту, она также может принадлежать Epic, если этот уровень иерархии был настроен. Будет ли один-ко-многим проектом: Epics дисквалифицировать многие-ко-многим через функции? –

+0

Я думаю, что если вы выберете has_many: projects, через:: функции и замените на role_to:: project от Epic, что должно быть хорошо. вы просто не получили бы доступ к эпикам через функции, потому что у него есть свои отношения к эпопеям – kswang2400