Home > Enterprise >  Managing polymorphic data in Rails
Managing polymorphic data in Rails

Time:06-21

I have an application where a User can create many Links, and each Link can store different type of data, depending on what type of Link it is. For example, a TelephoneLinkData stores a telephone number, an EmailLinkData stores an email address, a subject and a body. Each Link also has some fields in common, such as a reference to the user and a name.

I've tried to map this into ActiveRecord as cleanly as I can. Currently, I have a polymorphic relationship from Link to its data-type:

class Link < ApplicationRecord
  belongs_to :user
  belongs_to :link_data, polymorphic: true
  ...
class EmailLinkData < ApplicationRecord
  has_one :link, as: :link_data
  accepts_nested_attributes_for :links
  ...

Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes, what I'm trying to model is a class having multiple possible different child classes. This works fine, and I'm able to create Links through the various *LinkData controllers, but what I'd really want to do is have the Link act as the primary source of interaction for the user, so that the user manages their links through the /links path. For example, I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field

I've looked around for other ways to model this relationship, and the most common other suggestion seems to be Single-Table Inheritance, but the majority of my columns will differ between LinkData classes, so that feels like the wrong abstraction.

Is there a more idiomatic way to model this data structure?

CodePudding user response:

I was able to get things to work the way I wanted to using multiple table inheritance, based largely on this chapter: https://danchak99.wordpress.com/enterprise-rails/chapter-10-multiple-table-inheritance/

CodePudding user response:

As is always the case, the best choice depends on the business or application needs, so it's difficult to provide a recommendation without knowing more about what you're trying to do.

It sounds like you prefer the MTI approach, essentially using actual foreign keys and an XOR constraint to the Link table instead of a type column. That's a totally reasonable (although not as common) alternative to a polymorphic association.

However, I think there was a bit of a misunderstanding in your question.

Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes...

A polymorphic association in Ruby/Rails doesn't have anything to do with class inheritance (e.g. parents and children). You might be thinking of Single table inheritance. A polymorphic association allows one class (e.g. a Link) to be associated a record in any other table (e.g. the various classes of LinkData) via two fields, a association_id and association_type. These associated classes need not be related to each other. For example, a common use case might be the acts_as_commentable gem, that allows you to add a comment to any other object, and the comment would have a polymorphic association with the other classes.

In the second part of your question you mention that you'd like the User to interact with Link's via a single controller.

I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field

There's nothing stopping you from implementing this using the initially proposed data model. ActiveRecord may not handle this completely for you out of the box, but you can imagine implementing a link_data= method on the Link class that would create the appropriate associated object.

I'd say the pros/cons of using a polymorphic association would be...

Pros:

  • easy to setup and use
  • easy to make required (validate presence of / not null)
  • easy to associate with a new class

Cons:

  • no referential / database integrity
  • have to migrate data if you change a class name

And using the MTI approach is basically the opposite. A bit harder to setup and use, harder to add a new association/table, harder to ensure exactly one association exists... but the long term data quality benefits are significant.

  • Related