【问题标题】:Search method not pulling data搜索方法不提取数据
【发布时间】:2020-12-24 05:04:11
【问题描述】:

我刚刚在我的模型中创建了一个搜索方法。但是,它似乎部分起作用。本质上,如果有人要在搜索字段中输入英雄的名字,它应该从索引中提取与英雄相关的所有“报告”以及所有相关数据。

现在,我搜索“雷神”,假设我在与他关联的 4 份报告的列表中有 3 份报告。其他人与其他英雄相关联。它通过动作,然后将我带到索引页面,但不会在页面/表格上显示任何错误或报告。只是一个空的屏幕。像这样。

报表模型

class Report < ApplicationRecord
   validates :subject, presence: true, length: { minimum: 6, maximum: 100 }
   validates :description, presence: true, length: { minimum: 10, maximum: 300 }

   belongs_to :user
   
   has_many :report_heros
   has_many :heros, through: :report_heros

   has_many :report_villains
   has_many :villains, through: :report_villains

    def self.search(search)
        if search
            hero = Hero.find_by(hero_name: search)
                if hero
                    self.where(hero_id: hero.id)
                else
                    Report.all
                end
        else
            Report.all
        end
    end
    
end

报告索引

<div class="container">
   <h1 class="row justify-content-center">Reports</h1>
   <h3> Search for a Hero related Reports</h3>
   <%= form_tag reports_path, :method => "get" do %>
      <%= text_field_tag(:search, params[:search])%>
      <%= submit_tag("Search", :name => nil) %>
   <% end %>

   <table class="table table-bordered bg-light">
      <thead bgcolor="#959595">
         <tr>
            <th scope="col" class="text-center">Ticket Number</th>
            <th scope="col" class="text-center">Subject</th>
            <th scope="col" class="text-center">Created By</th>
            <th scope="col" class="text-center">Hero Involved</th>
            <th scope="col" class="text-center">Villain Involved</th>
            <th scope="col" class="text-center">Edit</th>
            <th scope="col" class="text-center">Delete</th>
         </tr>
      </thead>
      <tbody>
         <% @reports.each do |report|%>
         <tr>
         <% if report.user == current_user || current_user.admin? %>
            <td class="text-center"><%= link_to report.id, report_path(report) %></td>
            <td><%= report.subject %></td>
            <td><%= link_to report.user.email, report_path(report) %></td>
            <% report.heros.each do |hero|%>
            <td class="text-center"><%= link_to hero.hero_name, report_hero_path(report.id, hero.id) %></td>
            <% end %>
            <% report.villains.each do |villain|%>
            <td class="text-center"><%= link_to villain.villain_name, report_villain_path(report.id, villain.id) %></td>
            <% end %>
            <td class="text-center"><%= link_to "Edit Report", edit_report_path(report) %></td>
            <td class="text-center"><%= link_to "Delete Report", report_path(report), method: :delete, data: {confirm: "Are you sure?"} %></td>
            <% end %>
         </tr>
         <% end %>
      </tbody>
   </table>

   <%= link_to 'New Report', new_report_path, class: "btn btn-primary" %>
</div>

报表控制器

class ReportsController < ApplicationController
    before_action :require_login
    before_action :authenticate_user!, except: [:index, :show]
    before_action :set_report, only: [:show, :edit, :update, :destroy]
    before_action :require_same_user, only: [:edit, :update, :destroy]
    
    def index
        @reports = Report.all
        @reports = Report.search(params[:search])
    end
    
    def show
    end
    
    def new
        @report = current_user.reports.new
    end
    
    def create
        @report = current_user.reports.build(report_params)
        if @report.save
            flash[:notice] = "Report was created successfully."
            redirect_to @report
        else
            render 'new'
        end
    end
    
    def update
        if @report.update(report_params)
            flash[:notice] = "Report updated successfully"
            redirect_to @report
        else
            render 'edit'
        end
    end
    
    def edit
    end
    
    def destroy
        @report.destroy
        redirect_to reports_path
    end
    
    private
    
    def set_report
        @report = Report.find(params[:id])
    end
    
    def report_params
        params.require(:report).permit(:subject, :description, :hero_ids, :villain_ids, :search)
    end

    def require_login
        unless user_signed_in?
          flash[:error] = "You must be logged in to access this section"
          redirect_to root_path
        end
    end

    def require_same_user
        if current_user != @report.user && !current_user.admin?
          flash[:alert] = "You can only edit or delete your own article"
          redirect_to @report
        end
    end
end

这是正在查询的内容-

