【问题标题】:Rails 5 - Multiple Foreign keys belongs to the same tableRails 5 - 多个外键属于同一个表
【发布时间】:2017-06-07 09:36:04
【问题描述】:

我有一个属于 LoadingStation 模型的 Order 模型。而LoadingStation会在Order表中被使用两次,所以看起来像:

class CreateLoadingStations < ActiveRecord::Migration[5.0]
  def change
    create_table :loading_stations do |t|
      t.integer :type
      t.string :comp_name1
      t.string :street
      t.string :street_num
      t.string :zip_code
      t.string :city

      t.timestamps
    end
  end
  end



 class CreateOrders < ActiveRecord::Migration[5.0]
      def change
         create_table :orders do |t|
          t.string :status
          t.belongs_to :loading_station, class_name: "LoadingStation", index: true, foreign_key: "loading_station_id"
          t.belongs_to :unloading_station, class_name: "LoadingStation", index: true, foreign_key: "unloading_station_id"

          t.timestamps
        end
      end
    end

当我让运行 rails db:migrate 时,我得到了这个错误: ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: 关系“unloading_stations”不存在

Mmmmmm,似乎是未正确检测到 class_name。两个语句中的 class_name 应该相同,对吗?

让我们检查装载站的型号:

 class LoadingStation < ApplicationRecord
end

好的,我更改 CreateOrders 迁移:

 t.belongs_to :loading_station, class_name: "LoadingStation", index: true, foreign_key: "unloading_station_id"

现在,当我让运行 rails db:migrate 时,我得到了这个错误: ActiveRecord::StatementInvalid: PG::DuplicateObject: 错误:关系“订单”的约束“fk_rails_5294e269cc”已经存在

好的,我了解数据库中的外键似乎相同,数据库拒绝迁移任务。

但是当我定义谁不同的 foreign_key 名称时,当数据库检测到两个相同的时,foreign_key: 选项的意义是什么?

附录:

这是我的订单模型:

class Order < ApplicationRecord
end

最后一个问题

最后——在错误消息中——我想让两个外键指向同一个表。

【问题讨论】:

  • 您的Order 模型看起来如何?你真的定义了unloading_station 关联吗?

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


【解决方案1】:

我将只关注允许您引用加载站表两次的代码

在 Rails 5.1 或更高版本中,您可以这样做:

迁移

class createOrders < ActiveRecord::Migration
   def change
    create_table(:orders) do |t|
        t.references :loading_station, foreign_key: true
        t.references :unloading_station, foreign_key: { to_table: 'loading_stations' }
    end
  end
end

这将创建字段loading_station_idunloading_station_id 并使数据库级引用loading_stations

型号

class Order < ActiveRecord::Base
  belongs_to :loading_station
  belongs_to :unloading_station, class_name: "LoadingStation"
end

class LoadingStation < ActiveRecord::Base
  has_many :load_orders, class_name: "Order", foreign_key: "loading_station_id"
  has_many :unload_orders, class_name: "Order", foreign_key: "unloading_station_id"
end

