Rails Devise在控制器中构建嵌套模型 [英] Rails Devise Build nested model in controller

查看:168
本文介绍了Rails Devise在控制器中构建嵌套模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Devise ,并尝试在我的 RegistrationsController 中建立嵌套模型。



虽然这不工作。我可以通过

建立嵌套模型

  resource.build_nested_model 


这是我的 registrationscontrollers的新动作 / strong>

  def new 
super
resource.build_user_info
resource.user_info.languageskills .build
如果params [:is_driver] .to_i == 1
resource.build_driver
end
Rails.logger.debug(resource.build_user_info.inspect)
end

这是输出

 为127.0.0.1开始GET/ en / sign_up?is_driver = 1在2014-02-13 13:20:01 +0100 
由RegistrationsController处理#new as JS
参数:{is_driver=>1,locale=>en}
呈现的注册/ _new_user_fields.html.erb
呈现的布局/ new.html.erb in layouts / application(27.6ms)
呈现的布局/ _header.html.erb(2.8ms)
呈现的布局/ _messages.html.erb ms)
呈现的布局/ _footer.html.erb(0.6ms)
(0.2ms)开始事务
(0.1ms)提交事务
#< UserInfo id:nil, user_id:nil,first_name:nil,last_name:nil,year_of_birth:nil,city:nil,created_at:nil,updated_at:nil,gender_id:nil,interest:nil,about:nil,country_alpha2:nil>
在117ms完成200 OK(视图:96.5ms | ActiveRecord:0.4ms)

是不是可能?我猜想devise不会保存我对资源的更改,当我构建相关联的模型。我看到的唯一出路是定义我自己的变量持有更新的资源的一个完整的副本。这不是一个好的做法,但。



您会做什么?

解决方案

默认的 RegistrationsController.new 只是:

  def new 
build_resource({})
respond_with self.resource
end

这取决于你的版本的设计,但它似乎是这个或等同的很长的路。)



这意味着视图呈现在您重写的中添加嵌套模型之前,因为您在调用 super / code>。我想你有两个选择:


  1. 不要调用 super 。只需将第一行从默认 new 放在 new 开头,最后一行从默认<

  2. 覆盖 build_resource 而不是。在您重写的 build_resource 开始时,调用 super ,然后添加 中的 super 之后的你唯一需要做的是检查 build_resource 是否用 nil 或空哈希调用这种情况你构建你的空白user_info等,或者如果它是用非空哈希调用,那么不要添加你的空白user_info等,因为 build_resource 必须已经从 create 调用,因此哈希将包含您在user_info的表单上输入的任何内容,因此您不想使用空白版本覆盖! (你可以检查当前的url或类似的东西,而不是检查哈希参数,但我个人喜欢有点少。)

我以前使用过选项2。我喜欢它,因为它仍然调用 super ,所以我们仍然使用devise的实现 build_resource ,而选项1完全忽略devise的 new 的实现 - 如果他们对他们的 new 进行一些重要的更改,错过了? (例如,以前有一个资源变量局部于 new ,但它现在在 self ,你可以看到在上面的代码。)选项2只是一个更加fiddly,因为你需要检查是否应该添加你的空白user_info等等。这是由你的品位! p>

I'm using Devise and am trying to build nested models in my RegistrationsController.

Though this is not working. I can build nested models via

resource.build_nested_model

in the view, but not in the controller itself.

This is my registrationscontrollers' new-action

  def new
    super
    resource.build_user_info
    resource.user_info.languageskills.build
    if params[:is_driver].to_i == 1
      resource.build_driver
    end
    Rails.logger.debug(resource.build_user_info.inspect)
  end

This is the output it generates:

Started GET "/en/sign_up?is_driver=1" for 127.0.0.1 at 2014-02-13 13:20:01 +0100
Processing by RegistrationsController#new as JS
  Parameters: {"is_driver"=>"1", "locale"=>"en"}
  Rendered registrations/_new_user_fields.html.erb (12.1ms)
  Rendered registrations/new.html.erb within layouts/application (27.8ms)
  Rendered layouts/_header.html.erb (2.8ms)
  Rendered layouts/_messages.html.erb (0.2ms)
  Rendered layouts/_footer.html.erb (0.6ms)
   (0.2ms)  begin transaction
   (0.1ms)  commit transaction
#<UserInfo id: nil, user_id: nil, first_name: nil, last_name: nil, year_of_birth: nil, city: nil, created_at: nil, updated_at: nil, gender_id: nil, interests: nil, about: nil, country_alpha2: nil>
Completed 200 OK in 117ms (Views: 96.5ms | ActiveRecord: 0.4ms)

Why is it not possible? I guess devise isn't saving my changes to the resource, when I build the associated models. The only way out I see is defining my own variable holding a whole copy of the updated resource. This is not a good practice, though.

What would you do?

解决方案

I think the problem is that the default RegistrationsController.new is just:

def new
  build_resource({})
  respond_with self.resource
end

(It depends on your version of devise, but it seems to be this or equivalent for quite a long way back.)

This means that the view is rendered (by respond_with) before you add your nested models in your overridden new because you do this after calling super. I think you have two options:

  1. Don't call super. Just put the first line from the default new at the start of your new, and the last line from the default new at the end of yours.
  2. Override build_resource instead of new. At the beginning of your overridden build_resource, call super and then add the code that was in your new after the super in your build_resource. The only extra thing you'll need to do is check whether build_resource was called with nil or an empty hash, in which case you build your blank user_info etc., or if it was called with a non-empty hash then don't add your blank user_info etc., because build_resource must have been called from create, so the hash will contain whatever your user entered on the form for user_info, so you don't want to overwrite with a blank version! (you could perhaps check the current url or something like that instead of checking the hash parameter, but I personally like that a little less.)

I have used option 2 in the past. I like it because it still calls super, so we're still using devise's implementation of build_resource, whereas option 1 completely ignores devise's implementation of new -- and what if they make some important change to their new in future which you are then missing out on? (For example, there used to be a resource variable local to new, but it's now on self as you can see in the code above.) Option 2 is just a bit more fiddly because you need to check whether you should add your blank user_info etc. So it's up to your taste!

这篇关于Rails Devise在控制器中构建嵌套模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