2016-11-28 108 views
3

我在Rails的一个“创建”操作方法,并做到:验证失败时,Rails如何保留表单数据?

def create 
    @movie = Movie.new(movie_params) 
    if @movie.save 
    redirect_to @movie, notice: "Movie successfully created" 
    else 
    render :new 
    end 
end 

现在,我有几个验证到位的Movie模型。在这些情况下,验证失败,@movie.save返回false,我只需调用new模板(不沾new行动开展以来,render :new相同render template: 'new'

我不明白Rails这样可以保持表单数据,我已经进入时,它再次将呈现new图,这是怎么回事引擎盖,允许它这样做?

回答

7

让我们试着去了解这整个过程中,控制器的动作定义的逐点

实例变量与渲染视图共享。

在你的情况我假设有一个new动作像

def new 
    @movie = Movie.new 
end 

而且你,你已经创建了这样

= form_for @movie do |f| 

形式现在相应的视图new.html.erb,因为您知道@movie对象,您在form_for中传递的方法是在new中定义的操作。大多数情况下,我们不会将任何参数传递给new方法中的new操作。当您加载表单时,表单字段为空,因为对象的属性(在您的案例中为@movie)默认为空,因为我们只是初始化一个空对象(Movie.new)。

让我们假设你的Movie模型具有name属性,尝试在你的new动作现在做这个工作

def new 
    @movie = Movie.new(name: 'Hello World!') 
end 

时,你会加载新的动作,你会看到Hello World!填入您的name文本字段,因为您的@movie对象使用此值进行初始化。

另外,请记住,在这种情况下,Rails Convention-Over-Configuration会自动生成表单URL,默认情况下它指向create操作。当您提交form时,会向创建操作发出请求。这带我到下一个点。

当我们提交所有填写表单值被送到其路径与表单URL匹配的操作(在你的情况URL指向create行动)的形式

create动作您收到以模型属性(Movie属性)作为关键字并填充信息作为其值的散列形式的参数。在create行动的第一行是

@movie = Movie.new(movie_params) 

这是代码的一个非常重要的线,试着去了解这一点。假设您的表单只有一个文本字段,即name。现在movie_params是看起来像这样

def movie_params 
    params.require(:movie).permit(:name) 
end 

现在的方法,该方法movie_params会返回一个哈希像{ 'name' => 'Hello World!' },你现在通过这个哈希作为参数传递给Movie.new方法。

所以现在分手的代码之后,您创建行动的第一线看起来像

@movie = Movie.new({ name: 'Hello World!' }) 

这意味着您的@movie实例变量包含name属性设置为Hello World!Movie类的一个对象。在这里,初始化后,如果你做@movie.name它将返回Hello World!

现在,在第二行中,您正在调用@movie.save,由于您的案例中的验证失败,因此返回false,因为您已经在问题中提到过了。当它返回false执行将转到else部分。现在这带我到下一个点。

在控制器中调用render :action(在你的情况render :new)只呈现属于该动作的视图,并且不执行该动作码。

在你的情况下,你调用render :new,所以你实际上在创建操作中渲染了new.html.erb视图。换句话说,您只需使用new.html.erb中的代码,而不是使用new操作中的代码。这里,render :new实际上并未调用新操作,它仍处于创建操作中,但呈现new.html.erb视图。现在

,在new.html.erb你已经创建了一个看起来像

= form_for @movie do |f| 

现在,当我在我的第一点解释的形式,即在行动中声明的实例变量被渲染视图共享,在这case @movie对象,您在create中定义的操作由创建操作中呈现的new.html.erb共享。在我们的示例中,在create操作中,@movie对象已使用参数(movie_params)中收到的一些值进行了初始化,现在new.html.erbelse中呈现时,默认情况下在窗体中使用相同的@movie对象。你明白了吗,你看到魔法了吗?

这就是Rails的工作原理,这就是为什么当我们遵循约定时它真棒! :)

这些博客会帮助你理解的代码更好,

http://blog.markusproject.org/?p=3313

这主旨是伟大的,

https://gist.github.com/jcasimir/1210155

最后但并非最不重要的,官方的Rails指南布局和渲染

http://guides.rubyonrails.org/v4.2/layouts_and_rendering.html

希望上面的例子能够解决您的疑惑,如果没有,请随时在下面的评论框中放下您的查询。 :)

+2

这是一个坚实的回应,但海报也要求官方消息来源参考。补充一点,我会说这是值得的赏金! – toobulkeh

+1

@toobulkeh添加官方消息来源和参考。 – RSB

+2

@RSB - 这个回应帮助了我很大的时间。我不知道为了重新填充表单,我的create方法中的对象需要与新窗体中的相同。你在网上教授编程课吗? –

1

form_for助手从落后@movie变量取数据。在create行动形成数据分配给@movie变量。当你调用render :new的form_for需要列的数据来自@movie变量。

1

我不知道你想去的引擎盖下有多深,但基本上当你发送到创建方法的数据传递到paramsparams只是一个关键:值对,其中密钥和值是字符串,但rails有一个特殊的语法和方法可以转化为哈希值。 params数据通过Movie模型进行处理,结果存储在@movie。当表单呈现时,@movie日期被传递回表单 - 该数据用于重新填充表单。

我会推荐这个blog postthe rails guide进一步阅读。

0

我会尽量解释一点:在方法 创建首先我们设置实例变量

@movie = Movie.new(movie_params) 

@movie在这一刻充满了movie_params 后只会验证刹车领域,我们说的Rails 'render:new',变量为@movie

这是相同的,如果我们分配属性为形式:

= form_for Movie.new(movie_params) do ... 
0

当您提交表单。您可以调用create方法,其中movie_params的所有值均在@movie中初始化。现在,由于任何原因代码中断,您可以拨打相同对象render new@movie)。所以形式提出了价值观。

意味着整个过程中您的@movie对象持续存在。