Rails referencing parent id during csv import - ruby-on-rails

Firstly, I'm really sorry to ask such a basic question but I'm very new to Rails, Ruby & development in general and my lack of terminology understanding means that I am having a hard time finding the answer to this question.
I am importing a csv of vehicle data using:
def self.import(file, category_id)
CSV.foreach(file.path, headers: true) do |row|
Model.where(
:category_id => category_id,
:name => row[1],
:cap_id => row[10]
).first_or_create do |record|
record.layout = row[3],
... several records later ...
record.manufacturer_id = Manufacturer.where(:name => row[0], :category_id => category_id).id
end
end
end
but I am having trouble setting the manufacturer_id with the last line of the loop. I guess you can see what i'm trying to do here but I can't work out how this should be written.
Basically i have a list of manufacturers belonging to different categories (so, for example, there is a Ford manufacturer with category_id=1 (for cars) and another Ford manufacturer with category_id=54 for trucks.
Can I set the manufacturer_id of each record by taking the current category_id and the manufacturer name from the csv, cross referencing them and pulling back the id of the result?
When trying the above code i get the following error:
NoMethodError in ModelsController#import
undefined method `id' for #<ActiveRecord::Relation::ActiveRecord_Relation_Manufacturer:0x007fcd96f744a8>
Extracted source (around line #33):
32 record.description = row[2],
33 record.manufacturer_id = Manufacturer.where(:name => row[0], :category_id => category_id).id
34 end
35 end
36 end

The following line of code returns an ActiveRecord::Relation object
Manufacturer.where(:name => row[0], :category_id => category_id)
so when you called id on it, it raised an error. You should call first first and then id.
Manufacturer.where(:name => row[0], :category_id => category_id).first.id
Be warned that doing so assumes that you'll always get a manufacturer that matches the condition. To be more safe, you can use try
Manufacturer.where(:name => row[0], :category_id => category_id).first.try(:id)

Related

How do I get data from Arel output?

I'm successfully following some of the commands listed here and applying them to my own tables.
How do I turn the output into actual records/data ? Presently it's returning some output which I can assign to a variable, but I do not understand this output at all.. it doesn't look like an array or hash (but perhaps it is), and I do not understand how to access its contents (I want to use the output in a rails view, but at the least would like to access the table records in the console where I'm executing the Arel code)
For example, say I have two tables; User and Product as follows:
User = [{:id => 1, :name => "Joe", :email => "joe#gmail.com"}, {:id => 2, :name => "Jane", :email => "jane#gmail.com"}]
Product = [{:product_name => "Car Tire", :brand => "BMW", :last_edited_by => 1}, {:product_name => "Paint Brush", :brand => "Dulux", :last_edited_by => nil}, {:product_name => "Dog Biscuits", :brand => "Bowow Snacks", :last_edited_by => 2}, {:product_name => "Game", :brand => "Westwood", :last_edited_by => 1}]
Suppose I want to join the two tables User.id = Product.last_edited_by
In SQL it would be as simple as:
SELECT *
FROM User
JOIN Product
WHERE users.id = products.last_edited_by
Now if I plug this into scuttle.io to convert to Arel, it gives:
User.select(Arel.star).where(
User.arel_table[:id].eq(Product.arel_table[:last_edited_by])
).joins(
User.arel_table.join(Product.arel_table).on(null).join_sources
)
But trying this returns: ArgumentError: wrong number of arguments (given 1, expected 0)
Attempting to emulate advice given here, I come up with another approach:
users.project(Arel.star).join(User).on(Product[:last_edited_by].eq(User[:id]))
But this returns: NoMethodError: undefined method `project' for Array..
A final point, if I run
users = Arel::Table.new(User)
output = users.project(users[:id].as("id"), users[:id].count.as("count")).group("id")
I get some output which looks almost meaningful, but I don't know how to return that into something I can make sense of, and I wouldn't know how to use it if I provided it to a rails view

populate random data in ruby on rails

I need help in populate some random data in my database table.
I have a list of 10 users in my system. My allergy table has the following fields:
id user_id name reactions status
I have the following allergies hash in a variable called allergy_hash.
{:reaction_name=>"Bleeding", :status=>"Death", :name=>"A"} {:reaction_name=>"Nausea", :status=>"Serious", :name=>"B"} {:reaction_name=>"Fever", :status=>"Death", :name=>"C"} {:reaction_name=>"Blistering", :status=>"Serious", :name=>"D"}
Here is what I have done so far:
def create_random_data
users.each do |user|
allergies.each do |allergies_hash|
Allergy.where(user_id: user.id).first_or_create(
allergies_hash )
end
end
end
What the above does is just inserts Bleeding, Death and A into the table for all users 1 to 10.
But I need to insert such that different users can have different values. Also some users can have more than one allergy and the associated reactions.
NOTE: I do not mean completely random. For example name 'A' should still have the associated status 'Death' and reaction_name 'Bleeding'.
Name 'B' should have the associated status 'Serious' and reaction 'Nausea'in the allergy table.
When creating the users, use sample on allergies_hash = [{:reaction_name=>"Bleeding", :status=>"Death", :name=>"A"}, {:reaction_name=>"Nausea", :status=>"Serious", :name=>"B"}, {:reaction_name=>"Fever", :status=>"Death", :name=>"C"}, {:reaction_name=>"Blistering", :status=>"Serious", :name=>"D"}]
Allergy.where(user_id: user.id).first_or_create(allergies_hash.sample)
UPDATE
I'll loop through the users instead, so for each user you attempt to add from 1 to 3 allergies from your allergies_hash
User.all.each do |user|
[1,2,3].sample.times do
user.allergies.where(allergies_hash.sample).first_or_create
end
end
I would recommend you to check Faker and Factory girl to populate some random data.
You can either seed data into your app by going to the seed file in the app/db directory
and do something like this
User.delete_all
Bill.delete_all
u1 = User.create(:email => "bob#aol.com", :password =>"a", :password_confirmation => "a")
b1 = Bill.create(:name => "rent", :description => "the rent", :amount => 10_000, :day => 1)
b2 = Bill.create(:name => "cable", :description => "the cable", :amount => 150, :day => 5)
or you can also use the Faker gem to generate fake data.
http://geekswithblogs.net/alexmoore/archive/2010/01/18/faker-gem---a-quick-and-dirty-introduction.aspx

Importing CSV file into multiple models at one time

I have 3 models, Church, which has many Locations, which has many Pastors.
require 'csv'
csvfile = File.read("testimport.csv")
csv = CSV.parse(csvfile, :headers => false)
csv.each do |row|
c = Church.new
c.name = row[0]
c.url = row[10]
c.locations.build(:address => row[3], :zipcode => row[5], :phone => row[6], :email => row[2], :city => row[4])
c.save
end
As you can see in my short block of code I am creating a Church and its first location. How would I also add a pastor to that?
For instance will this work?
require 'csv'
csvfile = File.read("testimport.csv")
csv = CSV.parse(csvfile, :headers => false)
csv.each do |row|
c = Church.new
c.name = row[0]
c.url = row[10]
location = c.locations.build(:address => row[3], :zipcode => row[5], :phone => row[6], :email => row[2], :city => row[4])
location.pastors.build(:name => row[10])
location.save
c.save
end
Is there another way I should be going about this? Trying to move thousands of records from one rails app to another.
I take a slightly different approach to this, I have found a two step process is easier to use and build on.
The first step is load the data.
I use two 'staging' tables.
Something like:
staging_header
id Integer Unique Primary Key
run_number Integer Unique
run_name String
staging_data:
id Integer Unique Primary Key
staging_header_id Integer
element1 String
element2 String
element3 String
uploaded? Boolean # Placed on the individual records allows restarts.
...
So I load the testimport.csv directly into these loading tables - which support multiple runs if you make the run_number unique (sequence, etc.)
Now you have the data in sql and available in rails.
Now write the code to actually populate the application tables from this loading area.
This will also help you with the speed issue. Rails will only be inserting a few records per second, so you want to be able to do restarts, pauses, etc.
This will also help with validation. Initially you just want to load the data, regardless of any constraints (not null, unique, etc.).
Once loaded into staging you can be more selective and apply validations as you wish.
I was able to get this to work, the following is what I used. Let me know if there is a smarter way to do this. NOTE: if you are trying to do this, place the csv file in the root of your rails directory and execute this script line by line in the console. At least thats how I got it working.
require 'csv'
csvfile = File.read("testimport.csv")
csv = CSV.parse(csvfile, :headers => false)
csv.each do |row|
c = Church.new
c.name = row[0]
c.url = row[10]
c.locations.build(:title => "Sanctuary", :address => row[3], :zipcode => row[5], :phone => row[6], :email => row[2], :city => row[4])
c.save
loc = c.locations.first
loc.pastors.build(:firstname => row[1])
loc.save
end

Constructing a new object in Rails that is part of an active record association

This is just a simple question. I was trying to create a new object in Rails by passing in parameters to the constructor. However, when I execute the code, I get
SQLite3::SQLException: no such column: awards.user_id: SELECT "awards".* FROM "awards" WHERE "awards"."user_id" = 1
which means the object isn't being constructed properly. Should I be using create instead of new? That isn't working either.
def refresh_awards(user)
new_awards = []
if (user.karma < 40 ) #test award
a = Award.new(:name => "Nobody Award", :description => "From Jonathan", :category => "Community", :value => 1337, :level => 0, :handle => "nobody_award")
user.awards.append(a)
new_awards.append(a)
end
new_awards.each do |a|
flash[:notice] = "You received the " + a.name + "!"
end
end
Have you add has_many :awards to the User model? Have you added belongs_to :user to the Award model? Have you added the column user_id to the Award model (using a migration)? You'll need to do these three things to be able to use the user.awards method you're using. Read the Rails Guide on Associations for more detail.
Also, append isn't a Ruby method - the closest method would be <<. You would use it like this:
a = Award.new(:name => "Nobody Award", :description => "From Jonathan", :category => "Community", :value => 1337, :level => 0, :handle => "nobody_award")
user.awards << a
But you could neaten this into one line of code using the create method:
a = user.awards.create(:name => "Nobody Award", :description => "From Jonathan", :category => "Community", :value => 1337, :level => 0, :handle => "nobody_award")
EDIT: To create the user_id column in the Award model, run the following code from terminal (while in your app's directory):
rails generate migration AddUserIdToAward user_id:integer
rake db:migrate

Ruby on Rails 3: Updating join table, using 2 parameters?

I have a join table, which have 3 parameters. I want to update it, using a where-clause, something like this: (which obviously is not correct)
Grid.update(:page_id => #page_id,:thing_id => #thing_id,:number => #number ).where(:page_id => #page_id, :number => #number ).first
I need to find the record with mathing page_id and number, and then update the thing_id.
Thanks!
Jakob
Grid.where(:page_id => #page_id, :number => #number).first.
update_attributes(:page_id => ...,: thing_id => ..., :number => ...)

Resources