Rails Devise在控制器中构建嵌套模型 [英] Rails Devise Build nested model in controller
问题描述
我使用 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>。我想你有两个选择:
- 不要调用
super
。只需将第一行从默认new
放在new
开头,最后一行从默认< - 覆盖
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:
- Don't call
super
. Just put the first line from the defaultnew
at the start of yournew
, and the last line from the defaultnew
at the end of yours. - Override
build_resource
instead ofnew
. At the beginning of your overriddenbuild_resource
, callsuper
and then add the code that was in yournew
after thesuper
in yourbuild_resource
. The only extra thing you'll need to do is check whetherbuild_resource
was called withnil
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., becausebuild_resource
must have been called fromcreate
, 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屋!