【问题标题】:Self-Referential Associations in Rails 5Rails 5 中的自引用关联
【发布时间】:2019-01-11 23:22:37
【问题描述】:

我正在尝试在 Rails 5 中设置自引用关联。我有一个视频模型。一个视频可能有一个以前的视频(如电视连续剧)。理想情况下它会像这样;

irb(main):001:0> first_video  = Video.create(url: 'https://youtu.be/W0lhlPdo0mw')
irb(main):002:0> second_video = Video.create(url: 'https://youtu.be/gQhlw6F603o', previous_video: first_video)
irb(main):003:0> second_video.previous_video
=> #<Video id: 1, url: "https://youtu.be/W0lhlPdo0mw">

这是我目前的方法,但 (PG::UndefinedColumn: ERROR: column videos.video_id does not exist) 失败了,所以我不得不传递 id。

型号

class Video < ApplicationRecord
  has_one :previous_video, class_name: 'Video'
end

迁移

class CreateVideo < ActiveRecord::Migration[5.2]
  def change
    create_table :videos do |t|
      t.string :url, null: false
      t.references :previous_video, class: 'Video'

      t.timestamps
    end
  end
end

在 Rails 5 中实现这一目标的最佳实践是什么?为什么上述工作没有按预期工作? 干杯!

【问题讨论】:

  • 错误原因是因为t.references会在db中创建previous_video_id,而不是video_id。如果您查看您的架构文件,您应该能够看到这一点。

标签: ruby-on-rails activerecord ruby-on-rails-5


【解决方案1】:

你需要这样设置关联:

has_one :prev_video, :class_name => 'Video', :foreign_key => 'previous_video'

已经设置好可以调用

@video.prev_video

外键是数据库列,我称为 prev_video 的那个可以随意命名,只要你不使用与 db 列相同的名称(previous_video)。

【讨论】:

    【解决方案2】:

    t.references 用于模型上的belongs_to 关联,以及关联模型上的has_one。下面是我将如何编写模型和迁移来完成你想要的。

    更新(2019-05-31):

    修复了迁移中的外键创建问题,使其使用正确的目标表。还添加了更多信息的参考链接。

    # model
    class Video < ApplicationRecord
      belongs_to(
        # by default, the association name + "_id" will be used for the column name
        :next_video,
        optional: true,
        class_name: 'Video', # will use the table of the specified model
        inverse_of: :previous_video
      )
    
      has_one(
        :previous_video,
        class_name: 'Video', # will use the table of the specified model
        foreign_key: 'next_video_id',
        inverse_of: :next_video
      )
    end
    
    # migration
    class CreateVideo < ActiveRecord::Migration[5.2]
      def change
        create_table(:videos) do |t|
          t.string(:url, null: false)
    
          # This will create a `next_video_id` column. Add/remove any options as you
          # see fit.
          # - index: creates an index on the column
          t.references(:next_video, index: true)
    
          t.timestamps
        end
    
        # A foreign key could have been created when specifying the `references`
        # column inside `create_table`, but it's self referential, and I'm not sure
        # if it would work. So to be safe, the foreign key is created after the
        # table is created.
        add_foreign_key(:videos, :videos, column: :next_video_id)
      end
    end
    

    【讨论】:

    • next_video 会在此处查找名为 next_videos 的表,并且由于它不存在...它会抛出错误?
    • 我更新了我的答案以修复外键创建。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多