GabrielTheCoder
GabrielTheCoder

Reputation: 118

save multiple values as array into variable rails

I got a variable in a nested model (account) belonging the user model in which there is a column for :skills. in my edit_user_path, i would like to be able to save multiple skills into that column via checkboxes. For doing that I permitted the skills in the controller as array, even though it is saved as string into the database.

My controller:

    def user_params
        params.require(:user).permit(:email, :password, :password_confirmation, :account, 
            account_attributes:[:id, :username, {:skills => []}, :description, :location, :avatar, :tags, :tag_list])
    end

If i save multiple values via checkboxes into this variable inside of a nested form, i do it like this:

        <% skills = ["Coding", "Design", "Petting Cats"] %>
        <% skills.each do |skill| %>

        <div class="form-check form-check-inline">
          <div class="custom-control custom-checkbox">
            <%= form.check_box :skills, { multiple: true, class:"custom-control-input",id: skill }, skill, false %>
            <%= form.label skill, class:"custom-control-label", for: skill %>
          </div>
        </div>

        <% end %>

This works and the values are getting saved as an array, but oddly the array once saved into the database looks like this:

"[\"Artist\", \"Mixing\", \"Mastering\"]" 

instead of this:

["Artist", "Mixing", "Mastering"] 

Which leads to troubles, since i would like to iterate through all users later "filtering" for certain skills, like User.account.where(skills: "Petting Cats") if a user has Petting Cats saved somewhere inside of the array.

For Development i am using SQLite, for production PostgresQL. How do i save multiple strings into a string variable as clean array without mess, and how to iterate through the array later with a where query method?

Upvotes: 0

Views: 1082

Answers (1)

max
max

Reputation: 101891

You're falling into the common double whammy noob trap of using an array column and serialize.

The reason you are getting "[\"Artist\", \"Mixing\", \"Mastering\"]" stored in is that you are using serialize with a native array column. serialize is an old hack to store stuff in varchar/text columns and the driver actually natively converts it to an array. When you use serialize with a native array/json/hstore column your actually casting the value into a string before the driver casts it. The result is:

["[\"Artist\", \"Mixing\", \"Mastering\"]"]

Removing serialize will fix the immediate problem but you're still left with a substandard solution compared to actually doing the job right:

# rails g model skill name:string:uniq
class Skill < ApplicationRecord
  validates :name, uniqueness: true, presence: true
  has_many :user_skills, dependent: :destroy
  has_many :users, through: :user_skills
end
# rails g model user_skill user:belongs_to skill:belongs_to
# use `add_index :user_skills, [:user_id, :skill_id], unique: true` to ensure uniqueness
class UserSkill < ApplicationRecord
  validates_uniqueness_of :user_id, scope: :skill_id
  belongs_to :user
  belongs_to :skill
end
class User < ApplicationRecord
  has_many :user_skills, dependent: :destroy
  has_many :skills, through: :user_skills
end
<%= form_with(model: @user) do |form| %>
  <div class="field"> 
    <%= form.label :skill_ids %>
    <%= form.collection_select :skill_ids, Skill.all, :id, :name %>
  </div>
<% end %>
def user_params
  params.require(:user).permit(
    :email, :password, :password_confirmation, :account, 
    account_attributes: [
      :id, :username, :description, :location, :avatar, :tags, :tag_list,
    ],
    skill_ids: [] 
 )
end

This gives you:

  • Queryable data
  • Referential integrity
  • Normalization
  • Encapsulation
  • ActiveRecord Assocations!
  • The pleasure of not looking like an idiot in a review/audit

Upvotes: 4

Related Questions