[英]Rails - Devise Invitable - Two Devise User Models - Password can't be blank error with devise prior to invitation being sent
我正在嘗試使用兩個用戶模型設置 devise_invitable; 用戶和執行主機。 用戶 model 一切正常,首先實現了用戶代碼,然后添加了第二個 model,Exechost。
對於第二個 model,沒有創建或發送 email 邀請,我們被重定向到 devise 注冊表單,出現以下錯誤:
1 個錯誤禁止保存此 exechost:
我也在使用 rolify 和 pundit。
下面是終端 output:
Started POST "/exechosts" for ::1 at 2022-04-07 10:17:33 -0400
10:17:33 web.1 | Processing by Exechosts::RegistrationsController#create as HTML
10:17:33 web.1 | Parameters: {"authenticity_token"=>"[FILTERED]", "exechost"=>{"username"=>"test20", "email"=>"test20@test.com"}, "commit"=>"Add Moderator"}
10:17:33 web.1 | TRANSACTION (0.1ms) BEGIN
10:17:33 web.1 | ↳ app/controllers/exechosts/registrations_controller.rb:18:in `create'
10:17:33 web.1 | Exechost Exists? (0.3ms) SELECT 1 AS one FROM "exechosts" WHERE "exechosts"."email" = $1 LIMIT $2 [["email", "test20@test.com"], ["LIMIT", 1]]
10:17:33 web.1 | ↳ app/controllers/exechosts/registrations_controller.rb:18:in `create'
10:17:33 web.1 | TRANSACTION (0.1ms) ROLLBACK
10:17:33 web.1 | ↳ app/controllers/exechosts/registrations_controller.rb:18:in `create'
10:17:33 web.1 | Rendering layout layouts/application.html.erb
10:17:33 web.1 | Rendering exechosts/registrations/new.html.erb within layouts/application
10:17:33 web.1 | Rendered exechosts/shared/_error_messages.html.erb (Duration: 0.9ms | Allocations: 432)
10:17:33 web.1 | Rendered exechosts/shared/_links.html.erb (Duration: 0.1ms | Allocations: 72)
10:17:33 web.1 | Rendered exechosts/registrations/new.html.erb within layouts/application (Duration: 3.3ms | Allocations: 2073)
10:17:33 web.1 | Exechost Load (0.3ms) SELECT "exechosts".* FROM "exechosts" WHERE "exechosts"."id" = $1 ORDER BY "exechosts"."created_at" ASC, "exechosts"."id" ASC LIMIT $2 [["id", "9335e908-f90a-455c-9eb4-f2f8a7ab29ec"], ["LIMIT", 1]]
10:17:33 web.1 | ↳ app/views/pages/_navBar.html.erb:25
10:17:33 web.1 | ExecRole Load (0.4ms) SELECT "exec_roles".* FROM "exec_roles" INNER JOIN "exechosts_exec_roles" ON "exec_roles"."id" = "exechosts_exec_roles"."exec_role_id" WHERE "exechosts_exec_roles"."exechost_id" = $1 AND (((exec_roles.name = 'owner') AND (exec_roles.resource_type IS NULL) AND (exec_roles.resource_id IS NULL))) [["exechost_id", "9335e908-f90a-455c-9eb4-f2f8a7ab29ec"]]
10:17:33 web.1 | ↳ app/views/pages/_navBar.html.erb:27
10:17:33 web.1 | ExecRole Load (0.6ms) SELECT "exec_roles".* FROM "exec_roles" INNER JOIN "exechosts_exec_roles" ON "exec_roles"."id" = "exechosts_exec_roles"."exec_role_id" WHERE "exechosts_exec_roles"."exechost_id" = $1 AND (((exec_roles.name = 'superowner') AND (exec_roles.resource_type IS NULL) AND (exec_roles.resource_id IS NULL))) [["exechost_id", "9335e908-f90a-455c-9eb4-f2f8a7ab29ec"]]
下面是用用戶model邀請成功的output:
10:22:01 web.1 | Started POST "/account/users" for ::1 at 2022-04-07 10:22:01 -0400
10:22:01 web.1 | Processing by UsersController#create as HTML
10:22:01 web.1 | Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"first_name"=>"test2", "last_name"=>"test with User", "phone"=>"111-111-1111", "email"=>"testwithUser@test.com"}, "commit"=>"Add Team Member"}
10:22:01 web.1 | User Exists? (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."customer_num" = $1 LIMIT $2 [["customer_num", 86384], ["LIMIT", 1]]
10:22:01 web.1 | ↳ app/models/user.rb:39:in `block in assign_unique_customer_num'
10:22:01 web.1 | User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."created_at" ASC, "users"."id" ASC LIMIT $2 [["id", "e22018b6-61a9-401e-9d15-f02924debcfd"], ["LIMIT", 1]]
10:22:01 web.1 | ↳ app/controllers/application_controller.rb:20:in `current_account'
10:22:01 web.1 | Account Load (0.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT $2 [["id", "eaa76bc4-078f-432f-be4f-e1730f0c7274"], ["LIMIT", 1]]
10:22:01 web.1 | ↳ app/controllers/application_controller.rb:21:in `current_account'
10:22:02 web.1 | User Exists? (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "testwithuser@test.com"], ["LIMIT", 1]]
10:22:02 web.1 | ↳ app/controllers/users_controller.rb:90:in `block in create'
10:22:02 web.1 | User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."invitation_token" = $1 ORDER BY "users"."created_at" ASC, "users"."id" ASC LIMIT $2 [["invitation_token", "[FILTERED]"], ["LIMIT", 1]]
10:22:02 web.1 | ↳ app/controllers/users_controller.rb:90:in `block in create'
10:22:02 web.1 | TRANSACTION (0.3ms) BEGIN
10:22:02 web.1 | ↳ app/controllers/users_controller.rb:90:in `block in create'
10:22:02 web.1 | User Create (42.3ms) INSERT INTO "users" ("first_name", "last_name", "phone", "status", "customer_num", "expiration_date", "created_at", "updated_at", "email", "encrypted_password", "reset_password_token", "reset_password_sent_at", "remember_created_at", "sign_in_count", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip", "account_id", "invitation_token", "invitation_created_at", "invitation_sent_at", "invitation_accepted_at", "invitation_limit", "invited_by_type", "invited_by_id", "invitations_count") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27) RETURNING "id" [["first_name", "test2"], ["last_name", "test with User"], ["phone", "111-111-1111"], ["status", "acitve"], ["customer_num", 86384], ["expiration_date", nil], ["created_at", "2022-04-07 14:22:02.102863"], ["updated_at", "2022-04-07 14:22:02.102863"], ["email", "testwithuser@test.com"], ["encrypted_password", "[FILTERED]"], ["reset_password_token", "[FILTERED]"], ["reset_password_sent_at", "[FILTERED]"], ["remember_created_at", nil], ["sign_in_count", 0], ["current_sign_in_at", nil], ["last_sign_in_at", nil], ["current_sign_in_ip", nil], ["last_sign_in_ip", nil], ["account_id", "eaa76bc4-078f-432f-be4f-e1730f0c7274"], ["invitation_token", "[FILTERED]"], ["invitation_created_at", "2022-04-07 14:22:02.089182"], ["invitation_sent_at", "2022-04-07 14:22:02.089182"], ["invitation_accepted_at", nil], ["invitation_limit", nil], ["invited_by_type", "User"], ["invited_by_id", 0], ["invitations_count", 0]]
10:22:02 web.1 | ↳ app/controllers/users_controller.rb:90:in `block in create'
10:22:02 web.1 | TRANSACTION (3.5ms) COMMIT
10:22:02 web.1 | ↳ app/controllers/users_controller.rb:90:in `block in create'
10:22:02 web.1 | Rendering devise/mailer/invitation_instructions.html.erb
10:22:02 web.1 | Rendered devise/mailer/invitation_instructions.html.erb (Duration: 2.2ms | Allocations: 1376)
10:22:02 web.1 | Rendering devise/mailer/invitation_instructions.text.erb
10:22:02 web.1 | Rendered devise/mailer/invitation_instructions.text.erb (Duration: 1.7ms | Allocations: 698)
10:22:02 web.1 | Devise::Mailer#invitation_instructions: processed outbound mail in 12.1ms
10:22:03 web.1 | Delivered mail 624ef38a2a05e_9d004ed411349@Baileys-MacBook-Pro.local.mail (852.5ms)
10:22:03 web.1 | Date: Thu, 07 Apr 2022 10:22:02 -0400
10:22:03 web.1 | From: please-change-me-at-config-initializers-devise@example.com
10:22:03 web.1 | Reply-To: please-change-me-at-config-initializers-devise@example.com
10:22:03 web.1 | To: testwithuser@test.com
10:22:03 web.1 | Message-ID: <624ef38a2a05e_9d004ed411349@Baileys-MacBook-Pro.local.mail>
10:22:03 web.1 | Subject: Invitation instructions
10:22:03 web.1 | Mime-Version: 1.0
10:22:03 web.1 | Content-Type: multipart/alternative;
10:22:03 web.1 | boundary="--==_mimepart_624ef38a28bcc_9d004ed411214";
10:22:03 web.1 | charset=UTF-8
10:22:03 web.1 | Content-Transfer-Encoding: 7bit
10:22:03 web.1 |
10:22:03 web.1 |
10:22:03 web.1 | ----==_mimepart_624ef38a28bcc_9d004ed411214
10:22:03 web.1 | Content-Type: text/plain;
10:22:03 web.1 | charset=UTF-8
10:22:03 web.1 | Content-Transfer-Encoding: 7bit
10:22:03 web.1 |
10:22:03 web.1 | Hello testwithuser@test.com
10:22:03 web.1 |
10:22:03 web.1 | Someone has invited you to http://localhost:3000/, you can accept it through the link below.
10:22:03 web.1 |
10:22:03 web.1 | http://localhost:3000/users/invitation/accept?invitation_token=4TL5Vpyjfv5CFwCLGx9y
10:22:03 web.1 |
10:22:03 web.1 | This invitation will be due in April 21, 2022 02:22 PM.
10:22:03 web.1 |
10:22:03 web.1 | If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password.
10:22:03 web.1 |
10:22:03 web.1 | ----==_mimepart_624ef38a28bcc_9d004ed411214
10:22:03 web.1 | Content-Type: text/html;
10:22:03 web.1 | charset=UTF-8
10:22:03 web.1 | Content-Transfer-Encoding: 7bit
10:22:03 web.1 |
10:22:03 web.1 | <p>Hello testwithuser@test.com</p>
10:22:03 web.1 |
10:22:03 web.1 | <p>Someone has invited you to http://localhost:3000/, you can accept it through the link below.</p>
10:22:03 web.1 |
10:22:03 web.1 | <p>testing here what to say</p>
10:22:03 web.1 | <p><span class="translation_missing" title="translation missing: en.testing here what to say">Testing Here What To Say</span> </p>
10:22:03 web.1 |
10:22:03 web.1 |
10:22:03 web.1 |
10:22:03 web.1 |
10:22:03 web.1 | <p><a href="http://localhost:3000/users/invitation/accept?invitation_token=4TL5Vpyjfv5CFwCLGx9y">Accept invitation</a></p>
10:22:03 web.1 |
10:22:03 web.1 | <p>This invitation will be due in April 21, 2022 02:22 PM.</p>
10:22:03 web.1 |
10:22:03 web.1 | <p>If you don't want to accept the invitation, please ignore this email. Your account won't be created until you access the link above and set your password.</p>
10:22:03 web.1 |
10:22:03 web.1 | ----==_mimepart_624ef38a28bcc_9d004ed411214--
10:22:03 web.1 |
以下是路線:
get 'dashboard/show'
get 'execdashboard/show'
devise_for :users, controllers: { registrations: "registrations" }
devise_for :exechosts, controllers: { registrations: "exechosts/registrations", invitations: "exechosts/invitations" }
執行主機 model:
class Exechost < ApplicationRecord
rolify :role_cname => 'ExecRole'
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
在執行主機 controller 中:
class ExechostsController < ApplicationController
before_action :set_exechost, only: [:show, :edit, :update, :edit_roles ,:update_roles]
before_action :set_exechosts, only: [:index]
def new
@exechost = Exechost.new
set_exechost_choices
end
def create
@exechost = Exechost.unscoped.new(exechost_params.except("role"))
@exechost.password = "password123"
respond_to do |format|
begin
if @exechost.valid? && @exechost.invite!(current_exechost)
@exechost.add_role :moderator
format.html {
redirect_to exechosts_path,
notice: 'Moderator was successfully invited.'
}
else
set_exechost_choices
format.html { render :new }
end
rescue ActiveRecord::RecordNotUnique
flash[:alert]= 'Email must be unique'
format.html { render :new}
end
end
end
private
def pundit_user
user = current_exechost
end
def set_exechosts
@exechosts = Exechost.all
end
def set_exechost
@exechost = Exechost.find(params[:id])
end
def exechost_params
params.require(:exechost).permit(:username, :email, :role)
end
end
在邀請函 controller 中:
class Exechosts::InvitationsController < Devise::InvitationsController
before_action :update_sanitized_params, only: :update
def create
@exechost = Exechost.invite!(exechost_params[:exechost], current_exechost) do |u|
u.skip_invitation = true
end
ExechostInvitationNotificationMailer.invite_message(@exechost).deliver if @exechost.errors.empty?
@exechost.invitation_sent_at = Time.now.utc # mark invitation as delivered
if @exechost.errors.empty?
flash[:notice] = "successfully sent invite to #{@exechost.email}"
respond_with @exechost, :location => exechosts_path
else
render :new
end
end
private
def exechost_params
params.require(:exechost).permit(:username, :email, :role)
end
end
報名controller:
class Exechosts::RegistrationsController < Devise::RegistrationsController
protect_from_forgery with: :exception, prepend: true
prepend_before_action :require_no_authentication, only: [:cancel]
before_action :configure_sign_up_params
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [[:username, :email]])
end
# The path used after sign up.
def after_sign_up_path_for(resource)
exechosts_path
end
end
devise controller:
class Exechosts::DeviseController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
controller.render(options.merge(formats: :html))
rescue ActionView::MissingTemplate => error
if get?
raise error
elsif has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
redirect_to navigation_location
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end
和 config/initializers/devise.rb; 只有不可避免的部分,唯一開啟的是兩周限制。
# ==> Configuration for :invitable
# The period the generated invitation token is valid.
# After this period, the invited resource won't be able to accept the invitation.
# When invite_for is 0 (the default), the invitation won't expire.
config.invite_for = 2.weeks
# Number of invitations users can send.
# - If invitation_limit is nil, there is no limit for invitations, users can
# send unlimited invitations, invitation_limit column is not used.
# - If invitation_limit is 0, users can't send invitations by default.
# - If invitation_limit n > 0, users can send n invitations.
# You can change invitation_limit column for some users so they can send more
# or less invitations, even with global invitation_limit = 0
# Default: nil
# config.invitation_limit = 5
# The key to be used to check existing users when sending an invitation
# and the regexp used to test it when validate_on_invite is not set.
# config.invite_key = { email: /\A[^@]+@[^@]+\z/ }
# config.invite_key = { email: /\A[^@]+@[^@]+\z/, username: nil }
# Ensure that invited record is valid.
# The invitation won't be sent if this check fails.
# Default: false
# config.validate_on_invite = true
# Resend invitation if user with invited status is invited again
# Default: true
# config.resend_invitation = false
# The class name of the inviting model. If this is nil,
# the #invited_by association is declared to be polymorphic.
# Default: nil
#config.invited_by_class_name = 'User'
# The foreign key to the inviting model (if invited_by_class_name is set)
# Default: :invited_by_id
# config.invited_by_foreign_key = :invited_by_id
# The column name used for counter_cache column. If this is nil,
# the #invited_by association is declared without counter_cache.
# Default: nil
# config.invited_by_counter_cache = :invitations_count
# Auto-login after the user accepts the invite. If this is false,
# the user will need to manually log in after accepting the invite.
# Default: true
# config.allow_insecure_sign_in_after_accept = false
來自用戶 controller 的創建代碼; 代碼與添加引用用戶帳戶相同。
如上所述,創建操作確實創建了一個新用戶和 email 邀請。
def create
@user = User.unscoped.new(user_params.except("role"))
@user.account = current_account
@user.password = "password123"
respond_to do |format|
begin
if @user.valid? && @user.invite!(current_user)
@user.add_role :member, current_account
format.html {
redirect_to account_users_path,
notice: 'User was successfully invited.'
}
else
set_choices
format.html { render :new }
end
rescue ActiveRecord::RecordNotUnique
flash[:alert]= 'Email must be unique'
format.html { render :new}
end
end
end
/exechosts 轉到 Exechosts::RegistrationsController,它不是 ExechostsController,創建操作是從 Devise 開始的正常創建操作,需要密碼參數。 但是,/account/users 轉到 UsersController 請求,其中包含您的自定義代碼。
路由代碼不包含與 UsersController 或 ExechostsController 相關的任何內容。 此外,如果您有一些為用戶和 exechost 調用邀請的自定義代碼,您可能不需要到邀請 controller 的路由,並且您可能希望使用 skip 選項不為邀請生成路由。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.