Started GET "/reports?search=Thor" for ::1 at 2020-09-04 20:58:13 -0500
Processing by ReportsController#index as HTML
  Parameters: {"search"=>"Thor"}
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/reports_controller.rb:57:in `require_login'
  Hero Load (0.1ms)  SELECT "heros".* FROM "heros" WHERE "heros"."hero_name" = ? LIMIT ?  [["hero_name", "Thor"], ["LIMIT", 1]]
  ↳ app/models/report.rb:15:in `search'
  Rendering reports/index.html.erb within layouts/application
  Report Load (0.2ms)  SELECT "reports".* FROM "reports" WHERE "reports"."hero_id" = ?  [["hero_id", 1]]
  ↳ app/views/reports/index.html.erb:22
  Rendered reports/index.html.erb within layouts/application (Duration: 1.1ms | Allocations: 793)
[Webpacker] Everything's up-to-date. Nothing to do
  Rendered layouts/_navigation.html.erb (Duration: 0.5ms | Allocations: 435)
  Rendered layouts/_messages.html.erb (Duration: 0.1ms | Allocations: 17)
  Rendered layouts/_footer.html.erb (Duration: 0.0ms | Allocations: 5)
Completed 200 OK in 30ms (Views: 8.7ms | ActiveRecord: 0.4ms | Allocations: 7637)

【问题讨论】:

    标签: ruby-on-rails search


    【解决方案1】:

    我的第一个倾向是告诉你在你的关联中指定类名。例如:

    has_many :report_heros, class_name: 'Report::Hero'
    has_many :heros, through: :report_heros
    

    虽然这对我来说没有多大意义,因为您上面的代码正在查询 Hero 类。所以在我看来,这里的关联应该只是has_many :heroes, class_name: 'Hero',除非你确实有一个 Report::Hero 模型。此外,您可能需要在 has_many-through 语句中指定 source。旁注,“英雄”的复数形式是“英雄”,所以你的联想也拼错了。

    接下来,Report 和 Hero 是不同的对象类型,您的搜索方法有时会返回 Hero 对象,有时会返回 Report 对象。但是您的代码似乎需要 Report 对象。因此,如果 Hero 模型上不存在 user(或者如果它是空白的),它可能会命中 report.user == current_user || current_user.admin? 并评估为 false;这可以很容易地解释为什么一切都是空白的。

    我的下一个建议是对您的搜索类方法进行一些改进,包括其逻辑(因此它总是返回一个报告)以及清理和简化您的语法。你可能会这样写:

    def self.search(name)
      # Sanitize your input
      name = name.strip
      
      # This assumes the attribute on your Hero model is 'name'
      # Having the name 'hero_name' as a name on the Hero model is redundant
      if name.present?
        # Fetch all *Report* objects where the an associated Hero's name 
        # matches the name passed to the method as an argument
        reports = Report.joins(:heroes).where(heroes: { name: name }).presence 
        # You may need to replace 'joins' with 'includes'
      end
    
      # Calling [].presence returns nil, so using memoization here means 
      # Report.all is only queried if a name was present but the query returned []
      reports ||= Report.all
    end
    

    我最后的想法是您的数据库由于某种原因未配置为不区分大小写的匹配,但实际上我认为最有可能的问题是您的搜索方法返回了两种完全不同的数据类型(Hero 和 Report)。如果这不是问题,请提供有关相关模型及其架构的一些信息。

    【讨论】:

    • 感谢艾莉森,这很有效。我不太明白的唯一部分代码是 Report.joins(:heroes)。我从guides.rubyonrails.org/active_record_querying.html#joins 查找了参考资料,但仍然有一些理解上的困难。你碰巧有什么好的参考资料吗?仅供参考,我也知道 Heroes 拼写错误,哈哈。生成器以这种方式创建它,我在开发后期注意到它并保持原样。我在大部分应用程序中都考虑到了这一点。
    • 另外,感谢您抽出宝贵时间进行冗长的回复并帮助我了解发生了什么。
    • @MarrixRed NW,这很令人困惑。该 rails 查询表示获取所有具有通过 hero 关联关联的记录的报告,其中 hero 表中的 name 属性等于 search 方法接收到的 name 参数。它在 SQL 中评估类似于 SELECT * FROM reports INNER JOIN heroes ON reports.hero_id = heroes.id WHERE heroes.name = 'the name argument'; 当我第一次学习 Rails 查询中的关联过滤时,我发现这很有帮助:scoutapm.com/blog/…
    • 此外,如果 rails 模型生成器生成的名称不正确,您仍然可以修改任何您想要的内容,只要您使其保持一致(Rails 依赖于 rails 魔法的一致性)。我通常会查看生成的文件并更新名称和文件路径以具有正确的语法。这应该在应用迁移之前完成(例如,db:migrate)。
    【解决方案2】:

    这部分似乎不起作用(否则应该返回所有报告):

    hero = Hero.find_by(hero_name: search)
      if hero
        self.where(hero_id: hero)
    

    如果改成这样行不行

    self.where(hero_id: hero.id)
    

    【讨论】:

    • 不幸的是不是 - 仍然是同样的事情。我更新了代码并提供了 rails s 的结果
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    • 2021-01-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多