我有一个名为profiles的表,其中有几列:地址、电话、语言。我希望能够保存在语言列,几种语言,例如:英语,德语,西班牙语,并能够从配置文件/显示视图单独访问每种语言。在概要文件/新视图中,我会使用复选框选择语言。我不能做的是在控制器中的参数像保存在数据库中我的观点:
<div class="col-xl-8">
<h4>Idiomas:</h4>
<div class="form-check form-check-inline">
<%= f.check_box :es, class:'form-check-input' %>
<%= f.label :es, "Español" %>
</div>
<div class="form-check form-check-inline">
<%= f.check_box :en, class:'form-check-input' %>
<%= f.label :en, "Inglés" %>
</div>
<div class="form-check form-check-inline">
<%= f.check_box :pt, class:'form-check-input' %>
<%= f.label :pt, "Portugués" %>
</div>
<div class="form-check form-check-inline">
<%= f.check_box :fr, class:'form-check-input' %>
<%= f.label :fr, "Francés" %>
</div>
<div class="form-check form-check-inline">
<%= f.check_box :it, class:'form-check-input' %>
<%= f.label :it, "Italiano" %>
</div>
<div class="form-check form-check-inline">
<%= f.check_box :ge, class:'form-check-input' %>
<%= f.label :ge, "Alemán" %>
</div>
</div>
在同一列中存储多种语言是违反数据库规范化的。
https://en.wikipedia.org/wiki/Database_normalization
例如,查询说english
的人是非常困难的,如果你想添加/更改语言,你必须触摸几乎所有行等等。
我认为你应该做的是从配置文件表中分离语言信息,并使用profiles_languages表将两者连接起来。
配置文件表
您可以使用位字段来保存代表您选择的语言的位序列作为数据库中的单个列,有一些gems支持bit field
(https://www.ruby-toolbox.com/categories/Active_Record_Bit_Fields)。
使用flag_shih_tzu gem的演示
# migration
add_column :profiles, :flags, :integer, :null => false, :default => 0
# flag_shih_tzu-managed bit field
# Effective booleans which will be stored on the flags column:
# t.boolean :en
# t.boolean :es
# t.boolean :fr
# and so on ...
# models/profile.rb
class Profile
include FlagShihTzu
has_flags 1 => :en,
2 => :es,
3 => :fr
end
profile = Profile.new
profile.en = true
profile.es = true
profile.save
profile = Profile.last
profile.en? # true
profile.es? # true
profile.fr? # false
所以你可以设置true
为选定的语言从你的视图复选框。
如果你不想使用上面的bitfield
宝石,这里有一个使用postgres位串(或mysql binary-varbinary)的演示,所以你的语言设置被缓存在数据库上的位序列像这样&;01010011&;,1:selected, 0: none。
create_table :profiles, force: true do |t|
t.column :languages, "bit(8)" # "bit(16)" whatever ...
end
class Profile < ApplicationRecord
before_create :set_default_languages
# you could refactor as you want
[:ruby, :python, :js, :java, ...].each_with_index do |lang, index|
define_method("#{lang}?") do
result = ActiveRecord::Base.connection.execute(
"Select get_bit(languages, #{index})
From profiles Where id = #{self.id} Limit 1")
result&.first.values&.first == 1
end
define_method("#{lang}!") do
ActiveRecord::Base.connection.execute(
"Update profiles SET languages =
set_bit(languages, #{index}, 1) Where id = #{self.id}")
end
end
private
def set_default_languages
self.languages = "00000000"
end
end
profile = Profile.create(...)
profile.ruby? # false
profile.ruby! # set to 1
profile.reload
profile.ruby? # true