Mass updating of database fields from Rails console - ruby-on-rails-3

I have a Rails 3.2.19 app where I have a call model and a region model.
Call belongs_to :region and has region_id in the table
Region has_many :calls and the field I'm interested in is in the Region model called area which is a string type that contains a City (Houston, Dallas).
My question is. I have nearly 18K call records where I need to update the region_id to the region's area. i.e "1" or "2"
What's the best way to update this region_id field via the console or even better yet via PSQL which we use as a database?
I know this is probably a simple thing but I want to make sure I don't cause problems in production.

Use update_all
Call.where(area: 'Houston').update_all(region_id: 1)
To update calls which doesn't have region_id
Call.where("region_id IS NULL").update_all(region_id: 1)

Related

Rails, check database if uploading data in CSV already exists

I'm new to Rails and I would like to know if I can check if data already exists in my database while uploading new data from CSV. So far I haven't found this on the net.
I use a Postgres database. I don't know if I have to check it in Rails or in Postgres. In my database there are some columns like id, personal_id, cost_center. So I will check if one (or more) of my new data has the same personal_id with the the same cost_center while uploading.
How can I do this?
UPDATE
I've tried the solution of #huan son, it works but not the way I need it. So I tried different things and I think a SQL query is in my case the best choice.
DELETE FROM bookings WHERE id NOT IN (SELECT MIN(id) FROM bookings GROUP BY personal_id, wbs, date, hours, cost_center)
Booking.delete_all.where('id NOT IN (?)', Booking.select('MIN(id)').group(:personal_id, :wbs, :date, :hours, :cost_center).map(&:id))
My SQL query works like the way I want it but I don't know the right "translation" into rails because with the second code above my whole bookings table gets deleted
Solution
The solution for my problem is:
Booking.delete_all(['id NOT IN (?)', Booking.group(:personal_id, :wbs, :date, :hours, :cost_center).pluck('MIN(id)')])
Those are unique scopes.
You need to define those first in your database migration so postgres is making sure there is no double-value wie [key, key1, key2...]
add_index :table, [:personal_id, :cost_center_id], :unique => true
then you need to go into your rails-model and catch that uniqueness at validations.
validates_uniqueness_of :personal_id, :scope => :cost_center_id
with that, rails is querying every time before creating a object, the database and check if something with the unique-pair-values already exists. if so, its adding it to the #errors of the model, so the model can't be saved
You can use uniqueness validation, but in my experience when importing data from CSV, the problem is that if the item already exists, all the validation does is stop the record being saved. In most examples, you usually want to do something with the matched record. Therefore, I'd suggest you also look at find_or_initialize_by. This allows you to also update existing records from imported data.
So if you have Thing model with a name and cost for example, you may want to identify existing things by name, and update their costs. And create new things where no matching name exists. The following code would do that:
name, price = some_method_that_gets_name_and_price_from_csv
thing = Thing.find_or_initialize_by name: name
thing.price = price
thing.save
Also have a look at find_or_create_by which can be more suitable in some situations. I'd also still keep the validation of uniqueness in the model. I just wouldn't use validation to handle how the data was imported.

Can't figure out how to query a 2nd level resource in Rails

