Home > Enterprise >  Using a has_one or belongs_to in a polymorphic association
Using a has_one or belongs_to in a polymorphic association

Time:02-03

I have an Business model and an polymorphic Address model.

Each business has one main address but I would like advise on if it is better to have business belong_to the polymorphic model or if it is better to say each business has_one address

  belongs_to :main_location_address, class_name: "Address"

  has_one :main_location_address, class_name: "Address", as: :addressable

I have tried both but belongs_to still requires a addressable attribute for the address.

Also how would this work with accepts_nested_attributes_for in the Business model and in the controller?

CodePudding user response:

When representing relationships between entities it is important to reflect them in such a way that there is a logical resemblance to their real life counterparts.

It makes sense to say

Business has one address/main address

rather than saying Business belongs to an address.

If you want to enforce a database constrain that a business can only have one address, you can create a unique index. Since it is a polymorphic relation, I'm assuming it will be used by other models as well. If you do not need to enforce uniqueness for all models, you could go for a partial index to create a unique index only where addressable_type is Business.

class AddIndexToAddress < ActiveRecord::Migration
  def change
    add_index :addresses, [:addressable_id, :addressable_type], unique: true, where: "addressable_type = 'Business'"
  end
end

This will work if you only need strictly one address per business.

It is not clear from the question if you have an additional relationship to manage multiple addresses for a business as well. In that case you need opt for a belong_to relationship with business containing the main address id.

class Business < ApplicationRecord
    belongs_to :main_address, class_name: 'Address', foreign_key: :address_id, optional: true
    has_many :addresses, class_name: 'Address', as: :addressable
end

You can add validations as per your business requirements.

CodePudding user response:

If you want to ensure that a buisness actually can only have one main address then you need to use a belongs_to assocation and create a foreign key column.

class AddMainLocationAddressToBuisnesses < ActiveRecord::Migration[7.0]
  def change
    add_reference :buisnesses, :main_location_address, 
      null: true, 
      foreign_key: { to_table: :addresses }
  end
end
belongs_to :main_location_address, class_name: "Address", optional: true

Since the foreign key is stored on a column in the buisnesses table it can only have a single possible value per buisness.

has_one :main_location_address, class_name: "Address", as: :addressable

Will just fetch the first joined row off the addresses table. While you could monkey around and add addresses.main_location boolean column It provides no guarentee that there actually is just one main location address per addressable unless you for example write a custom constraint.

But then again if you're using a polymorphic assocation you have already tossed referential integrity out the window.

  • Related