Search code examples
ruby-on-railsrubyactiverecorddeviseomniauth

Ruby on Rails / ActiveRecord: Updating records with first_or_initialize causes RecordNotUnique


I'm currently getting user data from a SAML assertion and creating users in a local DB based on that info:

mapped_role = map_role user_role
user = User.where(email: auth_attrs.single('Email')).first_or_initialize do |u|
  u.firstname = auth_attrs.single('First Name')
  u.uid = auth_attrs.single('UID')
  u.provider = resp.provider
  u.role = mapped_role
 end

This works well enough, but when the user's details change (for instance, their role changes), that data doesn't get updated in the DB. What I've tried doing is moving the role assignment out of the do block (on the user object returned by first_or_initialize) and then calling a follow-up user.save, but this results in a pretty red screen informing me that the column 'email' isn't unique. I don't want to be creating a new record here, just updating an existing one. Is there a better pattern to be using here?

Edit: I've tried the various approaches laid out here, but they result in the same SQLite3 error. It seems like I'm missing something there.

Edit2: It looks like this might be due to Devise trying to do something behind the scenes with an email field of its own(?).


Solution

  • I think I would go about it like so

    mapped_role = map_role user_role
    # find the user or initatiate an new un-persisted user
    user = User.find_or_initialize_by(email: auth_attrs.single('Email'))
    attributes = {firstname: auth_attrs.single('First Name'), 
                  uid: auth_attrs.single('UID'), 
                  provider: resp.provider, 
                  role: mapped_role}
    # attempt to update the above record with the appropriate attributes 
    # this will set the attributes and fire #save
    if user.update(attributes)
      # successful
    else
      # handle validation errors 
    end
    

    This way there is no need for logical handling of users that are already persisted and new users.