【问题标题】:How do I build a link_to tag that takes a string ID instead of a numeric id?如何构建一个使用字符串 ID 而不是数字 ID 的 link_to 标记?
【发布时间】:2018-07-23 03:06:23
【问题描述】:

我有一个关于 Rails 5 的路由问题。我的 config/routes.rb 文件中有这个问题

  resources :votes

我的 VotesController 中的“show”方法可以采用数字或字符串形式的 ID。

  def show
    id = params[:id]
    if id.present?
      # If they entered a name
      if id.to_i == 0
        name = id.gsub "_", " "
        @person = Person.find_by_name(name)
      else
        @person = Person.find_by_id(id)
      end

我曾经能够构造这个link_to来链接到生成带有数字ID的链接的方法...

<%= link_to person.name, vote_path(person), :class => 'unvotedPersonLink' %>

但是,我想生成一个带有 ID 字符串值的链接,该 ID 由我的模型中名为“person.seo_name”的方法定义。所以我尝试了这个

<%= link_to person.name, vote_path(:id => person.seo_name), :class => 'unvotedPersonLink' %>

但出现错误

No route matches {:action=>"show", :controller=>"votes", :id=>nil} missing required keys: [:id]

如何构建我的 link_to 标记,以便它传入我想要的“字符串”参数而不是数字参数?

【问题讨论】:

  • 确保 seo_name 方法没有返回 nil
  • vote_path(:id =&gt; person.seo_name) 只要seo_name 有值就应该可以工作。尝试注释掉除resources :votes 之外的所有其他路由,以查看是否有其他路由定义受到干扰。
  • 您应该能够做到vote_path(person.seo_name) ...您不需要(也不应该)指定:id =&gt;...,因为这意味着第一个参数(链接对象)是@ 987654331@.

标签: ruby-on-rails model-view-controller routes ruby-on-rails-5 link-to


【解决方案1】:

你可以传入任何你想要的东西。如果错误告诉您您传递的 ID 是 nil,那么您正在使用的 Personseo_name 属性是 nil

只要vote_path 需要一个ID,您可以通过rake routes | grep vote 进行检查,那么就生成链接而言,这对您很有效:

<%= link_to person.name, vote_path(person.seo_name) %>

但请记住,您需要在控制器中进行必要的调整才能根据 seo_name 列而不是 id 搜索您的 persons 表。


此外,我强烈建议您将 utilize a gem 称为 friendly_id 以完成您想要完成的工作。它会让事情变得更清洁和更干燥,因为您可以在任何模型中轻松使用它。您可以轻松地使用 ID 一个 URL 友好的 slug 来查询您的表,只需在查询中添加 friendly

Person.friendly.find(1)

# OR #

Person.friendly.find('this-is-a-url-friendly-string')

【讨论】:

  • 我尝试将“vote_path(person)”块更改为您所拥有的 --“vote_path(person.seo_name)”,但我仍然收到错误消息,“No route matches {:action=> "show", :controller=>"votes", :id=>nil} 缺少必需的键:[:id]"。
  • 经过调试验证,“person.seo_name”确实不为零。
【解决方案2】:

在你看来

<%= link_to person.name, vote_path(person), :class => 'unvotedPersonLink' %>

<%= link_to person.name, vote_path(id: person.seo_name), :class => 'unvotedPersonLink' %>

在你的控制器中

@person = Person.find_by(id: params[:id]) || Person.find_by(name: params[:id])

确保 seo_name 方法不返回 nil

【讨论】:

  • 我应该对我的 config/routes.rb 文件做些什么吗?当我输入“ 'unvotedPersonLink' %>”时,我收到错误消息“No route matches {:action=>”show” , :controller=>"votes", :id=>nil} 在加载页面时缺少必需的键:[:id]"。
  • 您的控制器名称是否有间隔?
  • 投票控制器?文件路径为“app/controllers/votes_controller.rb”,文件第一行为“class VotesController
  • 不,@Natalia 你不需要对你的路线做任何事情。只要确保在您的数据库中的人确实有 seo_name。如果没有,则会出现此错误。
【解决方案3】:

异常来自vote_path,因此我们可以专注于此,而不用担心link_to 或控制器。

当我遇到这样的路线问题时,我要做的第一件事是运行rake routes(或现在的rails routes)以确保我得到了正确的名称。其次,我在控制台中使用 app 变量。例如:

> app.vote_path(1)
 => "/votes/1"
> app.vote_path(id: 1)
 => "/votes/1"
> app.vote_path("foo")
 => "/votes/foo"
> app.vote_path(id: "foo")
 => "/votes/foo"
> app.vote_path(nil)
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"votes", :id=>nil}, possible unmatched constraints: [:id]
> app.vote_path(id: nil)
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"votes", :id=>nil}, possible unmatched constraints: [:id]

你说你已经在其他地方检查过 person.seo_name 没有返回 nil,但我会再次检查,但看起来确实是问题所在。试试这个:

p = Person.find 5   # or whatever is the numeric id
app.vote_path(p.seo_name)

【讨论】:

    【解决方案4】:

    参数可以包含任何信息,但您必须根据需要在控制器中处理它们。默认情况下,路由需要有一个名为:id 的参数。如果您希望在您的路线中使用其他名称,您可以覆盖默认资源标识符:id

    查看rails routing

    确保person.seo_name 没有返回nil。因此,您可以在 Person 模型中使用 before_save 回调。在那里,您可以使用特定数据设置seo_name 字段。

    class Subscription < ActiveRecord::Base
       before_save :populate_seo_name, on: :create
    
       private
         def populate_seo_name
           self.seo_name = ...
         end
    end
    

    【讨论】:

      【解决方案5】:

      过去https://stackoverflow.com/a/26600064/4652075已经回答了这个问题。

      您正在将一个字符串传递给一个需要整数的字段,并且它被呈现为空。您应该将字符串值传递给需要字符串的字段。如果您的“投票”模型是由ActiveRecord 生成的,那么它很可能是一个整数。

      在你的情况下:

      class Person < ApplicationRecord
        def to_param
          identifier
        end
      
        # or
        alias_method :to_param, :identifier
      end
      
      class VotesController < ApplicationController
        before_action :set_voter
      
        def show end;
      
        private
      
        def set_voter
          if !params[:id]?
            @person = Person.find_by(identifier: params[:seo_name])
          else
            @person = Person.find(params[:id])
          end
        end
      end
      
      # in the view
      <%= link_to person.name, votes_path(identifier: person.seo_name), class: "whatever" %>
      

      应该可以。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-31
        • 1970-01-01
        • 2015-03-02
        • 2018-01-09
        • 2023-04-03
        • 1970-01-01
        • 2021-06-07
        • 2021-10-01
        相关资源
        最近更新 更多