Plusieurs clés étrangères référençant la même table dans RoR

Je veux Qu'un client fasse référence à deux modèles D'adresse, un pour l'adresse de facturation et un pour l'adresse d'expédition. Si je comprends bien, la clé étrangère est déterminée par son nom, comme _id. Évidemment, je ne peux pas nommer deux lignes address_id (pour référencer la table D'adresses). Comment puis-je faire?

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
27
demandé sur titaniumdecoy 2010-01-30 05:39:46

4 réponses

Cela ressemble à une relation has_many pour moi-mettez le customer_id dans la table D'adresses à la place.

Customer
  has_many :addresses

Address
  belongs_to :customer

Vous pouvez également fournir une clé étrangère et une classe dans la déclaration assoc

Customer
   has_one :address
   has_one :other_address, foreign_key => "address_id_2", class_name => "Address"
25
répondu Toby Hede 2010-01-30 02:42:20

Cela peut être un peu déroutant pour les nouveaux utilisateurs de Rails (comme je l'étais récemment), car certaines parties de la réponse ont lieu dans vos Migrations et d'autres dans vos modèles. En outre, vous voulez réellement modéliser deux choses distinctes:

  1. Une adresse appartient à un seul client et chaque client dispose de nombreuses adresses. Dans votre cas, ce serait 1 ou 2 adresses, mais je vous encourage à envisager la possibilité qu'un client peut avoir plus d'une adresse de livraison. Comme un exemple, j'ai 3 adresses de livraison distinctes avec Amazon.com.

  2. Séparément, nous voulons modéliser le fait que chaque client a une adresse de facturation et une adresse de livraison, qui pourrait plutôt être l'adresse de livraison par défaut Si vous autorisez plus d'une adresse de livraison.

Voici comment vous feriez cela:

Migrations

class CreateCustomers < ActiveRecord::Migration
  create_table :customers do |t|
    def up
      t.references :billing_address
      t.references :shipping_address
    end
  end
end

Ici, vous spécifiez qu'il y a deux colonnes dans ce tableau qui seront appelées : billing_address et: shipping_address et qui contiennent des références à une autre table. Rails va réellement créer des colonnes appelées 'billing_address_id' et 'shipping_address_id' pour vous. Dans notre cas, ils référenceront chacun des lignes dans la table des adresses, mais nous le spécifions dans les Modèles, pas dans les migrations.

class CreateAddresses < ActiveRecord::Migration
  create_table :addresses do |t|
    def up
      t.references :customer
    end
  end
end

Ici, vous créez également une colonne qui fait référence à une autre table, mais vous omettez le " _id " à la fin. Rails prendra soin de cela pour vous car il voit que vous avoir une table, 'customers', qui correspond au nom de la colonne (il connaît la pluralité).

La raison pour laquelle nous avons ajouté "_id" à la migration des clients est que nous n'avons pas de table "billing_addresses" ou "shipping_addresses", nous devons donc spécifier manuellement le nom de la colonne entière.

Modèles

class Customer < ActiveRecord::Base
  belongs_to :billing_address, :class_name => 'Address'
  belongs_to :shipping_address, :class_name => 'Address'
  has_many :addresses
end

Ici, vous créez une propriété sur le modèle client nommée: billing_address, puis spécifiez que cette propriété est liée à la classe Address. Rails, voyant le 'belongs_to', recherchera une colonne dans la table customers appelée 'billing_address_id', que nous avons définie ci-dessus, et utilisera cette colonne pour stocker la clé étrangère. Ensuite, vous faites exactement la même chose pour l'adresse de livraison.

Cela vous permettra d'accéder à votre adresse de facturation et à votre adresse de livraison, les deux instances du modèle D'adresse, via une instance du modèle client, comme ceci:

@customer.billing_address # Returns an instance of the Address model
@customer.shipping_address.street1 # Returns a string, as you would expect

Comme note de côté: la nomenclature' belongs_to ' est un peu déroutante dans ce cas, puisque le Les adresses appartiennent aux clients, pas l'inverse. Ignorez cependant votre intuition; le 'belongs_to' est utilisé sur n'importe quelle chose contenant la clé étrangère qui, dans notre cas, comme vous le verrez, est les deux modèles. Ah! comment est-ce pour de confusion?

Enfin, nous précisons qu'un client a plusieurs adresses. Dans ce cas, nous n'avons pas besoin de spécifier le nom de classe auquel cette propriété est liée car Rails est assez intelligent pour voir que nous avons un modèle avec un nom correspondant: "Adresse", à laquelle nous arriverons dans une seconde. Cela nous permet d'obtenir une liste de toutes les adresses du client en procédant comme suit:

@customer.addresses

Cela renverra un tableau d'instances du modèle D'adresse, qu'il s'agisse d'adresses de facturation ou d'adresses de livraison. En parlant du modèle D'adresse, voici à quoi cela ressemble:

class Address < ActiveRecord::Base
  belongs_to :customer
end

Ici, vous accomplissez exactement la même chose qu'avec les lignes 'belongs_to' dans le modèle Client, sauf que Rails fait de la magie pour vous; en regardant au nom de la propriété ('customer'), il voit le' belongs_to'et suppose que cette propriété référence le modèle avec le même nom ('Customer') et qu'il y a une colonne correspondante sur la table d'adresses ('customer_id').

Cela nous permet d'accéder au client auquel appartient une adresse comme ceci:

@address.customer # Returns an instance of the Customer model
@address.customer.first_name # Returns a string, as you would expect
52
répondu Richard Jones 2013-05-20 09:58:35

J'ai trouvé comment le faire grâce à 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

La table customers inclut les colonnes shipping_address_id et billing_address_id.

C'est essentiellement une relation has_two. J'ai trouvé ce fil utile aussi.

11
répondu titaniumdecoy 2016-07-01 13:13:38

J'ai eu le même problème et résolu en faisant ceci:

create_table :customers do |t|
  t.integer :address_id, :references => "address"
  t.integer :address_id_1, :references => "address"
  # other attributes not shown
end
3
répondu Mateus 2012-12-19 20:48:44