0

I am trying to make a backup table of users, called archived users. It creates the ArchivedUser by taking a hash of the current users attributes (self) and merging in the self.id as the user_id.

When a user is reinstated, their record as an ArchivedUser still remains in the ArchivedUser table. If the user gets deleted a second time, it should update any attributes that have changed.

Currently, it throws a validation error: Validation failed: User has already been taken, as the self.id already exists in the ArchivedUser table.

What is a better way to handle an object where you update an existing object if possible, or create a new record if it doesn't exist. I am using Rails 4 and have tried find_or_create_by but it throws an error

Mysql2::Error: Unknown column 'device_details.device_app_version'

which is odd, as that column exists in both tables and doesn't get modified.

User Delete Method

  # creates ArchivedUser with the exact attributes of the User
  # object and merges self.id to fill user_id on ArchivedUser
  if ArchivedUser.create!(
    self.attributes.merge(user_id: self.id)
  )

Thanks for taking a peek!

VegaStudios
  • 378
  • 1
  • 4
  • 22

1 Answers1

0

If your archived_users table is truly acting as a backup for users and not adding any additional functionality, I would ditch the ArchiveUser model and simply add an archived boolean on the User model to tell whether or not the user is archived.

That way you don't have to deal with moving an object to another table and hooking into a delete callback.

However, if your ArchiveUser model does offer some different functionality compared to User, another option would be to use single table inheritence to differentiate the type of user. In this case, you could have User govern all users, and then distinguish between a user being, for example, an ActiveUser or an ArchivedUser.

This takes more setup and can be a bit confusing if you haven't worked with STI, but it can be useful when two similar models need to differ only slightly.


That being said, if you want to keep your current setup, I believe there are a few issues I see with your code:

  1. If you are going to create an object from an existing object, it's good practice to duplicate the object (dup). That way the id won't be automatically set and can be auto-incremented.

  2. If you truly have deleted the User record from the database, there's no reason to store a reference to its id because it's gone. But if you aren't actually deleting the record, you should definitely just use a boolean attribute to determine whether or not the user is active or archived.

  3. I don't have enough context here as to why find_or_create_by isn't working, but if it were the case, then I would keep it as simple as possible. Don't use all the attributes, but just the consistent ones (like id) that you know will return the proper result.

  4. if ArchivedUser.create! # ... is problematic. The bang after create (i.e. create!) will throw an error if the record could not be created, making the if pointless. So, either use if if you don't want errors thrown and want to handle the condition in which the record was not created. Or use create! without if if you do want to throw an error.

Community
  • 1
  • 1
seancdavis
  • 2,739
  • 2
  • 26
  • 36