Rails 5 - 多个外键属于同一个表

时间:2017-01-22 22:25:35

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

我有一个属于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:relation&#34; unloading_stations&#34;不存在

嗯,似乎是检测到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:ERROR:constraint&#34; fk_rails_5294e269cc&#34;对于关系&#34;订单&#34;已存在

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

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

附录

这是我的订单模型:

class Order < ApplicationRecord
end

最终问题

最后 - 在错误消息中 - 我希望有两个外键指向同一个表。

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

看起来你还没有创建一个unloading_station模型。

答案 2 :(得分:0)

关于创建以下内容的迁移,我没有遇到任何错误:

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表格如下:

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

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

答案 3 :(得分:0)

我认为你们正在混淆迁移中应该包含的内容以及模型中应该包含的内容(我并不是以贬义的方式来表达这一点,所以请不要这样做。)

我已经整理了一个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

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

订单表迁移如下所示:

# 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: falseoptional: true模型中的Order

为了测试这是否有效,我在数据库种子中创建了一个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访问它。