Search code examples
ruby-on-railsnested-attributes

Nested attributes in Rails where they key is a column field


I have a Ruby on Rails project where I have a model which accepts nested attributes for another model. I am not receiving the input JSON from a standard form, but instead from a library which has a specific format.

Say my database looks like this:

user columns: id, name, country
user_setting columns: key, value, user_id

The input I receive looks like this:

{
  name: "name",
  country: "country",
  settings: {
     edit: true,
     autosave: false
  }
}

I can access the settings as the nested attributes by doing something like this:

alias_attribute :settings, :settings_attributes

But now the issue is that the settings field is being read as an array like ["edit", "true"] which can't be saved to the database.

Is there any way to configure the model to accept this input and interpret the key as the key column and the value as the value column?

I've considered restructuring the entire input to match what Rails is expecting but that just seems like extra work (for me and the server) and is a bit messy.


Solution

  • No, that's not supported in the behavior of accepts_nested_attributes_for, and AFAIK it cannot be configured to work that way. You could define a "virtual method" instead to accept a settings hash that manually sets the expected accepts_nested_attributes_for style:

    def settings=(hash)
      self.settings_attributes = hash.map do |key, value|
        {
          id: user_settings.find_by(key: key)&.id # update any existing user_settings instead of duplicating
          key: key,
          value: value
        }
      end
    end
    

    This will mark the associated UserSetting records as dirty, which then is saved automatically since you've declared accepts_nested_attributes_for. There's a lot of magic going on here, so you may want to balance the cleverness/framework knowledge required with maintainability. Writing a "dumber" solution may be better in the long run.