新手问题 [已解决] 谁来解读一下 Rails 4.2.0 中的 respond_to 与 respond_with?诸如 redirect_to [:admin, @project] 怎么写?

chairy11 · January 16, 2015 · Last by themorecolor replied at January 16, 2015 · 6502 hits

问题

Rails 4.2 脚手架生成的代码,好像是上面一句 respond_to :html,下面各个 respond_with。看起来是简洁了点,但我其实不太理解。 不理解,index, show, new, edit,都没问题,显示正常。 但 redirect_to [:admin, @project], 怎么办? 我用了 respond_with([:admin,@project]) ,会出错,提示说 undefined method `persisted?' for #Array:... 如果生生在下面加一个 redirect_to [:admin, @project],也会出错,提示说有双重 redirect 什么的……

附:

  • Rails 4.2 脚手架生成的代码 ```ruby class Admin::ProjectsController < Admin::ApplicationController before_action :set_project, only: [:show, :edit, :update, :destroy]

respond_to :html

def index @projects = Project.all respond_with(@projects) end

def show respond_with(@project) end

def new @project = Project.new respond_with(@project) end

def edit end

def create @project = Project.new(project_params) @project.save respond_with(@project) end

def update @project.update(project_params) respond_with(@project) end

def destroy @project.destroy respond_with(@project) end

private def set_project @project = Project.find(params[:id]) end

def project_params params.require(:project).permit(:background, :solution, :result) end end


- 我想跳转到后台的相关页面,这样写会出错,怎么写才正确?
```ruby
def update
    @service.update(service_params)
    respond_with([:admin,@service])
  end
  • Rails 4 生成的脚手架代码,用来对比 ```ruby class Admin::ProjectsController < Admin::ApplicationController before_action :set_project, only: [:show, :edit, :update, :destroy]

def index @projects = Project.all end

def show end

def new @project = Project.new end

def edit end

def create @project = Project.new(project_params)

respond_to do |format| if @project.save format.html { redirect_to [:admin, @project], notice: '项目创建成功!' } format.json { render :show, status: :created, location: @project } else format.html { render :new } format.json { render json: @project.errors, status: :unprocessable_entity } end end end

def update respond_to do |format| if @project.update(project_params) format.html { redirect_to [:admin, @project], notice: '项目信息成功更新!' } format.json { render :show, status: :ok, location: @project } else format.html { render :edit } format.json { render json: @project.errors, status: :unprocessable_entity } end end end

def destroy @project.destroy respond_to do |format| format.html { redirect_to admin_projects_url, notice: '项目已成功删除!' } format.json { head :no_content } end end

private def set_project @project = Project.find(params[:id]) end

def project_params params.require(:project).permit(:background, :solution, :result) end end



