Mel Mel - 1 month ago 7
Ruby Question

Rails - Polymorphic Associations with has many relationship - index values

I have models for Organisation and Package_ip.

The associations are:

Organisation

has_many :ips, as: :ipable, class_name: Package::Ip
accepts_nested_attributes_for :ips, reject_if: :all_blank, allow_destroy: true


Package::Ip

belongs_to :ipable, :polymorphic => true, optional: true, inverse_of: :ip


The Package_ips table has:

create_table "package_ips", force: :cascade do |t|
t.string "identifier"
t.text "description"
t.text "conditions"
t.integer "ipable_id"
t.string "ipable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "title"
t.string "status"
t.string "classification"
t.index ["ipable_type", "ipable_id"], name: "index_package_ips_on_ipable_type_and_ipable_id", unique: true, using: :btree
end


My objective is for organisations to create many instances of package_ip.

I have a problem however with the way the index works. If I want to create a second instance of package_ip, I get an error that says the index values are no longer unique.

Am I alright to remove the unique constraint from my index? I realise that the ipable_ip is going to be the organisation.id and the ipable_type is going to be the parent class (organisation). I don't see why that needs to be unique. But maybe I've missed something about polymorphic associations and the reliance on the index.

Answer

Yes, this limitation is from your business logic, and not built into rails. Rails should allow you to drop the unique constraint, it would be wise to ensure your business logic is safe to handle this.

You may have code like:

foo_ip org.ips.where(type: "foo").first

This would hopefully live in a named scope if it existed.

Update after comment: Presumably you are using IPs as "Intellectual Properties".

Let's suppose you have

class Song < Package::Ip
end

class ThemeSong < Song
end

class Logo < Package::IP
end

You may have business logic that expects an organization to have exactly one theme song or exactly one Logo.

So you may have code like:

theme = org.theme_songs.first

or:

logo = org.logos.first

# Or worse yet
logo = org.ips.where(:type = "Logo").first

So while Rails supports having multiple Songs or Logos, make sure your business logic doesn't just take the first one in some case.