rails active record has_many foreign key after custom function
Asked Answered
E

3

7

Consider following models

class User < AR
   has_many :resources
end

class Resource < AR
   belongs_to :user
end 

I have a requirement where the foreign key is saved after applying some function on it. So the value of user_id in resources table does not match id in users table but it can be calculate again from id.

How can I define the association? Let's say that function is dummy_func().

Equivocate answered 30/8, 2016 at 20:4 Comment(2)
You should probably save the hashed id against each user in a new column hashed_id and use that key for relational reference with ResourceUnfeigned
The objective is that no one should be able to link resource back to user by just looking at dbEquivocate
R
3

Since belongs_to returns class instance instead of association, you can define methods in Resource class

class Resource < ApplicationRecord
  def user
    User.find(user_id)
  end

  def user=(user)
    user_id = user.id
  end
end

Similar for has_many result in user can be achieved by creating common relation in resources method

class User < ApplicationRecord
  def resources
    Resource.where(user_id: id)
  end
end

So, if you use this code, you can replace any ids in Resource model, and behavior will exactly same as in belongs_to (Maybe there is some difference in depths). And you can achieve very similar behavior in User model, by writing methods by yourself.

Rachellrachelle answered 5/10, 2016 at 22:58 Comment(3)
Interesting... How will it affect the performance of application? Thank you for the reply :)Equivocate
@Equivocate In Resource user= method just assign the id to user_id and let the save method take care of updatiion. That will save you an update query. Also do not forget to add index on user_id column of resources table. Then you can relax when it comes to performanceDamien
@sadaf, this examples should not decrease performance. get method works very similar as ActiveRecord relation, so you should not be afraid. Also thanks to Dinesh for suggestion.Rachellrachelle
N
2

Perhaps you can you can use a callback in order to modify the current user_id somehow before saving it: callbacks.

I'd suggest something like :before_save or something of that nature where you define how you want the user_id to be modified in the resources table and then have a way of decrypting it as well.

Maybe you can use an encryption gem to encrypt and decrypt your attribute like attr-encrypted.

Hope this helps a bit!

Namnama answered 30/9, 2016 at 11:47 Comment(2)
Saving is not an issue. The issue is that I want associations to work as with normal(unencrypted) foreign keys.Equivocate
I don't think I understand your question then, maybe if you were a little more clear? You're saving the user_id after applying some function to it, so it's changing the user_id before saving it to the table already? You want to be able to fetch it but have it go back to what it was originally? In that case, why don't you just have some kind of decryption method when you try to fetch the record?Namnama
H
2

In the User model, you can override the setter. If you want to encrypt and decrypt the user ID (using attr_encrypted)...

You could try something like this:

attr_encrypted :id, key: ENCRYPTION_KEYS[:value]

def id=(value)
  send("encrypted_value=", encrypt(:id, value))
  instance_variable_set(:@id, value)
end

Then you can make a method that decrypts the ID

def decrypted_id
  decrypt(:id, encrypted_value)
end

Now, when the User is created, the database will set the ID as usual. But it will also create an encrypted_value which stores the id as an encrypted ID. You can use this encrypted value around your app to keep the database ID secret from the interface.

Here is an example in console...

Example in Console

Hornswoggle answered 30/9, 2016 at 23:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.