My rails migration is failing because of the following line:
username = User.find(user_id)
if (!username.nil?)
...
I have a user_id of 100 which has no matching user in Users so username is returning as nil (I think) but then my entire migration crashes
I thought that the if (!username.nil?) would overcome this... is there another way to not crash upon no results in find()?
If find cannot find the record with the id you specify then it raises an ActiveRecord::NotFoundError exception.
If you want to return nil when there is no such record you can do:
username = User.where(id: user_id).first
(and note that it is a User instance and not the name of the user)
Related
Rails executing update on deleted records.
I have a web app on ruby on rails in which I created some users and after that I opened the rails console and assigned U1 to one of my user let say last user then assigned the same User to U2. Then I run U1.destroy which executes successfully
after that I updated the name of user through U2 and it returns me true Although, user was destroyed from database when I checked it. My concern is rails should give me false as there was no object in database against that ID.
If you want to double check that record exists before updating you can use reload
user.reload.update(name: "Some Name")
It will raise ActiveRecord::RecordNotFound if record with such id is absent
UPDATE changes the values of the specified columns in all rows that satisfy the condition.
https://www.postgresql.org/docs/current/sql-update.html
Rails doesn't return false or raise an exception because the UPDATE is still a valid query in the database, even if no rows match the condition. If you connect directly to your PostgreSQL database and run...
UPDATE users
SET name = 'test'
WHERE id = 123
...if 123 is an id that no longer exists, then the database will successfully execute the query and respond with:
UPDATE 0
If it is an id that still exists, the database will respond with:
UPDATE 1
This is similar to how Rails behaves if you use update_all. If you were to run update_all on a record that no longer exists, you'd see something like:
User.where(id: 123).update_all(name: 'test')
=> 0
But if the record exists you'd see:
User.where(id: 123).update_all(name: 'test')
=> 1
No error will be raised.
The purpose of the Rails update and update_all methods is just to attempt to run an UPDATE query in the database. If there is a timing issue and the record no longer exists, that's not something that the database or Rails is designed to give warnings about.
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.
I notice this question pops up a lot, but after trying several recommended solutions I found I still can't figure out what is wrong. I have a model called sample and a user model as well. When a sample is approved the hours on the sample are supposed to be added to the users total hours, but the users value is never updated. Each user has a unique email which is stored in the sample when it is submitted for approval. I checked in the database to make sure it wasn't an issue with accessing the value, and no error is being thrown so I am not really sure what is happening. I'm pretty new to ruby and rails so any help is appreciated. My samples_controller.rb contains the function:
def approve
#sample = Sample.find(params[:id])
#sample.update(sample_status:1)
#user = User.find(Sample.email)
hours_update = #user.hours + #sample.volunteer_hours
#user.update_attributes(:hours, hours_update)
redirect_to samples_adminsamples_path
end
Edit: thanks for the help everyone, turns out I needed to use the command
#user = User.find_by(email: #sample.email)
in order to get the proper user.
Can you please give some more data like db structure of Sample and User tables.
From the limited information, I think the line number 4 (#user = User.find(Sample.email)) is the problem.
.find() tries to query the DB on id and Sample.email would be giving user's email and not the id of the corresponding user in db.
I am also guessing that in your controller, you are suppressing the thrown exception some where using begin-rescue block because .find() throws ActiveRecord::RecordNotFound exception if it fails to find the resource.
Alternatively, if it is fetching the user correctly, you can also try update_column to update the values.
You are using incorrect format for update_attributes
It should be
#user.update_attributes(hours: hours_update)
or
#user.update_attribute(:hours, hours_update)
NOTE: update_attribute doesn't triggers the callbacks
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")
Here is my controller code to check login details of a user
def validateLogin
#email = params[:userEmail1]
#pass = params[:userPassword1]
if params[:userEmail1] != nil
valid_user = Userprofile.find_by_sql(["select * from userprofiles where userEmail=? and userPassword=?", #email, #pass])
if valid_user.count > 0
session[:email] = #email
session[:uid] = valid_user.id
session[:userType] = valid_user.userType # usertype is a column in userprofiles table
# But here i am not receiving the usertype it gives error that undefined variable usertype.
redirect_to "/userhomes/"
else
flash[:message] = "Either email or password is incorrect"
redirect_to '/'
end
else
flash[:message]="Fields can not be blank"
render :action=>'defaults'
end
Please help
session[:userType] = valid_user.userType
# Error: (usertype is a column in userprofiles table)
But here i am not receiving the usertype it gives error that undefined variable usertype.
You are seeing this error because you receive an array of objects from find_by_sql. You even check the size of the array in your if clause.
From your code I think you expect only one returned object. But you still need to get it from the array like so:
profiles = Userprofile.find_by_sql(["select * from userprofiles where userEmail=? and userPassword=?", #email, #pass])
if profiles.count > 0
user_profile = profiles[0]
#... your other stuff
end
Another variant which also much better uses Rails idioms and especially ActiveRecord as is was inteded to be used is to let it construct the SQL by itself which is generally safer, less prone to errors and cacheble.
You didn't write which version of Rails you are using, but for Rails 2.3.x, it looks like this
user_profile = Userprofile.first(:conditions => {:userEmail => #email, :userPassword => #pass})
For Rails 3.x, it looks like this:
user_profile = Userprofile.where(:userEmail => #email, :userPassword => #pass).first
Both variants expect that you have a model called Userprofile, which you generally require to effectively work with database objects in Rails. What both queries do is to create a new model instance from the first row returned from your query (that's what the first does).
Generally, you should get a book or some guide on the internet and learn how to properly use ActivRecord. Note that the API has seriously changed between Rails 2.3 and Rails 3 so make sure to use a guide for your actual Rails version.
And as a final advice, you shouldn't store actual ActiveRecord objects in the session. They would need to be serialized on store and de-serialized on access. What makes it hard (or impossible to track object references.
Also, Rails uses the cookie session store by default, which means that the whole session data is stored in a cookie on the client. The data therein in fully readyabkle to anyone with access to the cookie as it is only signed to restrict tampering with the data, but it is not encrypted. Thus, in your case anyone would be able to ready the (unecrypted) password.
Instead of storing the model object, you should store it's id instead and get the actual (and up-to-date) object from the database instead on each request. This is much easier, saves you from cache inconsistencies (what happens if the user changes her password) and is probably faster than to transfer a huge session cookie from the client on each request.