在 rails 中使用设计,尝试在注册后添加第二个步骤以更新用户的姓名、个人简介和照片

Using devise in rails, trying to add a second step after Signup to update user with name, bio and photo

我是编码和 Whosebug 的新手,所以我希望我要问的问题不会太愚蠢 我想做的是在标题中:用户登录(电子邮件、密码)后,他们将被重定向到一个新表单,他们可以在其中填写他们的姓名、个人简介和照片。 一切都按计划进行,直到他们点击提交并遇到 2 个问题:

1/ 不考虑新数据 2/ 重定向错误

在 sign_up 之后,用户被重定向到 "profiles/:id/edit"。提交表单后,它重定向到 "user/:id",它不存在。

它不应该重定向到控制器中的 profiles#update 吗?

这是我的不同代码:

1条路线

Rails.application.routes.draw do
  devise_for :users
  root to: "pages#home"
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  resources :jam_sessions, only: [:show, :index, :new, :create] do
    resources :spots, only: [:show, :create, :new]
    resources :messages, only: [:create]
  end
  resources :participations, only: [:create, :update]

  resources :profiles, only: [:show, :edit, :update]
  resources :dashboards, only: [:index, :show]


  resources :reviews, only: [:create]


  mount ActionCable.server => "/cable"

  get 'users/:id', :to => 'profiles#edit', :as => :user

  patch 'profiles#edit', :to => 'profiles#update'
end 

2/profiles_controller

class ProfilesController < ApplicationController
  def show

    @user = User.find(params[:id])
    @reviews = Review.where(receiver_id: @user.id)
    @instruments = UserInstrument.where(user_id: @user.id)
    @participations = Participation.where(user_id: @user.id)
    date = Time.now

    if @participations != nil
      @jam_sessions = []

      @participations. each do |participation|
        spot = Spot.find_by(id: participation.spot_id)
        @jam_sessions << JamSession.find_by(id: spot.jam_session_id)

      end
      @future_jam_sessions = []
      @past_jam_sessions = []
      @jam_sessions.each do |jam_session|
        if jam_session.starts_at > date
          @future_jam_sessions << jam_session
        else
          @past_jam_sessions << jam_session
        end
      end
    else
      puts "no jam"
    end
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    @user.update(user_params)
    raise
    if @user.save
      redirect_to profiles_path(@user)
    else
      render "new"
    end
  end
  private

    def user_params
      params.require(:user).permit(:first_name, :last_name, :bio)
    end
end

3/application_controller

class ApplicationController < ActionController::Base
  before_action :authenticate_user!, except: [:home, :index, :show]

  before_action :configure_permitted_parameters, if: :devise_controller?
  def configure_permitted_parameters
    # For additional fields in app/views/devise/registrations/new.html.erb
    devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :bio])
    # For additional in app/views/devise/registrations/edit.html.erb
    devise_parameter_sanitizer.permit(:account_update, keys: [:username])
  end

  def default_url_options
    { host: ENV["DOMAIN"] || "localhost:3000" }
  end

end

4/用户模型

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :user_instruments, dependent: :destroy
  has_many :instruments, through: :user_instruments
  has_many :messages, dependent: :destroy
  has_one_attached :photo, dependent: :destroy
  has_many :reviews_written, class_name: "Review", foreign_key: :writer_id
  has_many :reviews_received, class_name: "Review", foreign_key: :receiver_id
  has_many :jam_sessions, dependent: :destroy

  def profile_picture
    if photo.attached?
      photo.key
    else
      "avatar-unknown.png"
    end
  end

  def full_name
    "#{first_name} #{last_name}"
  end
end

5/编辑视图

<%= simple_form_for(@user) do |f| %>
  <%= f.input :first_name %>
  <%= f.input :last_name %>
  <%= f.input :bio %>
  <%= f.input :photo, as: :file %>
    <%= f.submit 'Update profile' %>
<% end %>

谢谢! 奥利维尔

我是克拉拉 :)

所以我发现您的代码存在一些问题,但似乎有些东西已经可以正常工作了,这很好。 所以它正在创建一个新用户,然后重定向到您编辑用户的正确表单,对吗? (只是问,因为我没有看到从设计注册表到用户的重定向#edit)。

在你的应用程序控制器中,你有这一行,这是不必要的: devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :bio]) 如果您将直接将它们添加到设计注册表中,则只需要允许设计表单的其他参数。但是在这里您要添加一个新表单。

那么现在,我们该如何处理新表格呢? 您的代码中的问题非常微妙。您正在更改用户对象,但您选择使用 profiles controller 包括配置文件路由(但您也有一些用户路由)。问题是,在编辑用户表单中,以下行决定了当有人点击提交时 HTTP 请求的去向。

<%= simple_form_for(@user) do |f| %>

打开浏览器,查看生成html的inspect模式,会是这样的(会有更多的东西,但这是我们感兴趣的部分)

<form action="/users" accept-charset="UTF-8" method="patch">

这意味着,当有人点击 submit 时,将向 /users 发出 HTTP PATCH 请求 现在,您的路线目前的构建方式不适合这种情况。

所以你可以添加一条新路线

resources :users, only [:update]

An 到 users_controller#update 你可以把你现在在 profiles_controller#update 的代码。

现在这会给我们留下一个奇怪的情况,即编辑部分在配置文件控制器中,而更新部分在用户控制器中。您可以通过两种方式解决这个问题

  1. 将所有内容移至用户控制器。这样您就可以为用户控制器编辑和更新路由,编辑和更新操作(与代码中的 profile_controller#edit 和 #update 相同)和视图。不要忘记删除配置文件中的内容,否则两周后会非常混乱。
  2. 告诉简单的表格,不要去 users_controller,而是去 profiles_controller,你可以保持现有的设置。您可以通过添加此行来完成
 simple_form_for :user, url: user_path, method: "PATCH" ```

现在再说几句:

routes.rb中最后一行不正确。而且,您应该使用以下语法,而不是手动定义正常的 CRUD 操作:

...
resources :users, only: [:edit, :update]
...

并且在 update 操作中的 profiles controller 中,您需要更改渲染。 (不管你是把它留在那里还是把它移到users_controller)。应该是:

 if @user.save
   redirect_to profiles_path(@user)
 else
   render "edit"
 end

当用户未被保存时,您想要呈现 edit 而不是 new

最后,创建两种不同的表单有一个缺点,但我认为这不是一个大问题:验证。

如果您想对 :bio 进行验证,则它不适用于您现在拥有的此设置,因为在提交设计注册表单时已经创建了用户对象。在这第一点,验证将被测试——所以你无法测试生物是否已经存在。有一些 gems 可以处理这个问题,我还找到了这篇文章以供进一步研究。 https://www.honeybadger.io/blog/multi-step-forms-in-rails/