# 解决方案
采用respond_with, 可以参考这个帖子[respond_to Without All the Pain](http://www.justinweiss.com/blog/2014/11/03/respond-to-without-all-the-pain/)了解一下……

我读书少你不要骗我,4.2.0 生成的脚手架明明是这样的:

class ProjectsController < ApplicationController
  before_action :set_project, only: [:show, :edit, :update, :destroy]

  # GET /projects
  # GET /projects.json
  def index
    @projects = Project.all
  end

  # GET /projects/1
  # GET /projects/1.json
  def show
  end

  # GET /projects/new
  def new
    @project = Project.new
  end

  # GET /projects/1/edit
  def edit
  end

  # POST /projects
  # POST /projects.json
  def create
    @project = Project.new(project_params)

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

  # PATCH/PUT /projects/1
  # PATCH/PUT /projects/1.json
  def update
    respond_to do |format|
      if @project.update(project_params)
        format.html { redirect_to @project, notice: 'Project was successfully updated.' }
        format.json { render :show, status: :ok, location: @project }
      else
        format.html { render :edit }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /projects/1
  # DELETE /projects/1.json
  def destroy
    @project.destroy
    respond_to do |format|
      format.html { redirect_to projects_url, notice: 'Project was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_project
      @project = Project.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def project_params
      params[:project]
    end
end

4.2.0 一个需要注意的改动就是类级 respond_to 移到了 gem http://guides.rubyonrails.org/4_2_release_notes.html#respond-with-class-level-respond-to

#1 楼 @Rei 啊?那我的项目代码哪来的?我新建个项目试试……我明明都是用 scaffold 生成的啊!

#1 楼 @Rei 我的妈呀!太神奇了!这下我也不明白了!我刚刚 rails new 新建了个项目,生成的 scaffold 也是

class ProjectsController < ApplicationController
  before_action :set_project, only: [:show, :edit, :update, :destroy]

  # GET /projects
  # GET /projects.json
  def index
    @projects = Project.all
  end

  # GET /projects/1
  # GET /projects/1.json
  def show
  end

  # GET /projects/new
  def new
    @project = Project.new
  end

  # GET /projects/1/edit
  def edit
  end

  # POST /projects
  # POST /projects.json
  def create
    @project = Project.new(project_params)

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

  # PATCH/PUT /projects/1
  # PATCH/PUT /projects/1.json
  def update
    respond_to do |format|
      if @project.update(project_params)
        format.html { redirect_to @project, notice: 'Project was successfully updated.' }
        format.json { render :show, status: :ok, location: @project }
      else
        format.html { render :edit }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /projects/1
  # DELETE /projects/1.json
  def destroy
    @project.destroy
    respond_to do |format|
      format.html { redirect_to projects_url, notice: 'Project was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_project
      @project = Project.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def project_params
      params.require(:project).permit(:backgroud, :result)
    end
end

不行,我要在我刚才那旧项目新建个 scaffold 试试,这简直灵异事件了!!!哪来的代码?!!!

#3 楼 @chairy11 旧项目升级 rails 了没

#1 楼 @Rei 尼玛,好吓人!我在这个项目生成的 scaffold 得到的 controller 依然是这些 respond_with!

class TriesController < ApplicationController
  before_action :set_try, only: [:show, :edit, :update, :destroy]

  respond_to :html

  def index
    @tries = Try.all
    respond_with(@tries)
  end

  def show
    respond_with(@try)
  end

  def new
    @try = Try.new
    respond_with(@try)
  end

  def edit
  end

  def create
    @try = Try.new(try_params)
    @try.save
    respond_with(@try)
  end

  def update
    @try.update(try_params)
    respond_with(@try)
  end

  def destroy
    @try.destroy
    respond_with(@try)
  end

  private
    def set_try
      @try = Try.find(params[:id])
    end

    def try_params
      params.require(:try).permit(:try1, :try2)
    end
end

Undocumented :location option

You can use undocumented :location option to override where respond_to sends if resource is valid, e.g. to redirect to products index page instead of a specific product’s page, use:

respond_with(@product, :location => products_url)  

This is a nice way to add flash if you don’t have a any logic that needs to go around your flash.

def destroy
  @current_user_session.destroy
  respond_with @current_user_session do |format|
    format.html {redirect_to login_path, notice: "You have been logged out"}
  end
end

#4 楼 @Rei 我明明记得我那旧项目一开始就是用的 Rails4.2.0…… 本来我用 4.1.8 写了一部分,后来我看到 4.2 出了,就直接 rails new project,用的就是 4.2,没升级…… 哎呀呀,真的很诡异……

我知道一点,这个是新版本的写法,具体是哪个版本引入的记不清了,scaffold 一直没更新还是用的原来的。

我觉得新的写法更清楚简洁些。声明类型 html、json 等。方法只需要简写,代码减少很多也更易读吧。

format.html 感觉比较怪异的。

#6 楼 @themorecolor #8 楼 @chenge

我以为这个 respond_with 是新式用法,可是昨天查官方 Guide 也没看到,要不我还是用回之前的那款 respond_to 好了……

respond_to do |format|
      format.html { redirect_to projects_url, notice: 'Project was successfully destroyed.' }
      format.json { head :no_content }
    end

#8 楼 @chenge 有些地方简短了,有些也多了啊!比如:

# 旧
def index
    @projects = Project.all
end

# 新
def index
    @tries = Try.all
    # 非要这样声明结果么?
    respond_with(@tries)
end

话说,到底哪种才是最好的用法?

#8 楼 @chenge #6 楼 @themorecolor #4 楼 @Rei 发现我的这个项目竟然内置了 gem 'responders',不过我又单独在 gemfile 里声明了,免得下次升级时就找不到它了…… 决定用 respond_with 版本,因为我看到个帖子respond_to Without All the Pain,感觉的确不错

http://apidock.com/rails/ActionController/MimeResponds/respond_with 3.0 就有这个了,scaffold 没更新所以这个没推广开,很多人都不知道这个的。

#1 楼 @Rei 新的 scaffold 确实是她那样的。

#13 楼 @hanluner 我的也是新的啊。

You need to Sign in before reply, if you don't have an account, please Sign up first.