ruby 根据列值自定义has_one

hrirmatl  于 2023-11-18  发布在  Ruby
关注(0)|答案(4)|浏览(140)

刚接触Rails,想知道是否可以用ActiveRecords来建模这种关系:
我有以下模型(简化):

User:
 - name
 - email

个字符
然后我有一个模型attendee,它与这两个表都相关,但有一个类型列:

| id | join_type | join_id | 
|----|-----------|---------|
|  1 |    user   |    1    |
|  2 |   client  |    1    |


有没有一种方法可以使用has_one关系对活动记录进行建模?
比如说:

class Attendee < ApplicationRecord

    # OBVIOUSLY DOESNT WORK
    has_one :user, ->() { where(join_type: 'user') }, :class_name => 'User'
    has_one :client, ->() { where(join_type: 'client') }, :class_name => 'Client'

end


所以它会生成正确的左连接,所以呢?
谢谢

uemypmqf

uemypmqf1#

使用此结构创建与会者表

class CreateAttendees < ActiveRecord::Migration[7.0]
  def change
    create_table :attendees do |t|
      t.timestamps
      t.string :linked_object_type
      t.bigint :linked_object_id
    end

    # optinal
    add_index :attendees, %i[linked_object_type linked_object_id]
  end
end

个字符
然后,您可以像这样从Attendee示例访问用户/客户端

attendee = Attendee.first
   linked_object = attendee.linked_object # Depends on linked object type it will return the relative model instance


如果要从用户/客户访问与会者,可以按如下方式定义与会者

# frozen_string_literal: true

# This model is used to store user data
class User < ApplicationRecord
  has_many :attendees, as: :linked_object, linked_object_type: 'User' # You can scope it yourself, and define has_one depends on your design. 
end

xu3bshqb

xu3bshqb2#

首先,您将成为has_onebelongs_to这两个可怕命名所造成的典型混淆的受害者。
belongs_to意味着 this models表有一列引用另一个表(或同一个表)。这是您实际需要的。
has_one意味着 * 另一边 * 有一个引用这个表的列。有点像has_many,但它生成不同的方法,并将LIMIT 1附加到查询中。如果它被命名为referenced_by,它将为我们节省很多greif。
我认为您可能正在寻找的是通过连接表与多态关联的间接关联。

class Client
  has_many :attendences
  has_many :meetings, 
    through: :attendences, 
    as: :attendee
end

class User
  has_many :attendences
  has_many :meetings, 
    through: :attendences,
    as: :attendee
end

# I took the liberty of making the naming less confusing
class Attendence
  belongs_to :meeting
  belongs_to :attendee, 
    polymorpic: true
end

# Just an example - this class could be whatever you want
class Meeting
  has_many :attendences
  has_many :attendees, through: :attendences
  has_many :users,     through: :attendences
  has_many :clients,   through: :attendences
end

字符串
其工作方式是attendences表同时具有attendee_id和attendee_type列。后者存储关联项的类名。
通过将polymorphic选项传递给add_reference,可以在迁移中同时创建两个列:

class CreateAttendences < ActiveRecord::Migration[7.0]
  def change
    create_table :attendences do |t|
      t.references :attendee, null: false, polymorphic: true
      t.references :meeting, null: false, foreign_key: true
      t.timestamps
    end
  end
end


如果这实际上是一个好的设计是有争议的,因为你不能使用外键约束来维护数据库级别的引用完整性。
同样的目标可以通过简单地创建两个连接表来实现,即使ActiveRecord不能真正将其视为来自meetings端的单个关联。

j2cgzkjk

j2cgzkjk3#

我的方法是定义一个UserAttendee模型和一个ClientAttendee模型,这两个模型都继承自attendee(=单表继承,STI)。因此,attendee将需要一个type列,该列将在创建子类时自动填充。

# app/models/client_attendee.rb
class ClientAttendee < Attendee
  has_one :client
end

# app/models/user_attendee.rb
class UserAttendee < Attendee
  has_one :user
end

字符串
您仍然可以随意使用Attendee类。您在attendees表中显示join_id字段,这对于has_one关系不是必需的,只有belongs_to关系。UserClient将具有user_attendee_id和client_attendee_id列。

class User < ActiveRecord::Base
  belongs_to :user_attendee
end

class Client < ActiveRecord::Base
  belongs_to :client_attendee
end

exdqitrt

exdqitrt4#

欢迎来到Rails。我想提出一个更简单的解决方案:

class CreateAttendees < ActiveRecord::Migration[7.0]
  def change
    create_table :attendees do |t|
      t.timestamps
      t.integer :user_id
      t.integer :client_id
    end

    #add Indices and Foreign Keys as you wish

    #add_index...
    #add_foreign_key...
  end
end

字符串
这样你就可以使用你最喜欢的数据库系统的所有数据完整性特性,并简化你的rails代码如下:

class Attendee < ApplicationRecord

   belongs_to :user, optional: true
   belongs_to :client, optional: true

   #if you like you can add a getter method:
   def linked_object
     return user || client  
   end
end

class User < ActiveRecord::Base
  has_one :attendee        # or has_many - as your data model requires
  
  # You can even add some data integrity features in rails using this:
  # has_one :attendee, dependent: destroy
end

class Client < ActiveRecord::Base
  has_one :attendee
  #has_one :attendee, dependent: destroy
end


这样的代码更容易阅读和维护。你会很高兴没有出席记录指向无处。

相关问题