【问题标题】:ActionController::UrlGenerationError with :admin namespace and nested resources带有 :admin 命名空间和嵌套资源的 ActionController::UrlGenerationError
【发布时间】:2016-09-23 11:45:03
【问题描述】:

早安 StackOverflow,

我创建了一个管理命名空间,在命名空间内我有一个客户端资源,其中嵌套了一个站点资源,如下所示:

# routes.rb

namespace :admin do

  resouurces :clients do
    resources :sites
  end

end

我在尝试从“站点”显示页面转到站点“编辑”页面时遇到以下错误:

ActionController::UrlGenerationError at /admin/clients/9A81622C/sites/88AA/edit

然后 BetterErrors 在错误消息下方给了我这个:

No route matches {:action=>"show", :client_id=>nil, :controller=>"admin/clients/sites", :id=>"88AA"} missing required keys: [:client_id]

admin_client_site 的 rake 路由输出如下:

     admin_client_sites POST   /admin/clients/:client_id/sites(.:format)          admin/clients/sites#create
  new_admin_client_site GET    /admin/clients/:client_id/sites/new(.:format)      admin/clients/sites#new
 edit_admin_client_site GET    /admin/clients/:client_id/sites/:id/edit(.:format) admin/clients/sites#edit
      admin_client_site GET    /admin/clients/:client_id/sites/:id(.:format)      admin/clients/sites#show
                        PATCH  /admin/clients/:client_id/sites/:id(.:format)      admin/clients/sites#update
                        PUT    /admin/clients/:client_id/sites/:id(.:format)      admin/clients/sites#update
                        DELETE /admin/clients/:client_id/sites/:id(.:format)      admin/clients/sites#destroy

“显示页面”上的编辑链接目前如下,这就是问题所在(或者我相信):

<%= link_to "Edit", edit_admin_client_site_path(@client, @site) %>

客户端的form_for,网站是:

<%= form_for [:admin, @client, @site], :url => admin_client_sites_url do |f| %>

我花了最后一天查看各种堆栈答案,但仍然无法解决这个问题,非常感谢这里的任何帮助,提前感谢,如果您需要更多文档,请告诉我!

编辑 #1 - 添加客户端和站点模型

Client.rb
class Client < ActiveRecord::Base
  before_create :generate_client_ident

    # Model Relations
    has_many :sites, dependent: :destroy

    # Model Validations
    validates_uniqueness_of :client_ident

    # Unique Admin Identifier
    def generate_client_ident
      begin
        self.client_ident = SecureRandom.hex(4).upcase
        other_client = Client.find_by(client_ident: self.client_ident)
      end while other_client
    end

    # Vanity URL
    def to_param
      client_ident
    end
end


Site.rb
class Site < ActiveRecord::Base
  before_create :generate_site_ident

  # Model Relations
  belongs_to :client

  # Model Validations
  validates_uniqueness_of :site_ident

  # Unique Admin Identifier
  def generate_site_ident
    begin
      self.site_ident = SecureRandom.hex(2).upcase
      other_site = Site.find_by(site_ident: self.site_ident)
    end while other_site
  end

  # Vanity URL
  def to_param
    site_ident
  end

end

编辑 #2 - 添加控制器

