使用accepts_nested_attributes_for遇ActiveRecord::AssociationTypeMismatch错误求助
Hey there! Let's figure out why you're hitting that ActiveRecord::AssociationTypeMismatch error and fix it up.
What's causing the issue?
The core problem is conflicting association setups:
- Your
Usermodel declares a plainhas_one :email, which tells Rails to look for anemailrecord tied to the user via auser_idcolumn. - But your
Emailmodel uses a polymorphicbelongs_to :contactable, which relies oncontactable_idandcontactable_typecolumns instead.
When you try to create a user with nested email attributes, Rails gets confused—it tries to assign the User to an email association that doesn't align with the polymorphic contactable setup, hence the type mismatch.
Plus, your original setup doesn't distinguish between the primary login email (one per user) and contact emails (multiple per user), which is critical for your use case.
Step-by-Step Fix
1. Add a flag to distinguish primary vs contact emails
First, we need a way to mark which email is the user's login account. Generate a migration to add an is_primary boolean column to the emails table:
rails generate migration AddIsPrimaryToEmails is_primary:boolean
Run the migration:
rails db:migrate
(You can set a default value of false in the migration if you want, just add default: false to the column definition.)
2. Update your model associations
Adjust the User model to explicitly define the two types of email relationships, and tie them to the polymorphic contactable association in Email:
class User < ApplicationRecord has_secure_password # Primary login email (one per user, marked as primary) has_one :primary_email, -> { where(is_primary: true) }, class_name: "Email", as: :contactable, dependent: :destroy # Contact emails (multiple per user, non-primary) has_many :contact_emails, -> { where(is_primary: false) }, class_name: "Email", as: :contactable, dependent: :destroy # Allow nested creation of the primary email accepts_nested_attributes_for :primary_email end
Update the Email model to enforce uniqueness of primary emails per user, and remove the unnecessary accepts_nested_attributes_for :contactable (since we're creating emails from the user side, not the other way around):
class Email < ApplicationRecord belongs_to :contactable, polymorphic: true validates :email, presence: true, uniqueness: true # Ensure only one primary email per user validates :is_primary, uniqueness: { scope: :contactable_id }, if: -> { is_primary } end
3. Fix your controller's create logic
Update your user_controller.rb to handle the nested primary email attributes, and ensure the is_primary flag is set to true for the login email:
class UsersController < ApplicationController def create @user = User.new(user_params) # Ensure the primary email is marked as such if @user.primary_email_attributes.present? @user.primary_email_attributes[:is_primary] = true end if @user.save redirect_to @user, notice: 'User created successfully!' else render :new end end private def user_params params.require(:user).permit( :name, :password, :password_confirmation, primary_email_attributes: [:email] # is_primary is set in the controller ) end end
Why this works
- We've clearly separated the primary login email from contact emails using the
is_primaryflag. - The associations now correctly use the polymorphic
contactablesetup, so Rails knows how to tie users to their emails without conflicting column expectations. - The validation ensures no user can have more than one primary login email, which aligns with your requirement.
内容的提问来源于stack exchange,提问作者Pavel Perevezencev

