[英]Attributes not saving with devise and accepts_nested_attributes_for
I have an almost working sign up form with Devise.我有一个几乎可以工作的 Devise 注册表单。 Whilst it seems to work, it's not saving the fields in my Custaddress
table other than the user_id
.虽然它似乎有效,但它并没有保存我的Custaddress
表中的字段,而不是user_id
。 Any help to work out how to have it save the rest of the information would be great.任何关于如何让它保存其余信息的帮助都会很棒。 The following is greatly truncated!!以下内容大大删减!!
User.rb contains: User.rb 包含:
class User < ApplicationRecord
has_one :custaddress
accepts_nested_attributes_for :custaddress
end
Custaddress contains:客户地址包含:
class Custaddress < ApplicationRecord
has_many :orders
belongs_to :user
end
Registrations controller contains:注册控制器包含:
As you can see, this just builds on the "standard" Devise controller.如您所见,这只是建立在“标准”Devise 控制器上。 There is no create or new here as I assume it's using the standard methods.这里没有 create 或 new ,因为我假设它使用的是标准方法。
class Users::RegistrationsController < Devise::RegistrationsController
invisible_captcha only: :create
protected
def build_resource(hash = {})
self.resource = resource_class.new_with_session(hash, session)
resource.build_custaddress
# Jumpstart: Skip email confirmation on registration.
# Require confirmation when user changes their email only
resource.skip_confirmation!
# Registering to accept an invitation should display the invitation on sign up
if params[:invite] && (invite = AccountInvitation.find_by(token: params[:invite]))
@account_invitation = invite
# Build and display account fields in registration form if enabled
elsif Jumpstart.config.register_with_account?
account = resource.owned_accounts.first
account ||= resource.owned_accounts.new
account.account_users.new(user: resource, admin: true)
end
end
def update_resource(resource, params)
# Jumpstart: Allow user to edit their profile without password
resource.update_without_password(params)
end
def sign_up(resource_name, resource)
if cookies[:ordernum]
order = Order.where(ordernum: cookies[:ordernum]).first
if order
order.update!(user: resource, custaddress: resource.custaddress)
cookies.delete "ordernum"
end
end
sign_in(resource_name, resource)
# If user registered through an invitation, automatically accept it after signing in
if params[:invite] && (account_invitation = AccountInvitation.find_by(token: params[:invite]))
account_invitation.accept!(current_user)
# Clear redirect to account invitation since it's already been accepted
stored_location_for(:user)
end
end
end
My new.html.erb contains:我的 new.html.erb 包含:
<%= form_with(model: resource, as: resource_name, url: registration_path(resource_name, invite: params[:invite])) do |f| %>
<%= f.fields_for :custaddress do |cust| %>
<div class="form-group">
<%= cust.label "Apartment/Unit Number", class: "font-bold" %>
<%= cust.text_field :apartment, class: "form-control", placeholder: "Unit 2 or Apartment 307" %>
</div>
And much more!
My application controller has:我的应用程序控制器有:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SetCurrentRequestDetails
include SetLocale
include Jumpstart::Controller
include Accounts::SubscriptionStatus
include Users::NavbarNotifications
include Users::TimeZone
include Pagy::Backend
include CurrentHelper
include Sortable
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :masquerade_user!
before_action :store_user_location!, if: :storable_location?
protected
# To add extra fields to Devise registration, add the attribute names to `extra_keys`
def configure_permitted_parameters
extra_keys = [:avatar, :first_name, :last_name, :time_zone, :preferred_language]
signup_keys = extra_keys + [:terms_of_service, :invite, owned_accounts_attributes: [:name], custaddress_attributes: [:address, :apartment, :city, :state, :country, :postcode, :mobile]]
devise_parameter_sanitizer.permit(:sign_up, keys: signup_keys)
devise_parameter_sanitizer.permit(:account_update, keys: extra_keys)
devise_parameter_sanitizer.permit(:accept_invitation, keys: extra_keys)
end
def after_sign_in_path_for(resource_or_scope)
stored_location_for(resource_or_scope) || super
end
# Helper method for verifying authentication in a before_action, but redirecting to sign up instead of login
def authenticate_user_with_sign_up!
unless user_signed_in?
store_location_for(:user, request.fullpath)
redirect_to new_user_registration_path, alert: t("create_an_account_first")
end
end
def require_current_account_admin
unless current_account_admin?
redirect_to root_path, alert: t("must_be_an_admin")
end
end
private
def storable_location?
request.get? && is_navigational_format? && !devise_controller? && !request.xhr?
end
def store_user_location!
# :user is the scope we are authenticating
store_location_for(:user, request.fullpath)
end
end
My logs are showing:我的日志显示:
Processing by Users::RegistrationsController#create as JS
17:08:57 web.1 | Parameters: {"authenticity_token"=>"uVphxW4gCQntvHFxRb33dl9cqxv9vlL69Wc2zOMoF1M+pUk8c2HnHwgQFIkMbfmxYraVI7rYBVCPgfSD1u7OHg==", "user"=>{"first_name"=>"[FILTERED]", "last_name"=>"[FILTERED]", "email"=>"[FILTERED]", "password"=>"[FILTERED]", "time_zone"=>"Sydney", "custaddress_attributes"=>{"apartment"=>"", "address"=>"XXXXXXXX", "city"=>"XXXXXXX", "state"=>"XXXXX", "postcode"=>"XXXX", "mobile"=>"XXXXXXXX", "country"=>"XXXXXXX"}, "terms_of_service"=>"1"}, "enc-rmjxhdab"=>"", "button"=>""}
So, I know it's getting the information from the form.所以,我知道它是从表单中获取信息。 However I have no idea where it's saving the Custaddress
record.但是我不知道它在哪里保存Custaddress
记录。 It only seems to be associating the user_id
:它似乎只是关联user_id
:
Custaddress Create (0.2ms) INSERT INTO "custaddresses" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["user_id", 3], ["created_at", "2020-09-25 02:20:39.109187"], ["updated_at", "2020-09-25 02:20:39.109187"]]
12:20:39 web.1 | (33.9ms) COMMIT
Can anyone please help me work out why the custaddress isn't saving.任何人都可以帮我弄清楚为什么 custaddress 没有保存。 I've spent hours on this and read every article on google (well it certainly feels like it).我已经花了几个小时在这上面阅读了谷歌上的每一篇文章(当然感觉就是这样)。
I've tried to track this down, to no avail.我试图追踪这一点,但无济于事。
Adding an answer for completeness.添加一个完整的答案。
Devise's create
action calls the build_resurce
method that you are overriding. Devise 的create
操作调用您要覆盖的build_resurce
方法。 the problem is that you overrid it for the new
action but didn't take into account the create
action.问题是您为new
操作覆盖了它,但没有考虑create
操作。
So, you are doing this:所以,你正在这样做:
def build_resource(hash = {})
self.resource = resource_class.new_with_session(hash, session)
resource.build_custaddress
For the new
action there's no issue, resource.build_custaddress
will instantiate a new custaddress
object for you to be able to use the fields_for
helper.对于new
操作没有问题, resource.build_custaddress
将实例化一个新的custaddress
对象,以便您能够使用fields_for
助手。
The problem is that, for the create
action, at that point resource
already has a custaddress
with the values from the request (set by the previous line), then you do resource.build_custaddress
and replace the current custaddress
with a new empty one.问题是,对于create
操作,此时resource
已经有一个带有请求值的custaddress
(由前一行设置),然后您执行resource.build_custaddress
并将当前custaddress
替换为新的空地址。
The solution is to only build a custaddress if it's not nil:解决方案是只构建一个 custaddress 如果它不为零:
def build_resource(hash = {})
self.resource = resource_class.new_with_session(hash, session)
resource.build_custaddress if resource.custaddress.nil?
That way you'll have a new empty custaddress for the new
action, but respect the values from the request for the create
, edit
or update
actions.这样,您将有一个新的空 custaddress 用于new
操作,但尊重来自create
、 edit
或update
操作请求的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.