Nested registration data in Rails 3.1 with Devise

2020-07-22 16:47发布

Hello again, stackoverflow community!

I'm working on writing a simple blog system in Rails, and I'm using Devise for the authentication part.

I got the basic email/password registration going, and now I've set up a one-to-one connection of the users table (the basic one generated by devise) and a UserData table, containing things such as username, permissions, about, and so on.

It's pretty easy - the user table is 100% devise vanilla, and has_one UserData. Userdata belongs_to a user.

I tried expanding the sign up form generated by Devise, so that it would allow a user to enter, not just his e-mail and password, but also his desired user name. That way, when the form would be sent, if the names of the form fields would be correct (the right structure of nested hashes, such as "email" for the user's email, and "UserData[name]" for the user's desired name), the new user would be created automatically, and the proper corresponding UserData entry should also be created automatically by Rails, correct?

Right now, I'm having a huge issue with the UserData[name] field not appearing at all... Here's the file I'm having trouble with...

new.html.erb

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>

  <div><%= f.label :email %><br />
<%= f.email_field :email %></div>


#This is the tidbit that doesn't work
<%= f.fields_for "UserData" do |ud_f| %>
  <%= ud_f.label "username" %>
  <%= ud_f.text_field :name %>
<% end %>

  <div><%= f.label :password %><br />
  <%= f.password_field :password %></div>

<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>

<div><%= f.submit "Sign up" %></div>
<% end %>

<%= render :partial => "devise/shared/links" %>

I have been searching for an answer for hours and hours, but to no avail. If I purposely misspell the word "UserData" in the fields_for block, it prints out the field. This is strange, and I have no idea what's making that field disappear...

I'd really appreciate a little bit of help! Thanks for your time! :)

edit 1+2:

After a few messing around, I've overridden devise's default registration controller and created my own. I've edited the devise route to call my custom controller as timbrandes suggested, but I can't seem to access @user in my controller. Here's the code I have so far...

class RegistrationsController < Devise::RegistrationsController
  def new
   resource = build_resource({})
   resource.UserData = UserData.new
   respond_with resource
  end

  def create
    logger.debug "\n\n\nIN CREATE\n\n\n\n"
    logger.debug params.to_json
    super
  end

  def update
    super
  end
end

2条回答
家丑人穷心不美
2楼-- · 2020-07-22 17:11

Well, a lot of time has passed, but I eventually got it working by scrapping and re-doing most of my Devise code. To be honest, it wasn't that much to re-write, and now it works great.

For any future developers browsing this (slightly confusing) question and answer, the only advice I can give you is to make sure to write structured form code, especially for nested resources. This is my current registrations_controller.rb that works:

class RegistrationsController < Devise::RegistrationsController
  def new
    resource = build_resource({})
    resource.build_user_data
    respond_with resource
  end

  def create
    resource = build_resource(params[:user])

    if(resource.save)
      sign_in(resource_name, resource)
      respond_with resource, :location => after_sign_up_path_for(resource)
    else
      render :action => "new"
    end


  end

  def update
    super
  end
end 

And the .erb for the registration form:

<div class="form-box">  

    <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
      <%= devise_error_messages! %>

      <div><%= f.label :email %>
      <%= f.email_field :email %></div>

        <%= f.fields_for(:user_data)  do |udf| %>
          <%= udf.label :name, "Desired user name" %>
          <%= udf.text_field :name %>

          <%= udf.label :roles, "Privileges"%>
          <% for role in UserData::ROLES %>
               <%= check_box_tag "user[user_data_attributes][roles][]" , role, resource.user_data.roles.include?(role) %>
               <%=h role.humanize %><br />
          <% end %>
          <%= hidden_field_tag "user[user_data_attributes][roles][]" %>

        <% end %>

      <div><%= f.label :password %>
      <%= f.password_field :password %></div>

      <div><%= f.label :password_confirmation %>
      <%= f.password_field :password_confirmation %></div>

      <div><%= f.submit "Sign up" %></div>
    <% end %>

    <%= render :partial => "devise/shared/links" %>

</div>
查看更多
啃猪蹄的小仙女
3楼-- · 2020-07-22 17:12

There is a reason your fields don't appear, that is, you have to call (for example) @user.user_data.build somewhere in your Controller (RC #196 Nested Model Form Part 1) to make an instance of the user_data object that relates to the fields_for fields.

Own way I can think of is to go with a custom devise controller (see list of Controllers), pick the corresponding method for registration and add something like @user.user_data.build to it.

The other way is to denormalize your data scheme and put everything into User itself (not really an answer to your question but anyway ;-)). Plus: no messing with monkey patched Devise Controllers.

Make sure that you have defined accepts_nested_attributes_for :user_data in your devise model.

查看更多
登录 后发表回答