Комплексная Вложенная Ассоциация has_many / has_one - PullRequest
0 голосов
/ 29 июня 2019

У меня сложная модель, поэтому сначала я задам вопрос, а подробности ниже. У меня есть несколько кросс-модельных ассоциаций. Я думаю, что они правильно, но я не могу свернуть резервное копирование, используя сквозные ассоциации.

Примеры:

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

  1. DevOps
  2. Разработка
  3. Тестирование
  4. 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)

  1. DevOps: $ 1000
  2. Разработка $ 2000
  3. Тестирование $ 100
  4. 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...