【讨论】:

    【解决方案2】:

    我认为您混淆了迁移中应该包含的内容和模型中应该包含的内容(我的意思不是贬义,所以请不要这样认为)。

    我已经为你准备了一个 Rails 5 项目来展示我认为你想要实现的目标:

    总之,我相信您希望将装货站和卸货站存储在一个表中,但将它们作为订单属性单独引用。换句话说,您正在寻找单表继承。

    这是我快速构建的:

    # app/models/order.rb
    class Order < ApplicationRecord
      belongs_to :loading_station, optional: true
      belongs_to :unloading_station, optional: true
    end
    
    # app/models/station.rb
    class Station < ApplicationRecord
    end
    
    # app/models/loading_station.rb
    class LoadingStation < Station
      has_many :orders
    end
    
    # app/models/unloading_station.rb
    class UnloadingStation < Station
      has_many :orders
    end
    

    如您所见,LoadingStationUnloadingStation 模型继承了 Station 模型。 Station 模型在数据库中获取一个表。

    # db/migrate/20170826085833_create_orders.rb
    class CreateStations < ActiveRecord::Migration[5.1]
      def change
        create_table :stations do |t|
          t.string :comp_name1
          t.string :street
          t.string :street_num
          t.string :zip_code
          t.string :city
          t.string :type, null: false
          t.timestamps
        end
      end
    end
    

    类型列定义为:string,并保存子类模型的类名,LoadingStationUnloadingStation

    orders 表迁移如下所示:

    # db/migrate/20170826085833_create_orders.rb
    class CreateOrders < ActiveRecord::Migration[5.1]
      def change
        create_table :orders do |t|
          t.belongs_to :loading_station, null: true, index: true
          t.belongs_to :unloading_station, null: true, index: true
          t.string :status
        end
      end
    end
    

    如您所见,它引用了LoadingStation.idUnloadingStation.id。我不确定这些属性是否是强制性的,所以我在列定义中将它们设置为null: false,在Order 模型中将它们设置为optional: true

    为了测试它是否有效,我在数据库种子中创建了一个 LoadingStation、一个 UnloadingStation 和一个 Order:

    # db/seeds.rb
    loading_station_1 = LoadingStation.create!(
      comp_name1: "Loading Station 1",
      street: "Park Ave",
      street_num: "300",
      zip_code: 10001,
      city: "NY"
    )
    
    unloading_station_4 = UnloadingStation.create!(
      comp_name1: "Unloading Station 4",
      street: "Madison Ave",
      street_num: "204",
      zip_code: 10001,
      city: "NY"
    )
    
    Order.create!(
      loading_station: loading_station_1,
      unloading_station: unloading_station_4,
      status: "delivered"
    )
    

    要测试这一切,只需创建数据库、运行迁移并执行种子:

    rails db:create
    rails db:migrate
    rails db:seed
    

    要实时测试结果,请打开rails console

    irb(main):001:0> pp Station.all
    
    Station Load (0.3ms)  SELECT "stations".* FROM "stations"
    [#<LoadingStation:0x007fcb8ac39440
      id: 1,
      comp_name1: "Loading Station 1",
      street: "Park Ave",
      street_num: "300",
      zip_code: "10001",
      city: "NY",
      type: "LoadingStation",
      created_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00,
      updated_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00>,
     #<UnloadingStation:0x007fcb8ac39288
      id: 2,
      comp_name1: "Unloading Station 4",
      street: "Madison Ave",
      street_num: "204",
      zip_code: "10001",
      city: "NY",
      type: "UnloadingStation",
      created_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00,
      updated_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00>]
    
    irb(main):002:0> pp Order.all
    
    Order Load (0.2ms)  SELECT "orders".* FROM "orders"
    [#<Order:0x007fcb8bca2700
      id: 1,
      loading_station_id: 1,
      unloading_station_id: 2,
      status: "delivered">]
    
    irb(main):003:0> order = Order.first
    
    Order Load (0.2ms)  SELECT  "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ?  [["LIMIT", 1]]
    => #<Order id: 1, loading_station_id: 1, unloading_station_id: 2, status:  "delivered">
    
    irb(main):004:0> pp order.loading_station
    
    LoadingStation Load (0.2ms)  SELECT  "stations".* FROM "stations" WHERE "stations"."type" IN ('LoadingStation') AND "stations"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
    #<LoadingStation:0x007fcb8c0e4390
     id: 1,
     comp_name1: "Loading Station 1",
     street: "Park Ave",
     street_num: "300",
     zip_code: "10001",
     city: "NY",
     type: "LoadingStation",
     created_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00,
     updated_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00>
    
    irb(main):005:0> pp order.unloading_station
    
    UnloadingStation Load (0.3ms)  SELECT  "stations".* FROM "stations" WHERE "stations"."type" IN ('UnloadingStation') AND "stations"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
    #<UnloadingStation:0x007fcb8a36a378
     id: 2,
     comp_name1: "Unloading Station 4",
     street: "Madison Ave",
     street_num: "204",
     zip_code: "10001",
     city: "NY",
     type: "UnloadingStation",
     created_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00,
     updated_at: Sat, 26 Aug 2017 09:06:53 UTC +00:00>
    
    irb(main):006:0> pp order.status
    
    "delivered"
    

    我希望这对你有帮助。我已将代码签入github,您可以通过https://github.com/JurgenJocubeit/SO-41796815访问。

    【讨论】:

      【解决方案3】:

      我在使用以下内容创建迁移时没有收到任何错误:

      t.belongs_to :loading_station, class_name: "LoadingStation", index: true, foreign_key: "loading_station_id"
      
      t.belongs_to :unloading_station, class_name: "LoadingStation", index: true, foreign_key: "unloading_station_id"
      

      订单表如下所示:

      Order(id: integer, status: string, loading_station_id: integer, unloading_station_id: integer, created_at: datetime, updated_at: datetime)
      

      所以它有你需要的必要ID!

      【讨论】:

        【解决方案4】:

        您似乎还没有创建 unloading_station 模型。

        【讨论】:

        • 是的,我不想创建额外的模型。我只想让两个外键指向同一个表。我认为 t.belongs_to 之后的语法不正确。
        猜你喜欢
        • 2019-02-18
        • 1970-01-01
        • 1970-01-01
        • 2017-11-10
        • 2013-10-27
        • 1970-01-01
        • 1970-01-01
        • 2020-07-24
        • 1970-01-01
        相关资源
        最近更新 更多