我希望能够在一个表中使用两列来定义关系.以任务应用程序为例.

Attempt 1:

class User < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :owner, class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end

那么Task.create(owner_id:1, assignee_id: 2)

这允许我执行Task.first.owner,返回user oneTask.first.assignee,返回user two,但User.first.task不返回任何内容.这是因为任务不属于用户,它们属于ownerassignee.所以

Attempt 2:

class User < ActiveRecord::Base
  has_many :tasks, foreign_key: [:owner_id, :assignee_id]
end

class Task < ActiveRecord::Base
  belongs_to :user
end

这完全失败了,因为两个外键似乎不受支持.

所以我想要的是能够说User.tasks,并获得用户拥有和分配的任务.

基本上是建立一种关系,相当于Task.where(owner_id || assignee_id == 1)个查询

可能吗?

Update

我不打算使用finder_sql,但这个问题的答案似乎接近我想要的:Rails - Multiple Index Key Association

所以这个方法看起来是这样的,

Attempt 3:

class Task < ActiveRecord::Base
  def self.by_person(person)
    where("assignee_id => :person_id OR owner_id => :person_id", :person_id => person.id
  end 
end

class Person < ActiveRecord::Base

  def tasks
    Task.by_person(self)
  end 
end

虽然我可以在Rails 4分钟内让它工作,但我一直得到以下错误:

ActiveRecord::PreparedStatementInvalid: missing value for :owner_id in :donor_id => :person_id OR assignee_id => :person_id

推荐答案

TL;DR

class User < ActiveRecord::Base
  def tasks
    Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
  end
end

User级移除has_many :tasks个.


使用has_many :tasks毫无意义,因为在表tasks中没有任何名为user_id的列.

我为解决这个问题所做的是:

class User < ActiveRecord::Base
  has_many :owned_tasks,    class_name: "Task", foreign_key: "owner_id"
  has_many :assigned_tasks, class_name: "Task", foreign_key: "assignee_id"
end

class Task < ActiveRecord::Base
  belongs_to :owner,    class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
  # Mentioning `foreign_keys` is not necessary in this class, since
  # we've already mentioned `belongs_to :owner`, and Rails will anticipate
  # foreign_keys automatically. Thanks to @jeffdill2 for mentioning this thing 
  # in the comment.
end

这样,你既可以拨打User.first.assigned_tasks,也可以拨打User.first.owned_tasks.

现在,您可以定义一个名为tasks的方法,该方法返回assigned_tasksowned_tasks的组合.

就可读性而言,这可能是一个很好的解决方案,但从性能Angular 来看,它不会像现在这样好,为了获得tasks,将发出两个查询,而不是一次,然后,这两个查询的结果也需要合并.

因此,为了获得属于用户的任务,我们将以以下方式在User类中定义一个自定义tasks方法:

def tasks
  Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
end

这样,它将在一个查询中获取所有结果,而我们不必合并或组合任何结果.

Ruby-on-rails相关问答推荐

Rails 6升级没有加载Mongoid,这是一个问题

RSpec系统测试无法从工厂访问新创建的记录

如何从 gem 覆盖 JSONAPI.configure

Ruby on Rails 更新触发了 Mailer 函数中的 ArgumentError

如何保持 en.yml DRY?

如何启动 rails 控制台并专门使用测试数据库?

Rails 3 中图像路径的完整 url

如何更改 Heroku 应用程序的 DATABASE_URL

在 Windows 上使用 Ruby 进行开发

Rails 3:如何正确显示textarea中的文本?

水豚 click_link 与 :href 匹配

如何将 yaml 文件解析为 ruby​​ 哈希和/或数组?

机械师 vs FactoryGirl - 优点和缺点

在 Controller 中调用模型方法

Rails 引擎中的迁移?

如何让 ActiveAdmin 使用强参数?

ActiveRecord 事务中的错误处理?

:javascript haml 标签中的内联Ruby ?

Sidekiq 在 docker-compose 上的 127.0.0.1:6379 (Errno::ECONNREFUSED) 上连接到 Redis 时出错

ActiveRecord 查找以