You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 7 + Ransack搜索Action Text富文本字段报错求助

Rails 7 + Ransack 4: 解决Action Text字段搜索报错undefined method 'type' for nil:NilClass

问题根源

  1. body并非Post表的直接字段,而是通过has_rich_text :body自动创建的rich_text_body关联,存储在action_text_rich_texts表中。直接将body加入ransackable_attributes会导致Ransack尝试在Post模型中查找不存在的字段,触发nil:NilClass错误。
  2. ransackable_associations配置错误:has_rich_text创建的关联名称是rich_text_body,而非action_text_rich_texts,错误的关联名称会让Ransack无法识别关联表。

解决方案步骤

  1. 修正Post模型的Ransack配置,移除错误的属性声明,替换正确的关联名称,并添加自定义ransacker映射关联字段。
  2. 调整控制器查询逻辑,确保关联表被正确连接,避免查询异常。

修改后的代码

Post模型

class Post < ApplicationRecord
  has_rich_text :body
  has_many :comments, dependent: :destroy
  belongs_to :user
  has_one_attached :post_image

  validates :title, presence: { message: 'must be given please' }, length: { minimum: 5 }
  validates :body, presence: true, length: { minimum: 10 }
  validate :post_body_length
  validate :acceptable_image

  # 自定义ransacker,将body搜索映射到关联表的body字段
  ransacker :body, type: :string do
    ActionText::RichText.arel_table[:body]
  end

  def self.ransackable_attributes(_auth_object = nil)
    # 移除body,它不是Post的直接属性
    %w[title created_at updated_at user_id]
  end

  def self.ransackable_associations(_auth_object = nil)
    # 使用正确的关联名称rich_text_body
    %w[users comments rich_text_body]
  end

  private

  def acceptable_image
    return unless post_image.attached?

    errors.add(:post_image, 'is too big') unless post_image.blob.byte_size <= 5.megabyte

    acceptable_types = ['image/jpeg', 'image/png']
    return if acceptable_types.include?(post_image.content_type)

    errors.add(:post_image, 'must be a JPEG or PNG')
  end

  def post_body_cant_be_empty
    if self.body.blank?
      self.errors.add(:body, "can't be empty")
    end
  end

  def post_body_length
    if self.body.to_plain_text.length < 10
      self.errors.add(:body, 'is too short (minimum is 10 characters)')
    end
  end
end

SearchesController

class SearchesController < ApplicationController
  def index
    @query = Post.ransack(params[:q])
    # joins确保关联表被连接,includes预加载优化性能
    @posts = @query.result.joins(:rich_text_body).includes(:rich_text_body, :user)
  end
end

搜索表单(无需修改)

<%= search_form_for @query, url: searches_path, method: :get, html: { class: "d-flex", role: "search" } do |f| %>
  <%= f.search_field :title_or_body_cont, class: "form-control me-2", placeholder: "Search title or body..." %>
  <%= f.submit "Search", class: "btn btn-outline-secondary-light btn-sm" %>
<% end %>

说明

  • 自定义ransacker让Ransack识别body搜索条件,自动映射到action_text_rich_texts.body字段,原表单的title_or_body_cont参数可直接生效。
  • joins(:rich_text_body)强制连接关联表,确保Ransack能访问到关联字段;includes用于预加载关联数据,避免N+1查询问题。

内容的提问来源于stack exchange,提问作者Yurii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:41:01