Rails get title of associated resource by id - ruby-on-rails

I have 2 models(Clients and Projects) that are connected like this:
class Project < ActiveRecord::Base
belongs_to :cliente
end
class Cliente < ActiveRecord::Base
has_many :projects
end
Projects have a :cliente_id column in its schema, so if I do:
Project.cliente_id I will get the cliente_id correctly.
My doubt is, I want to get the client name from it's id, so I need something like:
Project.cliente_id.name
Which is the correct way to retrieve this info?

You find associated objects through the association:
project = Project.find(1) # Returns the full `project` object
project.cliente # Returns the full `cliente` object
project.cliente.name # Returns just the `name` attribute
project.cliente_id == project.cliente.id # Returns true

You can get the complete Cliente object with project.cliente (note that the _id is not used). So you can use it like a regular Cliente; for example, to get name just do:
project = Project.find(1)
project.cliente.name

Related

Failing to update foreign keys

I have the following association:
class Tenant < ApplicationRecord
belongs_to :landowner
end
and
class Landowner < ApplicationRecord
has_many :tenants
end
In my webapp, when I initialise a Tenant object, I add a default Landowner, which I have already stored with id of 0:
# my initilisation
new_tenant = Tenant.create(landowner_id: 0)
The problem I'm running into is that I want to update this Tenant object's landowner to a new landowner, say with id of 1. I'm trying to do this through the console with:
# I only have one tenant, and I made sure the last landowner is a new different landowner
Tenant.first.landowner = Landowner.last
Tenant.first.save!
I don't get any errors but the change does not persist. I have also tried:
Tenant.first.update({:landowner_id => 1})
but this does not work either (no error, but changes do not persist). Can someone help me with this?
You are reinitialising the object again by calling first() on Tenant for the second time, try this instead
tenant = Tenant.first
tenant.landowner = Landowner.last
tenant.save!
This will either save or throw a validation error.
Hope it helps!

Print attribute of object

I have 2 classes with:
class User < ApplicationRecord
has_one :address
end
class Address < ApplicationRecord
belongs_to :user
I need to write code, which will print the value of attribute with the name 'postcode' of 100 users from database.
I have some code on this point, but not sure that it's a good way to solve the problem:
#users = User.all
#users.limit(100).each do |user|
puts "#{user.postcode}"
end
Who has better ideas?
I'd use pluck
puts User.limit(100).pluck('postcode')
# or
puts User.joins(:address).limit(100).pluck('addresses.postcode')
Pluck is best suited for your scenario.
User.where(condition).pluck(:postcode)
(#where condition is optional)
Event if you want to fetch other column with postcode you can simply include that in pluck. for e.g.
User.where(condition).pluck(:id, :postcode)
(#using multiple column inside pluck will only work with rails4 and above)

rails active record has_many foreign key after custom function

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().
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.
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!
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...

What method will return ActiveRecord owner object from class method?

Let's say I have users which have items:
class User < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :user
end
Now I want to add a class method to item for a customized record creation:
def self.create_personalized
create description: "#{user.name}' item"
end
But of course, since this is a class method, user is undefined. But if I call it using user.items.create_personalized, then there is an associated user via the relationship. I know that Item.create_personalized is aware of the user because this works:
def self.create_personalized
item = create
item.update_attribute :description, "#{item.user.name}'s item"
end
But clearly that's not the best way to access the owner object. What is the correct method?
If you do
Item.scope_attributes
then you'll get a hash of any attributes from the current scope that apply to item. These are the ones that would be extracted from the scope if you called create. These are the DB level attributes so you'll get a user id rather than the user itself.
The best option I've found is current_scope.proxy_association.owner. It's still a bit indirect, but it returns the exact object I'm looking for.
def self.create_personalized
user = current_scope.proxy_association.owner
create description: "#{user.name}'s item"
end

Use variable other than :id in rails 3 routes

I'm trying to get my rails 3 app to use a route that looks something like:
exampleapp.com/patients/123456
rather than
exampleapp.com/patients/1
where "123456" would be associated with the patient's medical record number (:mrn), which is already in the :patients table and is a unique integer. I want to use the :mrn in place of the usual :id. How would I go about this?
Sorry if this has already been asked - I couldn't find the terminology as to what I'm trying to do is called. Thanks!
You could do this,
class Patient < ActiveRecord::Base
self.primary_key = "mrn"
end
However, this will change a bunch of other things. The to_params will use mrn. The controller will still use params["id"], but the value will be the mrn field. The Patient.find method will work on the mrn field, but not the id field. (You can user Patient.find_by_mrn and Patient.find_by_id which will work on their specified fields.) Also, all foreign keys will be to the mrn value.
You can edit the mrn field, and you will still have an id field (unless you turn it off), however, editing could be a pain because all the foreign keys will have to be corrected.
Alternatively, if you just want to change the URL, then in your config/routes.rb file instead of
resources :patient
use
match "/patients/:mrn" => "patients#show"
match "/patients/:mrn" => "patients#update", :via => :put
You could just add this to your Patients model
def class Patient < ActiveRecord::Base
self.primary_key = "mrn"
end
You can get per-resource identifier customization by redefining the member_scope and nested_scope methods on the Resource instance.
resources :patients do
#scope[:scope_level_resource].tap do |u|
def u.member_scope
"#{path}/:mrn"
end
def u.nested_scope
"#{path}/:#{singular}_mrn"
end
end
end

Resources