【问题标题】:Using both has_one and has_many association同时使用 has_one 和 has_many 关联
【发布时间】:2016-06-22 15:03:56
【问题描述】:

我正在尝试在一个模型中使用 has_one 和 has_many 方法。一个任务流可以有很多任务,但它也有一个默认任务。

我正在尝试在任务流表中创建一个包含任务 ID 的列。但是,当我尝试设置该默认任务时,它不起作用。

class Taskflow < ActiveRecord::Base
    has_many :tasks
    has_one :default_task, :class_name => 'Task'

class Task < ActiveRecord::Base
    belongs_to :taskflow

我为数据库播种,然后在 Rails 控制台中尝试将任务分配为任务流 default_task:

taskflow1 = Taskflow.first
task1 = Task.first
taskflow1.default_task = task1

这不适用于任务流 default_task 值保持为“nil”。实现所需行为的正确方法是什么?

任何帮助将不胜感激。

编辑

迁移文件是:

class CreateTaskflows < ActiveRecord::Migration
  def change
    create_table :taskflows do |t|
      t.string :title
      t.string :description
      t.references :default_task
      t.timestamps null: false
    end
  end
end

class CreateTasks < ActiveRecord::Migration
  def change
    create_table :tasks do |t|
      t.string :task_type
      t.text :help
      t.text :data
      t.belongs_to :taskflow
      t.timestamps null: false
    end
  end
end

【问题讨论】:

  • 首先,您似乎从未设置任务,因此“taskflow1.default_task = task”会将 default_task 设置为 nil,因为从未设置任务。是否所有 TaskFlow 都具有相同的默认任务,还是在 TaskFlow 实例之间发生变化?
  • 抱歉打错了。任务流实例之间的默认任务可能不同。非常感谢。

标签: ruby-on-rails ruby-on-rails-4 associations


【解决方案1】:

我会以不同的方式实现它。我会在 Task 模型上创建一个布尔字段 default_task。 Taskflow 将具有以下 has_one 和 has_many 关联:

class Taskflow < ActiveRecord::Base
    has_many :tasks
    has_one :default_task, class_name: 'Task', condition: proc{"tasks.default_task = true"}

从语义上讲,Taskflow 应该有许多任务,并且在这些任务中只有一个默认任务。在我看来,任务流属于默认任务听起来有点人为。

您还可以添加一项功能以将单个任务作为默认任务持续维护(将位设置为 true,更多详细信息 here),或在创建任务流时创建默认任务。这完全取决于您的要求。

并且,如果您仍然希望将 default_task_id 列放在您的任务流模型上并使用 has_one 关联,您可以执行以下操作:

class Taskflow < ActiveRecord::Base
    has_many :tasks
    has_one :default_task, class_name: "Task", primary_key: "default_task_id", foreign_key: "id"

【讨论】:

    【解决方案2】:

    has_one 用于指定与另一个类的一对一关联。仅当其他类包含外键时才应使用此方法。

    在您的情况下,您应该使用 belong_to,因为 default_task 引用在 TaskFlow 模型中(在您的迁移中)。

    TaskFlow 模型有两种关系:

    • 一个任务流有很多任务
    • TaskFlow 有一个特定任务,它是默认任务。

    class Taskflow < ActiveRecord::Base has_many :tasks belongs_to :default_task, :class_name => 'Task'

    class Task < ActiveRecord::Base belongs_to :taskflow

    按照你的方式,Rails 不会知道哪个任务是默认任务。

    【讨论】:

      【解决方案3】:

      这里的人对belongs_to 或如何破解has_one 的看法是正确的。我知道belongs_to 在这种情况下感觉不对,但是如果您在表中使用“引用”,您通常总是希望使用belongs_to。因此,我假设将任务本身的默认任务标记为默认任务 - 但如果它们可以是不同任务流的一部分,其中不同的任务可能是默认任务 - 那么这当然是不可能的。

      这里已经提到的另一个选项是:您可以将范围添加到 has_many :tasks 关系。像这样:

      has_many :tasks do
        def default
          joins(:task_flow).where('tasks.id = task_flows.default_task_id').first
        end
      end
      

      那你就可以申请了

      @task_flow.tasks.default
      

      【讨论】:

      • 我看不出它与范围有什么不同(只是不太清楚),但是,Rails 非常好,因为您可以用多种方式表达事物以使其清晰和合适针对特定情况。顺便说一句,关于 has_one “hack”,它不是 hack。 has_onehas_many 中的 condition 参数是自定义关联的定义明确且有用的行为。
      • 我称其为“hack”,因为它与惯例相矛盾。我认为您至少可以同意,您已经配置了很多,以使 has_one 在此处的数据模型中工作。编辑 - PS:我刚刚查看了您当前的答案。并且通过布尔值标记它并不像黑客。但是重新配置 primary_key 和 foreign_key 来完成几乎与约定相反的工作对我来说确实感觉像是“黑客”^^
      • 好吧,我称之为“hack”,即使它并没有真正反映所需的设计,也只是为了让某件事情发挥作用而完成。让我们以我们当前的案例为例。 RobotEyes 使用belongs_to 作为Taskflow 中的default_task,同时Taskflow has_many 任务。甚至听起来很人为:一个任务流有很多任务并且属于一个默认任务。更好的版本是:一个任务流有很多任务,其中一个是默认任务(我仍然认为任务上的布尔值会是一个更好的解决方案,因为它允许您以后拥有多个默认任务)。
      • belongs_to 只是因为它开始这样工作而被使用,但它真的反映了设计理念吗? Rails 相对于其他框架的巨大优势之一是它非常具有表现力,它允许您在代码中反映各种建模。 “有一个”,“有很多”等有非常严格的定义,如果某物应该是“有一个”,则不应将其转换为“属于”以使其工作。我使用的“primary_key”和“foreign_key”属性是在 Rails 中特意设计的,以反映这种情况,它们是明确定义的 Rails 约定。
      【解决方案4】:

      我得到了这个工作。模型中需要belongs_to,而不是has_one

      class Taskflow < ActiveRecord::Base
          has_many :tasks
          belongs_to :default_task, :class_name => 'Task'
      
      class Task < ActiveRecord::Base
          belongs_to :taskflow
      

      我认为 has_one 与我的假设有相反的关系。 http://guides.rubyonrails.org/association_basics.html#the-has-one-association

      【讨论】:

      • 通过使用belongs_to,你暗示一个任务有一个/多个任务流,即使你没有明确说明这样的关联。这是否准确反映了您的要求?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多