rails can you reference a model within a model? - ruby-on-rails

I seem to have run into a problem with trying to use a model in another model in Rails.
I am pulling a list of users from Active Directory with LDAP in a dropdown. I want to parse the cn that I get from Ldap into a firstname and lastname.
The problem I am running into is that I need to find a record in the users model. The parsing is being done in observations.rb.
Observation.rb:
def parse_employee
#emp_name = '' #initialize
self.employee_raw = self.employee_raw[2...-2] # get rid of the quotes and brackets
#emp_name = self.employee_raw.split(' ') # split first/last names
#emp_first_name = #emp_name[0] #Grab the first name
#emp_last_name = #emp_name[1] # grab the surname
#user = User.where("last_name like ?", #emp_last_name)
self.employee_id = #user.id
end
I've played with this quite a bit and it appears that I can't reference other models from within a model.
To sum up, what I am trying to do is
1. Have the user select the appropriate person from a dropdown that is pulled via LDAP from active directory.
2. Use the first and last names to find the appropriate user in my user table (Right now I'm just trying to get it to work with the last name as that is unique enough)
3. When I find the correct user in the user table, enter that id in the employee_id field in my observations table.

Related

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

Rails 5.1 has_many through - associated fields

(See example schema image below)
I am attempting to query a single user from the users table using the email field, along with the id & key fields from the applications table. The results should contain the user found (if any), along with the application (referenced using the key & id fields) and the applications_users associated data.
I can easily write SQL manually to perform this operation:
SELECT
"users".*,
"applications_users"."scopes",
"jwt_applications".*
FROM
"users"
INNER JOIN
"applications_users" ON "applications_users"."user_id" = "users"."id"
INNER JOIN
"jwt_applications" ON "jwt_applications"."id" = "applications_users"."application_id"
WHERE
"users"."email" = 'rainbows#unicorns.net'
AND "jwt_applications"."id" = '01daafc9-2169-4c78-83e9-37ac0a473e3d'
AND "jwt_applications"."key" = 'follow_the_rainbow'
LIMIT 1
However, I cannot for the life of me get the query correct when using ActiveRecord.
These are the unsuccessful attempts I have made thus far:
user = User.where(email: args[:username]).joins(:applications).merge(
JwtApplication.where(id: args[:application][:id], key: args[:application][:key])
).take!
This gets the user correctly, however Rails performs a second SQL query when I attempt to access user.applications (and it also returns all applications associated with the user; so it appears to disregard the id & key conditions)
user = User.where(email: args[:username]).joins(:applications).merge(
JwtApplication.where(id: args[:application][:id], key: args[:application][:key])
).references(:applications_users).take!
This gets the user correctly and also the correct application (yay!), however Rails performs a second SQL query if I attempt to call user.applications_users -- it also returns a collection for all data inside the applications_users table (again, disregarding the id & key conditions)
user = User.where(email: args[:username]).joins(:applications).where(
jwt_applications: {
id: args[:application][:id],
key: args[:application][:key]
}
).take!
This gets the correct user, however Rails performs another SQL query when I attempt to access user.applications -- also returning all applications.
Anyway, hopefully a Rails genius can shed some light on this question! I will be the first to admit that I am by no means a Rails expert; I have spent the last 10 years of my professional career coding in PHP & C++, so please bear with me if this comes off as a stupid question :)
Not sure if this is something you're looking for but...
You can write ActiveRecord query like (join model should be implicitly added to your query):
User.joins(:applications).where(email: email).where(applications: { key: key, id: id})
Where email, key and id as params to pass to the query.
On top of that query you can use select fields to get everything you need:
user = User.joins(:applications).where(email: email).where(applications: { key: key, id: id}).select('users.*, applications.id as appid applications.key as appkey').first
That will give you back the user model (if present) or empty relation if nothing matches your criteria.
You can then call the fields like
user.appid
user.appkey
You can always call select ('users.*, application_users.scopes, applications.*) which will return you all the fields in single instance (still under User model) BUT duplicate fields like id will only be shown once, that's why it's better to grab just the fields you want and give them unique identifiers like I've shown with appid and appkey.
Again, might not be exactly what you're after, but hopefully it points you in the right direction!

Add value to column in database via the rails console

I have a table in my database called "users". In this table, I have 4 columns: Firstname, Lastname, Age, Location (all created with the usual: rails generate migration add_firstname_to_users firstname:string).
If I have an existing user with the columns FirstName, Lastname, and Age already populated but Location is empty, how can I add a value to Location for a specific user via the rails console? Been googling all day, cant seem to find an answer.
Thanks!
Open rails console: rails console (in short rails c)
Find specific user: user = User.find 1 # Note here '1' is specific user ID
And then,
user.location = "location value"
user.save! # Force to raise errors if any validation fails
OR
user.update_attribute(:location, "location value")

Selecting second entry in rails console?

I need to select the second entry in the User model.
User.second does not work, nor does User.2 or User.two.
I'm trying to set u to the second User entry (u = User.2)
The following should work:
User.all.second
User.offset(1).first
# Assuming you are using incremental keys and have users with ID's 1 and 2:
User.find(2)

How checking value from DB table?

I want create a simple checking value from database. Here is my code:
def check_user_name(name, email)
db_name = Customers.find_by_name(name).to_s
db_email = Customers.find_by_email(email).to_s
if name == db_name && email == db_email
return 'yes'
else
return 'no'
end
end
But I have allways 'no' variant....why ?
Because you are calling to_s on your Customers model and not actually getting the name. The two fetch lines you have should be:
Customers.find_by_name(name).name.to_s # to_s probably not necessary if you know this field is a string
Customers.find_by_email(email).email
But, you're making two separate requests to the database. I don't know what the purpose of this is (as you could be selecting two different Customers) but you could do:
if Customers.where(name: name, email: email).exists?
"yes"
else
"no"
end
Since you are, however, selecting by name and email - I would highly recommend that you make sure those fields are indexed because large tables with those requests will bog the server and make that route rather slow (I would actually recommend that you pursue other routes that are more viable, but I wanted to mention this).
When you give Customers.find_by_name(name), you will not get name of a customer. Actually it will return activerecord object, so from this object you need to get the name and email of a customer, like below,
name = Customers.find_by_name(name).name
email = Customers.find_by_email(email).email
Now you will get the exact name and email of matched data from DB.

Resources