Ruby on Rails使用Associations关联无法获取路径全部对应术语问题
问题描述
我正在做一个用Ruby连接PostgreSQL数据库的项目。现有三张存储数字的表,还有一张表存储数字和其他表中文字的映射关系,需要在Ruby页面中展示对应文字而非数字。我尝试配置了has_many和belongs_to关联,但始终只能获取paths表中path列最后一个数字对应的文字,比如路径为/298/299/300时,只能拿到300对应的文字,需要获取全部三个数字对应的文字。
模型代码
class MuseumObject < ApplicationRecord belongs_to :path, :foreign_key => "main_path_id" end class Path < ApplicationRecord has_one :termlist, through: :termlist_paths has_many :museum_objects has_one :termlist_path end class TermlistPath < ApplicationRecord belongs_to :path belongs_to :termlist end class Termlist < ApplicationRecord belongs_to :path belongs_to :termlist_path end
表结构Schema
create_table "museum_objects", id: :serial, force: :cascade do |t| t.integer "main_path_id" end create_table "paths", force: :cascade do |t| t.string "path" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["path"], name: "index_paths_on_path", unique: true end create_table "termlist_paths", force: :cascade do |t| t.bigint "termlist_id" t.bigint "path_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["path_id"], name: "index_termlist_paths_on_path_id" t.index ["termlist_id", "path_id"], name: "index_termlist_paths_on_termlist_id_and_path_id", unique: true t.index ["termlist_id"], name: "index_termlist_paths_on_termlist_id" end create_table "termlists", force: :cascade do |t| t.string "name" t.string "name_en" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "position" t.string "name_ar" end add_foreign_key "museum_object_paths", "paths" add_foreign_key "termlist_paths", "paths" add_foreign_key "termlist_paths", "termlists" end
索引页面视图代码
<tbody> <% @museum_objects.each do |museum_object| %> <tr> <td><%= museum_object.path.termlist_path.termlist.name_en%></td> </tr> <% end %> </tbody> </table>
解决方案
你当前的问题核心是关联配置错误,Path和Termlist属于多对多关联,你配置成了has_one,因此只能读取到最后一条关联数据,按以下步骤修改即可:
- 修正模型关联配置
class Path < ApplicationRecord has_many :museum_objects # 替换原有的has_one配置,一个path对应多个关联的术语 has_many :termlist_paths has_many :termlists, through: :termlist_paths end class Termlist < ApplicationRecord # 移除多余的belongs_to :path,Termlist通过中间表关联Path has_many :termlist_paths has_many :paths, through: :termlist_paths end
- 给Path模型扩展方法,批量获取全路径对应名称
class Path < ApplicationRecord # 省略其余关联配置 def full_path_names(lang = :name_en) # 拆分path字符串提取所有术语ID term_ids = path.split('/').reject(&:empty?).map(&:to_i) # 按path中的顺序查询对应术语,返回指定语言的名称列表 Termlist.where(id: term_ids).order(Arel.sql("position(id::text in '#{path}')")).pluck(lang) end end
- 修改查询逻辑和视图代码
首先在控制器层做预加载避免N+1查询:
@museum_objects = MuseumObject.includes(path: :termlists).all
然后修改视图展示逻辑:
<td><%= museum_object.path.full_path_names.join(' / ') %></td>
如果不需要额外处理顺序,也可以直接调用关联方法输出:
<td><%= museum_object.path.termlists.pluck(:name_en).join(' / ') %></td>
内容的提问来源于stack exchange,提问作者Emily
相关产品推荐
相关产品推荐

