Mel Mel - 9 months ago 66
Ruby Question

Rails - Polymorphic Associations with has many relationship - index values

I have models for Organisation and Package_ip.

The associations are:


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


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

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 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 Source

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

class ThemeSong < Song

class Logo < Package::IP

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


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.