Reputation: 118
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
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:
Upvotes: 4