Rails 4 - настройка, автоматическое создание профиля при создании нового пользователя

Я попытался убрать этот пост, поэтому он не кажется таким уж длинным для новых посетителей. Он включает ошибки, вытекающие из следующих предложений от других в ответах ниже.

Я пытаюсь создать приложение с Rails 4.

Я использую придумывание и omniauth.

У меня есть модели для пользователя и профиля. Пользователь имеет один профиль и профиль принадлежит пользователю.

Моя цель - использовать модель пользователя для всех вещей, которые пользователь не затрагивает. Я использую модель профиля для вещей, которые пользователь поддерживает.

Когда создается новый пользователь, я хочу, чтобы пользователь был перенаправлен на страницу своего профиля, который должен быть частично заполнен деталями, созданными в пользовательской модели.

У меня проблемы.

У меня есть:

User.rb

has_one :profile

Profile.rb

belongs_to :user

Omniauth_callbacks_controller.rb

if @user.persisted?
  sign_in_and_redirect_user(:user, authentication.user.profile)
  set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
else
  session["devise.#{provider}_data"] = env["omniauth.auth"]
  redirect_to new_user_registration_url
end

Я также попробовал переадресацию обратного вызова:

 sign_in_and_redirect @user,  event: :authentication

Профили/show.html.erb

<div class="container-fluid">
    <div class="row">
        <div class="col-xs 8 col-xs-offset-1">
            <%= @profile.user.formal_name %>


            <%= @profile.occupation %>
            <%= @profile.qualification.awards %>
            <%= @profile.overview %>
            <%= @profile.research_interests %>

        </div>

        <div class="col-xs 3">
            <div class="geobox">
            <%= render 'addresses/location' %>
            <%= @profile.working_languages %>
            <%= @profile.external_profile %>

            </div>
        </div>  
    </div>

    <div class="row">
        <div class="col-xs 10 col-xs-offset-1">
            <%= render 'profiles/activity' %>
        </div>
    </div>  

</div>

_nav.html.erb

<% if user_signed_in? %>
                    Hi <%= link_to(current_user.first_name.titlecase, profile_path(current_user)) %></span>

Я также пробовал:

_nav.html.erb

<% if user_signed_in? %>
                    Hi <%= link_to(current_user.first_name.titlecase, user_profile_path(current_user.profile)) %></span>

Что мне нужно сделать, чтобы добавить какую-то функциональность, чтобы позволить новому пользователю перейти на страницу своего профиля?

В настоящее время, когда я пытаюсь войти в систему и нажав ссылку на навигационную панель, я хочу перейти на страницу профиля. Вместо этого я перехожу к сообщению об ошибке, в котором говорится, что страница, которую я ищу, не существует. Путь, который он ищет, начинается с профиля (а не пользователя/профиля) - не уверен, что это подсказка.

Мои маршруты:

Профили:

profiles GET       /profiles(.:format)                    profiles#index
                         POST      /profiles(.:format)                    profiles#create
             new_profile GET       /profiles/new(.:format)                profiles#new
            edit_profile GET       /profiles/:id/edit(.:format)           profiles#edit
                 profile GET       /profiles/:id(.:format)                profiles#show
                         PATCH     /profiles/:id(.:format)                profiles#update
                         PUT       /profiles/:id(.:format)                profiles#update
                         DELETE    /profiles/:id(.:format)                profiles#destroy

Пользователь:

user_password POST      /users/password(.:format)              devise/passwords#create
       new_user_password GET       /users/password/new(.:format)          devise/passwords#new
      edit_user_password GET       /users/password/edit(.:format)         devise/passwords#edit
                         PATCH     /users/password(.:format)              devise/passwords#update
                         PUT       /users/password(.:format)              devise/passwords#update
cancel_user_registration GET       /users/cancel(.:format)                users/registrations#cancel
       user_registration POST      /users(.:format)                       users/registrations#create
   new_user_registration GET       /users/sign_up(.:format)               users/registrations#new
  edit_user_registration GET       /users/edit(.:format)                  users/registrations#edit
                         PATCH     /users(.:format)                       users/registrations#update
                         PUT       /users(.:format)                       users/registrations#update
                         DELETE    /users(.:format)                       users/registrations#destroy
       user_confirmation POST      /users/confirmation(.:format)          devise/confirmations#create
   new_user_confirmation GET       /users/confirmation/new(.:format)      devise/confirmations#new
                         GET       /users/confirmation(.:format)          devise/confirmations#show
             user_unlock POST      /users/unlock(.:format)                devise/unlocks#create
         new_user_unlock GET       /users/unlock/new(.:format)            devise/unlocks#new
                         GET       /users/unlock(.:format)                devise/unlocks#show
           finish_signup GET|PATCH /users/:id/finish_signup(.:format)     users#finish_signup

NEW ATTEMPT: В дополнение к предложению ниже я вложил ресурсы как:

  resources :users do
     resources :profile
  end

и я изменил перенаправление на:

def self.provides_callback_for(provider)
    class_eval %Q{
      def #{provider}
        @user = User.find_for_oauth(env["omniauth.auth"])

        if @user.persisted?
           sign_in_and_redirect [current_user, current_user.profile || current_user.build_profile]

          set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
        else
          session["devise.#{provider}_data"] = env["omniauth.auth"]
          redirect_to new_user_registration_url
        end
      end
    }
  end