class Admin::Clients::SitesController < ApplicationController
  before_action :authenticate_admin_admin!
  before_action :set_site, only: [:show, :edit, :update, :destroy]

  # GET /sites
  # GET /sites.json
  def index
    @sites = Site.all
  end

  # GET /sites/1
  # GET /sites/1.json
  def show
    @client = Client.find_by_client_ident(params[:id])
  end

  # GET /sites/new
  def new
    @client = Client.find_by_client_ident(params[:id])
    @site = Site.new
  end

  # GET /sites/1/edit
  def edit
    @client = Client.find_by_client_ident(params[:id])
  end

  # POST /sites
  # POST /sites.json
  def create
    @client = Client.find_by_client_ident(params[:id])
    @site = Site.new(site_params)
    @site.client = @client

    respond_to do |format|
      if @site.save
        format.html { redirect_to admin_clients_url, notice: 'Site was successfully created.' }
        format.json { render :show, status: :created, location: [:admin, @client] }
      else
        format.html { render :new }
        format.json { render json: @site.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /sites/1
  # PATCH/PUT /sites/1.json
  def update
    respond_to do |format|
      if @site.update(site_params)
        format.html { redirect_to [:admin, @client], notice: 'Site was successfully updated.' }
        format.json { render :show, status: :ok, location: [:admin, @client] }
      else
        format.html { render :edit }
        format.json { render json: @site.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /sites/1
  # DELETE /sites/1.json
  def destroy
    @site.destroy
    respond_to do |format|
      format.html { redirect_to sites_url, notice: 'Site was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_site
      @site = Site.find_by_site_ident(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def site_params
      params.require(:site).permit(:client_id, :site_ident)
    end
end

class Admin::ClientsController < ApplicationController
  before_action :authenticate_admin_admin!
  before_action :set_client, only: [:show, :edit, :update, :destroy]

  # GET /clients
  # GET /clients.json
  def index
    @clients = Client.all
  end

  # GET /clients/1
  # GET /clients/1.json
  def show
    @client = Client.find_by_client_ident(params[:id])
    @site = @client.sites
  end

  # GET /clients/new
  def new
    @client = Client.new
  end

  # GET /clients/1/edit
  def edit
  end

  # POST /clients
  # POST /clients.json
  def create
    @client = Client.new(client_params)

    respond_to do |format|
      if @client.save
        format.html { redirect_to [:admin, @client], notice: 'Client was successfully created.' }
        format.json { render :show, status: :created, location: @client }
      else
        format.html { render :new }
        format.json { render json: @client.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /clients/1
  # PATCH/PUT /clients/1.json
  def update
    respond_to do |format|
      if @client.update(client_params)
        format.html { redirect_to [:admin, @client], notice: 'Client was successfully updated.' }
        format.json { render :show, status: :ok, location: @client }
      else
        format.html { render :edit }
        format.json { render json: @client.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /clients/1
  # DELETE /clients/1.json
  def destroy
    @client.destroy
    respond_to do |format|
      format.html { redirect_to [:admin, @client], notice: 'Client was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_client
      @client = Client.find_by_client_ident(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def client_params
      params.fetch(:client, {}).permit(:client_ident, :client_name, :street_number, :street_name, :unit_apt, :grid, :city,
                             :province, :postal_code, :office_tel, :office_ext, :cell_tel, :fax, :contact_email, :same_as_above,
                             :bill_to_client_name, :bill_to_street_number, :bill_to_street_name, :bill_to_grid, :bill_to_city,
                             :bill_to_province, :bill_to_postal_code)
    end
end

EDIT #3 添加错误截图

编辑 #4 - 单击“站点显示页面”上的编辑链接时添加 Rails 服务器日志

Started GET "/admin/clients/9A81622C/sites/88AA" for ::1 at 2016-05-25 14:10:51 -0600
Processing by Admin::Clients::SitesController#show as HTML
  Parameters: {"client_id"=>"9A81622C", "id"=>"88AA"}
  Admin Load (0.4ms)  SELECT  "admins".* FROM "admins" WHERE "admins"."id" = $1  ORDER BY "admins"."id" ASC LIMIT 1  [["id", 1]]
  Site Load (0.2ms)  SELECT  "sites".* FROM "sites" WHERE "sites"."site_ident" = $1 LIMIT 1  [["site_ident", "88AA"]]
  Client Load (0.2ms)  SELECT  "clients".* FROM "clients" WHERE "clients"."id" = $1 LIMIT 1  [["id", 9]]
  Rendered admin/clients/sites/show.html.erb within layouts/application (3.4ms)
Completed 500 Internal Server Error in 12ms (ActiveRecord: 0.8ms)

【问题讨论】:

  • 显示 Admin::SitesController 中的内容。检查您是否在编辑操作中正确分配了@client 变量。错误说@client 是nil - 所以无法生成edit_admin_client_site_path 因为@site 必须与@client 相关并且不能与nil client 相关,显然。
  • 我已经添加了控制器,看来我已经在需要的地方添加了变量。
  • 我的建议是深入调试。在 show 操作中检查 @client 的值。检查链接轨生成的内容。检查请求是否进入编辑操作。

标签: ruby ruby-on-rails-4 model-view-controller nested-routes


【解决方案1】:

看起来问题可能出在您的 Sites 控制器中。在查看您如何尝试设置 @client 时,我发现您将其设置为:@client = Client.find_by_client_ident(params[:id])

但在嵌套params[:id] 的这一级别将引用站点的ID,而不是客户端的ID。不知道您是如何使用/存储 client_ident 与客户端的实际 id 相比,我本来希望看到以下内容:

@client = Client.find_by_id(params[:client_id])

这是因为在您的路线中,您已为 .../clients/:client_id/sites/:site_id 设置了路径,因此 :client_id 将被传入。在这种情况下,当您尝试在您正在传递的站点控制器中查找客户端时站点的 id 作为参数,而不是 client_id

如果您的 client_ident != client.id 那么我会避免尝试使用 params[:id] 来查找/加载 Client.find_by_client_ident 的记录,因为这些不应该匹配(除非,就像我说的,我'我在你如何实现 client_ident 方面遗漏了一些东西)。

让我们知道您的想法以及该更改是否适合您:)

【讨论】:

  • client_ident 本质上只是 id:1,2,3 等常用的虚荣/替代品。但我希望将它们放在 url 中,以使某人更难跳来跳去可能获得对应用程序的访问权限。
  • 我也试图暗示你的修复,但是我仍然收到同样的错误,原始错误没有变化。
  • @ShawnWilson 当您进行更改时,您将sites#show 操作更改为什么?您能否将byebugbinding.pry(无论您使用哪个)放入sites#show 操作中,然后看看@client 的值是多少?
  • 我更改了显示操作以反映您的 @client = Client.find_by_id(params[:client_id]) 并且显示页面上的编辑链接是 我仍然得到同样的错误
  • 显示页面浏览器中的当前 URL 是否显示:client_identclient_id?如果它显示 ident 值,那么我们可能必须使用以下内容:@client = Client.find_by_client_ident(params[:client_id]) 虽然路径中的值可能是 client_ident,但 rails(由于路由)仍将其视为 client_id 用于参数目的。 (手指和脚趾交叉)
【解决方案2】:

我知道这个问题可能很久以前就已经回答了.. 但是查看您的代码我发现这里有一个错误

resouurces :clients do

资源

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-14
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 2012-10-02
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多