У меня сложная модель, поэтому сначала я задам вопрос, а подробности ниже. У меня есть несколько кросс-модельных ассоциаций. Я думаю, что они правильно, но я не могу свернуть резервное копирование, используя сквозные ассоциации.
Примеры:
Expenditure.first.budget => OK
ExpenditureItem.first.expenditure.budget => OK
ExpenditureItem.first.project => OK
ExpenditureItem.first.budget => FAIL:
ExpenditureItem Load (0.5ms) SELECT "expenditure_items".* FROM "expenditure_items" ORDER BY "expenditure_items"."id" ASC LIMIT $1 [["LIMIT", 1]]
NoMethodError: undefined method `klass' for nil:NilClass
from /Users/aximus/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0.rc1/lib/active_record/reflection.rb:790:in `source_reflection'
Сумасшедшая вещь, которую я не понимаю, это то, почему «ExpenditureItem.first.project» работает, но «ExpenditureItem.first.budget» не работает, когда бюджет принадлежит Project?
Я думал добавить сюда несколько дополнительных прямых внешних ключей, но подумал, что это сработает. Сейчас я могу добавить их (budget_id и budget_code_id), но стараюсь этого избежать. Я также могу добавить методы в моделях.
Я надеюсь, что здесь что-то упущено, или кто-то может сказать мне, что я не могу сделать это с ActiveRecord.
У меня также есть еще один слой «Клиент», к которому относятся «BudgetCode» и «Проект», но я полагаю, что это был достаточно сложный вопрос.
Вопрос: почему мои модельные ассоциации не работают так, как задумано?
Вот краткий обзор моих моделей
class BudgetCode < ApplicationRecord
has_many :budget_code_items
has_many :projects
class BudgetCodeItem < ApplicationRecord
belongs_to :budget_code
Так бы это выглядело так:
Бюджетный код № 1
- DevOps
- Разработка
- Тестирование
- UI / UX
Затем вы создаете бюджет:
class Budget < ApplicationRecord
belongs_to :project
belongs_to :budget_code
has_many :budget_items
has_many :expenditure_items, through: :budget_code_item
has_many :expenditures, through: :expenditure_item
class BudgetItem < ApplicationRecord
belongs_to :budget_code_item
belongs_to :budget
Бюджет myapp (Бюджет № 1)
- DevOps: $ 1000
- Разработка $ 2000
- Тестирование $ 100
- UI / UX $ 10 000
Затем у меня есть расходы по этому бюджету:
class Expenditure < ApplicationRecord
belongs_to :vendor
belongs_to :budget
has_one :budget_code, through: :budget
has_one :project, through: :budget
has_one :client, through: :project
has_many :budget_items, through: :budget
has_many :expenditure_items
class ExpenditureItem < ApplicationRecord
before_save :calculate_amount
belongs_to :budget_code_item
belongs_to :expenditure
has_one :budget_code, through: :budget_code_item
has_one :budget, through: :expenditure
has_one :project, through: :budget
UPDATE
Вот моя связанная схема. Я вынул весь пух - это показывает отношения.
create_table "budget_code_items", force: :cascade do |t|
t.bigint "budget_code_id", null: false
t.index ["budget_code_id"], name: "index_budget_code_items_on_budget_code_id"
end
create_table "budget_codes", force: :cascade do |t|
t.bigint "client_id", null: false
t.index ["client_id"], name: "index_budget_codes_on_client_id"
end
create_table "budget_items", force: :cascade do |t|
t.bigint "budget_code_item_id", null: false
t.bigint "budget_id", null: false
t.index ["budget_code_item_id"], name: "index_budget_items_on_budget_code_item_id"
t.index ["budget_id"], name: "index_budget_items_on_budget_id"
end
create_table "budgets", force: :cascade do |t|
t.bigint "project_id", null: false
t.bigint "budget_code_id", null: false
t.index ["budget_code_id"], name: "index_budgets_on_budget_code_id"
t.index ["project_id"], name: "index_budgets_on_project_id"
end
create_table "expenditure_items", force: :cascade do |t|
t.bigint "budget_code_item_id", null: false
t.index ["budget_code_item_id"], name: "index_expenditure_items_on_budget_code_item_id"
t.index ["expenditure_id"], name: "index_expenditure_items_on_expenditure_id"
end
create_table "expenditures", force: :cascade do |t|
t.bigint "budget_id", null: false
t.boolean "flag_active", default: true
t.index ["budget_id"], name: "index_expenditures_on_budget_id"
end