提问人:bweathers 提问时间:4/7/2022 最后编辑:bweathers 更新时间:4/12/2022 访问量:480
Rails - Devise Invitable - 两个 Devise 用户模型 - 在发送邀请之前,Password can't be blank error with devise
Rails - Devise Invitable - Two Devise User Models - Password can't be blank error with devise prior to invitation being sent
问:
我正在尝试使用两个用户模型设置devise_invitable;用户和 Exechost。用户模型一切正常,首先实现了用户代码,然后添加了第二个模型 Exechost。
使用第二种模型,不会创建或发送电子邮件邀请,并且我们会被重定向到设计注册表单,并出现以下错误:
1 个错误禁止保存此 exechost:
- 密码不能为空
我也在使用 rolify 和 pundit。
以下是终端输出:
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"=>"[email protected]"}, "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", "[email protected]"], ["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"]]
以下是使用用户模型邀请时的成功输出:
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"=>"[email protected]"}, "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", "[email protected]"], ["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", "[email protected]"], ["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 [email protected] (852.5ms)
10:22:03 web.1 | Date: Thu, 07 Apr 2022 10:22:02 -0400
10:22:03 web.1 | From: [email protected]
10:22:03 web.1 | Reply-To: [email protected]
10:22:03 web.1 | To: [email protected]
10:22:03 web.1 | Message-ID: <[email protected]>
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 [email protected]
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 [email protected]</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" }
Exechost 模型:
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
在 Exechost 控制器中:
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
在邀请控制器中:
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
注册控制器:
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
设计控制器:
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
用户控制器中用于创建的代码;代码与添加引用用户帐户相同。
如上所述,创建操作会创建新用户和电子邮件邀请。
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
答:
1赞
Sergio Cambra
4/12/2022
#1
/exechosts 转到 Exechosts::RegistrationsController,它不是 ExechostsController,并且创建操作是 Devise 的正常创建操作,需要密码参数。但是,/account/users 转到 UsersController 请求,其中包含您的自定义代码。
路由代码不包含与 UsersController 或 ExechostsController 相关的任何内容。此外,如果您有一些自定义代码为用户和执行主机调用邀请,您可能不需要到邀请控制器的路由,并且您可能希望使用跳过选项来不为邀请生成路由。
评论