Я получаю эту ошибку: NoMethodError (undefined метод `profile 'для nil: NilClass):

СЛЕДУЮЩЕЕ ПОСЛЕДСТВИЯ:

Я сохраняю все вышеперечисленное, но попробуйте заменить перенаправление следующим образом:

def self.provides_callback_for(provider)
    class_eval %Q{
      def #{provider}
        @user = User.find_for_oauth(env["omniauth.auth"])

        if @user.persisted?
           sign_in_and_redirect [@user, @user.profile || @user.build_profile]

          set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
        else
          session["devise.#{provider}_data"] = env["omniauth.auth"]
          redirect_to new_user_registration_url
        end
      end
    }
  end

RuntimeError (Could not find a valid mapping for [#<User id: 1 ... 

а затем он перечисляет все атрибуты в таблице пользователя, а затем показывает:

#<Profile id: nil, user_id: 1, 

за которым следуют все атрибуты в таблице профилей.

НОВАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ

Я нашел это сообщение: Rails Попытка создать профиль после создания пользователя

И я попробовал добавить:

 after_create do
    create_user_profile
  end

для моей модели пользователя.

Я сохранил перенаправление так же, как и у меня (после предложения Алексея) и попытался снова.

Я получаю такое же сообщение об ошибке:

RuntimeError (Could not find a valid mapping for [#<User id: 1

Может ли кто-нибудь увидеть, как это решить?

ДАЛЬНЕЙШАЯ ПОПЫТКА:

Я нашел этот пост:   Ruby on Rails - создание профиля при создании пользователя

который говорит для Rails 4 (который я использую), мне нужен только этот обратный вызов:

after_create: create_profile

Когда я пытаюсь это сделать, я получаю ту же ошибку, что и выше.

AN IDEA:

Я использовал генератор лесов для моего контроллера профиля.

Мой метод создания:

  def create
    @profile = Profile.new(profile_params)

    respond_to do |format|
      if @profile.save
        format.html { redirect_to @profile }
        format.json { render :show, status: :created, location: @profile }
      else
        format.html { render :new }
        format.json { render json: @profile.errors, status: :unprocessable_entity }
      end
    end
  end

Возможно, эта ошибка связана с тем, что идентификатор пользователя не был специально указан в этом методе. У меня есть whitelisted user_id в сильных параметрах для контроллера профилей.

НОВАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ

В этой статье предлагается использовать build_profile вместо create_profile.

https://stackoverflow.com/info/8337682/create-another-model-upon-user-new-registration-in-devise

Я изменил свой user.rb на

  after_create :build_profile

Когда я сохраняю это и пытаюсь получить ту же ошибку.

ДРУГОЙ ПОПЫТКА

Ответ Matt в этом сообщении предполагает, что я должен включить определение build_profile как:

 after_create :build_profile

  def build_profile
    Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID directly.
  end

Я изменил свой user.rb на:

  after_create :build_profile

  def build_profile
    Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID directly.
  end

Когда я сохраняю и пытаюсь это сделать, я получаю аналогичную ошибку, но с дополнительной информацией. Журналы все еще показывают:

RuntimeError (Could not find a valid mapping for [#<User id: 1,

Но после всего этого сообщения об ошибке он дает эту новую информацию:

SQL (2.9ms)  INSERT INTO "profiles" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["user_id", 1], ["created_at", "2015-12-18 21:58:49.993866"], ["updated_at", "2015-12-18 21:58:49.993866"]]
2015-12-18T21:58:49.966648+00:00 app[web.1]:   Profile Load (1.5ms)  SELECT  "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT 1  [["user_id", 1]]
2015-12-18T21:58:50.007428+00:00 app[web.1]: Completed 500 Internal Server Error in 113ms (ActiveRecord: 21.8ms)
2015-12-18T21:58:49.941385+00:00 app[web.1]:   User Load (1.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]

Означает ли это, что это проблема?

ОБНОВЛЕНИЕ

Я попробовал это снова (без внесения каких-либо изменений в код). Теперь я получаю сообщение об ошибке, но на этот раз журналы говорят:

RuntimeError (Could not find a valid mapping for [#<User id: 1, first_name: "Me", last_name: "Ma", email: "[email protected]", encrypted_password: "$2a$jqQn..HSvlcNtfAH/28.DQoWetO...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 16, current_sign_in_at: "2015-12-15 09:57:14", last_sign_in_at: "2015-12-15 09:27:07", current_sign_in_ip: #<IPAddr: IPv4:49.191.55>, last_sign_in_ip: #<IPAddr: IPv4:49.191.135.255.255>, confirmation_token: nil, confirmed_at: "2015-12-15 09:02:58", confirmation_sent_at: "2015-12-15 08:42:48", unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil, created_at: "2015-12-09 23:53:20", updated_at: "2015-12-15 09:57:14", image: nil>, #<Profile id: 1, user_id: 1, title: nil, hero_image: nil, overview: nil, occupation: nil, external_profile: nil, working_languages: nil, created_at: "2015-12-18 21:58:49", updated_at: "2015-12-18 21:58:49">]):

Разница на этот раз заключается в том, что в профиле есть ключ идентификатора пользователя из 1. Ранее это было ноль.

Ответ 1

Вы можете использовать вложенные ресурсы

resources :users do
  resource :profile
end

а затем

redirect_to [current_user, current_user.profile || current_user.build_profile]

Ответ 2

Вы добавили sing_in_and_redirect_user в свой контроллер обратных вызовов. Чтобы отредактировать путь, к которому он перенаправляется, вы также должны переопределить и отредактировать after_sign_in_path_for в applications_controller.

class ApplicationController < ActionController::Base
  def after_sign_in_path_for(resource)
    request.env['omniauth.origin'] || stored_location_for(resource) || root_path
  end
end

Здесь вы можете отредактировать фактический путь, на который перенаправляется. Не уверен в части omniauth, но вы можете добавить profile_path(resource.profile).

Ссылка: https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in