I'm having trouble when querying Users.
My nesting resources are:
resources :users do
resources :photos do
resources :pins
end
end
1.) I have a user model, that has_many :photos.
2.) :photos has_many :pins
I want to list my users on which users have more pins in their photos.
So, I tried:
#members_ordered = User.includes(photos: :pins).group("users.id").group("photos.id").group("pins.id").order('COUNT(pins.id) DESC')
Not working though. Any ideas? Thanks guys
I have two observations, but neither directly fix the code in your example.
First, looking at my output from trying something similar, it seems like you either need quite complex SQL (which really isn't Rails' forte) or several simple queries (which, depending on the size of your app, could hit performance) to achieve this.
A little experimenting doesn't seem to show a significant difference (<1ms) in the time that one more complex query takes compared to that which three simple queries require (as in solution one)
Solution one, if performance is not crucial, for example, if this is a small, low-traffic solution, my instinct would be to add that the User model has_many :pins, through: :photos, which lets you call things like User.includes(photos: :pins).all, then user.pins.count, although, as I've mentioned, this causes a bit more database use.
Solution two, if performance is important, my suggestion would be to cache the count of pins against the user model. This could be as simple as an extra database column to store it, and have a background process (using delayed_job or similar) re-calculate the count each time it changes (so, maybe after_create in the Pin model.
The benefit of this is the slow, time-consuming query only gets run when the value changes, and the rest of the time, the value gets lifted from a single-table SELECT, which should take quite a bit less time than either solution one or the more complex query.
Both of these are less-than-perfect, and I think the most elegant and efficient way of working is to use a combination of a built-in function and a beautifully simple query:
The third solution, which brings together both of these options to some extent, is Rails' counter_cache option. As there are two levels to it, I can't see a native way to include all of these in one query, so we will automatically generate a count for each Photo, then add these up to get the User count.
Create a migration to add a pins_count field to the Photo model, so, in terminal, type;
rails g migration AddPinsCountToPhotos pins_count:integer
Update the belongs_to :photo line of the Pin model to;
belongs_to :photo, counter_cache: true
Now, every time a Pin gets created or deleted, the pins_count column of its Photo will be updated.
Now, to get the values for users;
Create a migration to add a pins_count field to the User model, so, in terminal, type;
rails g migration AddPinsCountToUsers pins_count:integer
Now we need to create an method in the Photo model, which we will run each time a pin is saved, so add this to your Photo model;
def update_user_counts
total_photos = self.user.photos.sum(:pins_count)
self.user.update_attribute(:pins_count, total_photos)
end
Finally, we need to tell Rails to call this whenever a pin is created or updated. We do this with a simple method that just calls the action from the Photo model;
after_save :update_photo_counts
def update_photo_counts
photo.update_user_counts
end
Now, whenever a pin is saved, it automatically updates the Photos pins_count, and then our new method totals the pins_counts from all of the Photos for that user, and saves them to the Users pins_count

Rails 4 SQL Join Code

I'm building a marketplace app. I have a Listing model (users list items to sell) and a User model. In the listing model, I have a userid column. And in the User model, I have a name field. In my listing show page, I want to display something like the below:
"Sold by #{#listing.user.name}"
But the join doesn't work in retrieving the name from the user table. If I change it to listing.userid then it works but I want to display the users name.
my user model has has_many :listings, dependent: :destroy
My listings model has belongs_to :user.
How can I display the user's name on the listing show page?
If you really have a column called userid instead of user_id then you have something very slightly different to what Rails expects... which is why Rails isn't finding it for you automatically.
Your best bet is to rename the column (using a migration) to user_id to take advantage of the Rails default behaviour. Trust me - it's worth the effort up front if you can do this.
If for some odd reason you can't (serious business constraints), then there are ways of telling rails that you are using a non-standard foreign-key... but lets not get to that unless you have to.

Does rails load associated relation each time

E.g. I have a model called Book which belongs_to model Shelf with fields :bookcase and :number.
I wonder, if I call to book.shelf.bookcase and then later to book.shelf.number does Rails actually load corresponding shelf each time? Or does it cache book.shelf somewhere? Shouldn't I make a new variable shelf = book.shelf and use shelf.bookcase and shelf.number instead?
Rails will cache association information, so you get whatever is in the cache when you access it. It does not reload it from the database each time.
There are cases when you do want a reload from the database, in which case the following syntax is used:
book.shelf(true).number
See section 3.1 in A Guide to Active Record Associations.

Rails 3 destroy oldest item in database if user owns more than 10

I have an Activity stream I have designed using STI in rails 3.
I want to do a check to see if a user has more than 10 activity items on create. When a user creates his 11th Activity in the database, I want it to destroy the oldest record * essentially the first record the user made and so on.* This way I am keeping my database clean of thousands of old useless records.
I am assuming I would start at the model level, but since I dont want to define the "current_user" in the model, it should be in the controller.
Any help would be appreciated to accomplish this task, I am fairly new at these type of more advanced tasks in rails.
You can hook this logic up in an AR callback. Assuming you kept rails conventions when modeling you classes, and each activity belongs to a user, you can then easily do the following in your Activity model:
after_save do |record|
if Activity.where(:user => record.user).count >= 11
Activity.where(:user => record.user).order('created_at asc').first.destroy
end
end
I guess this will create three transactions to the db (one for count, another to find the first record, and one to delete it). I wonder if there's a more efficient way to do this, as it will be invoked on every Activity#save...