お客様に、請求先住所と配送先住所の2つの住所モデルを参照してもらいたい。私が理解しているように、外部キーは_idのようにその名前で決まります。明らかに、(Addressテーブルを参照するために)2つの行にaddress_idという名前を付けることはできません。どうすればいいですか?
create_table :customers do |t|
t.integer :address_id
t.integer :address_id_1 # how do i make this reference addresses table?
# other attributes not shown
end
これは、私とのhas_many関係のように聞こえます。代わりに、customer_idをAddressテーブルに入れてください。
Customer
has_many :addresses
Address
belongs_to :customer
Assoc宣言で外部キーとクラスを提供することもできます
Customer
has_one :address
has_one :other_address, foreign_key => "address_id_2", class_name => "Address"
これは、Rails(最近のように)を初めて使用する人)を混乱させる可能性があります。回答の一部は、マイグレーションとモデルの一部で発生するためです。また、 2つの別個のものをモデル化します。
住所は単一の顧客に属しており、各顧客には多数の住所があります。あなたの場合、これは1つまたは2つの住所ですが、顧客が複数の配送先住所を持つ可能性を考慮することをお勧めします。例として、Amazon.comには3つの配送先住所があります。
これとは別に、各顧客が請求先住所と配送先住所を持っているという事実をモデル化したいと思います。複数の配送先住所を許可する場合は、代わりにdefault配送先住所になる可能性があります。
これを行う方法は次のとおりです。
class CreateCustomers < ActiveRecord::Migration
create_table :customers do |t|
def up
t.references :billing_address
t.references :shipping_address
end
end
end
ここでは、このテーブルに:billing_addressおよび:shipping_addressと呼ばれ、別のテーブルへの参照を保持する2つの列があることを指定しています。 Railsは、実際には「billing_address_id」と「shipping_address_id」という列を作成します。この場合、これらはそれぞれAddressesテーブルの行を参照しますが、マイグレーションではなくモデルで指定します。
class CreateAddresses < ActiveRecord::Migration
create_table :addresses do |t|
def up
t.references :customer
end
end
end
ここでは、別のテーブルを参照する列も作成していますが、末尾の "_id"を省略しています。 Railsは、列名に一致する(複数形について知っている)テーブル 'customers'があることがわかるので、これを処理します。
「_id」をCustomersマイグレーションに追加した理由は、「billing_addresses」または「shipping_addresses」テーブルがないため、列名全体を手動で指定する必要があるためです。
class Customer < ActiveRecord::Base
belongs_to :billing_address, :class_name => 'Address'
belongs_to :shipping_address, :class_name => 'Address'
has_many :addresses
end
ここでは、:billing_addressという名前のCustomerモデルにプロパティを作成し、このプロパティがAddressクラスに関連していることを指定しています。 Railsは、「belongs_to」を確認すると、上記で定義した「billing_address_id」という顧客テーブルの列を探し、その列を使用して外部キーを格納します。次に、配送先住所に対してまったく同じことを行います。
これにより、次のように、顧客モデルのインスタンスを介して、住所モデルの両方のインスタンスである請求先住所と配送先住所にアクセスできます。
@customer.billing_address # Returns an instance of the Address model
@customer.shipping_address.street1 # Returns a string, as you would expect
補足として、この場合、「belongs_to」の命名法は混乱を招きます。アドレスは顧客に属し、逆ではないためです。ただし、直感を無視してください。 'belongs_to'は、外部キーが含まれているものに使用されます。外部キーは、今回の例ではbothモデルです。はぁ!混乱するのはどうですか?
最後に、顧客に多数の住所があることを指定しています。この場合、このプロパティが関連付けられているクラス名を指定する必要はありません。Railsは、一致する名前のモデルがあることを確認するのに十分スマートです: 'Address'、これはこれにより、次のようにして顧客のすべてのアドレスのリストを取得できます。
@customer.addresses
これは、請求先住所か配送先住所かに関係なく、住所モデルのインスタンスの配列を返します。アドレスモデルといえば、次のようになります。
class Address < ActiveRecord::Base
belongs_to :customer
end
ここでは、Customerモデルの「belongs_to」行とまったく同じことを行っていますが、Railsが魔法をかける;プロパティ名( 'customer')を見る)、 「belongs_to」を参照し、このプロパティが同じ名前のモデル(「Customer」)を参照し、一致する列が住所テーブル(「customer_id」)にあると想定しています。
これにより、次のようにアドレスが属する顧客にアクセスできます。
@address.customer # Returns an instance of the Customer model
@address.customer.first_name # Returns a string, as you would expect
私はTobyのおかげでそれを行う方法を理解しました:
class Address < ActiveRecord::Base
has_many :customers
end
class Customer < ActiveRecord::Base
belongs_to :billing_address, :class_name => 'Address', :foreign_key => 'billing_address_id'
belongs_to :shipping_address, :class_name => 'Address', :foreign_key => 'shipping_address_id'
end
Customersテーブルには、shipping_address_id列とbilling_address_id列が含まれています。
これは本質的にhas_two関係です。 this thread も参考になりました。
私は同じ問題を抱えており、これを解決しました:
create_table :customers do |t|
t.integer :address_id, :references => "address"
t.integer :address_id_1, :references => "address"
# other attributes not shown
end
Rails 5.1以降では、次のように実行できます:
create_table(:customers) do |t|
t.references :address, foreign_key: true
t.references :address1, foreign_key: { to_table: 'addresses' }
end
これにより、フィールドaddress_id
およびaddress1_id
が作成され、データベースレベルでaddresses
テーブルが参照されます。
class Customer < ActiveRecord::Base
belongs_to :address
belongs_to :address1, class_name: "Address"
end
class Address < ActiveRecord::Base
has_many :customers,
has_many :other_customers, class_name: "Customer", foreign_key: "address1_id"
end
FactoryBotを使用する場合、ファクトリは次のようになります。
FactoryBot.define do
factory :customer do
address
association :address1, factory: :address
end
end