How to do this polymorphic relation


I’m trying to create a polymorphic relation between three tables. I’m confused about some of the things and how to do it properly.

So I’m following Solidus e-commerce which is written in Ruby. I have got two tables which have Zone and Zone_Member.


        has_many :zone_members

        with_options through: :zone_members, source: :zoneable do
        has_many :countries, source_type: "Spree::Country"
        has_many :states, source_type: "Spree::State"

Zone Member

         belongs_to :zone, class_name: 'Spree::Zone', counter_cache: true, inverse_of: :zone_members, optional: true
         belongs_to :zoneable, polymorphic: true, optional: true

Can I create a Zoneable table where I can store country id and state id? But I’m confused about the relation

Or is there any other way to do this?

Yes, there is another way to do it…

But it’s not how rails does it, it’s different :slight_smile:

I got a different way

Zone Member

field :zoneable_type, :string
field :zoneable_id, :integer
belongs_to :zone, Zone

the zoneable_type can be the Module name of the zone type

Elixir.Country  or Elixir.State

and the zoneable_id is its id

as an example the Repo.get/2 can call this way

iex> type= zone_member.zoneable_type|>to_atom
zone=  Repo.get(type, zone_member.zoneable_id)

Well, You can but it is not recommended :slight_smile:

First of all, it is important to remember Ecto does not provide the same type of polymorphic associations available in frameworks such as Rails and Laravel. In such frameworks, a polymorphic association uses two columns, the parent_id and parent_type. For example, one todo item would have parent_id of 1 with parent_type of “TodoList” while another would have parent_id of 1 with parent_type of “Project”.

The issue with the design above is that it breaks database references. The database is no longer capable of guaranteeing the item you associate to exists or will continue to exist in the future. This leads to an inconsistent database which end-up pushing workarounds to your application.

The design above is also extremely inefficient, especially if you’re working with large tables. Bear in mind that if that’s your case, you might be forced to remove such polymorphic references in the future when frequent polymorphic queries start grinding the database to a halt even after adding indexes and optimizing the database.

Check also the polymorphic_embed library out, which may address some of those polymorphic associations needs.

What about Ecto.Type? We can store the same module as a string but when we fetch it can give the module. Then we can do all the things with that.