Isha Aggarwal
Isha Aggarwal

Reputation: 452

Splitting devise users table into two tables

I have a users table integrated with devise gem. The table has certain fields like current_sign_in_ip, last_sign_in_ip,current_sign_in_at, etc. Devise modifies these fields when the user logs in. I would like these fields to be moved to another table and populate them via devise or otherwise. Is there a way I can achieve this?

Upvotes: 3

Views: 799

Answers (2)

Isha Aggarwal
Isha Aggarwal

Reputation: 452

Thanks for the response. It helped me to move in the correct direction. Let me share how I went about solving the problem I was facing.

As I wanted one-to-many relationship between users and user_logs table, I could not delegate the trackable methods to the UserLog model. Also, I wanted the user_logs table to be an insert-only table, hence I needed to override update_tracked_fields method of Devise Trackable module accordingly.

So :

  1. I created a new user_logs table and UserLog model.

  2. Overrid the update_tracked_fields method to actually insert a new row everytime in the user_logs table instead of updating users table.

  3. Created getter methods for these trackable fields in User model that actually fetches required column value from the user_logs table. Like this :

    self.try(:user_logs).last.try(:current_sign_in_ip)
    

Upvotes: 1

Collin Graves
Collin Graves

Reputation: 2267

There are two approaches to this answer:

The first (and easier, although not as pretty) option is to delegate the :trackable methods to a second class (called UserDetail below). For example:

class UserDetail < ActiveRecord::Base
  ...
end

class User < ActiveRecord::Base
  delegate :current_sign_in_ip, :last_sign_in_ip .... to: :user_detail

  ...
end

A cleaner option would be create a concern that avoids overriding Devise. You'd have to extend this on to your User model:

module Trackable
  def update_tracked_fields!(request)
    old_current, new_current = user_detail.current_sign_in_at, Time.now.utc
    user_detail.last_sign_in_at     = old_current || new_current
    user_detail.current_sign_in_at  = new_current

    old_current, new_current = self.current_sign_in_ip, request.ip
    user_detail.last_sign_in_ip     = old_current || new_current
    user_detail.current_sign_in_ip  = new_current

    user_detail.sign_in_count ||= 0
    user_detail.sign_in_count += 1

    user_detail.save(:validate => false)
  end
end

I got the second solution from Rodrigo Flores via this link. He reminds us to put record.update_tracked_fields!(warden.request) on the Warden::Manager.after_set_user block like they do here.

Upvotes: 5

Related Questions