rake db:seed populate keys when seed - ruby-on-rails

I have state data in a txt file that I used to seed my States db which has columns :id and :name. the :name is the state 2-digit code. Used the following code in seeds.rb file:
State.delete_all
open("C:/Sites/rails_projects/sales_tracking/lib/assets/states.txt") do |states|
states.read.each_line do |state|
name = state
State.create!(:name => name)
end
end
I now have my Cities.txt file with data of city,state. My cities db has columns :id, :name, :state_id. :state_id is the foreign key from the states table. What code do I need to add to the below part of my seeds.rb file to populate the :state_id while running rake db:seed on the city seed data ("code" is 2-digit state id).
City.delete_all
open("C:/Sites/rails_projects/sales_tracking/lib/assets/cities.txt") do |cities|
cities.read.each_line do |city|
name, code = city.chomp.split(",")
??
City.create!(:name => name, :state_id => state_id)
end
end

Use a dynamic finder to get the state:
City.create!(:name => name, :state => State.find_by_name(code))
Or if you'd like to avoid a few queries, and if the state code is guaranteed to exist, you can keep track of the states as you seed them in a hash and reuse them for the cities:
State.delete_all
City.delete_all
#states = {}
open("states.txt").read.each_line do |code|
#states[code] = State.create!(:name => code)
end
open("cities.txt").read.each_line do |city|
name, code = city.chomp.split(",")
City.create!(:name => name, :state => #states[code])
end

Related

Rename a certain key in a hash

I have a column car_details with 2000 entries, each of which is a hash of info that looks like this:
{"capacity"=>"0",
"wheels"=>"6",
"weight"=>"3000",
"engine_type"=>"Diesel",
"horsepower"=>"350",
"fuel_capacity"=>"35",
"fuel_consumption"=>"30"}
Some cars have more details, some have less. I want to rename the "fuel_consumption" key to "mpg" on every car that has that key.
Well, a previous answer will generate 2000 requests, but you can use the REPLACE function instead. Both MySQL and PostgreSQL have that, so it will be like:
Car.update_all("car_details = REPLACE(car_details, 'fuel_consumption', 'mpg')")
Take a look at the update_all method for the conditions.
See also PostgreSQL string functions and MySQL string functions.
Answer posted by #Ivan Shamatov works very well and is particular important to have good performances on huge databases.
I tried it with a PostgreSQL database, on a jsonb column.
To let it works we have to pay same attention to data type casting.
For example on a User model like this:
User < ActiveRecord::Base {
:id => :integer,
:created_at => :datetime,
:updated_at => :datetime,
:email => :string,
:first_name => :string,
:last_name => :string,
:custom_data => :jsonb
}
My goal was to rename a key, inside custom_data jsonb field.
For example custom_data hash content from:
{
"foo" => "bar",
"date" => "1980-07-10"
}
to:
{
"new_foo" => "bar",
"date" => "1980-07-10"
}
For all users records present into my db.
We can execute this query:
old_key = 'foo'
new_key = 'new_foo'
User.update_all("custom_data = REPLACE(custom_data::text, '#{old_key}'::text, '#{new_key}'::text)::jsonb")
This will only replace the target key (old_key), inside our jsonb hash, without changing hash values or other hash keys.
Note ::text and ::jsonb type casting!
As far as I know, there is no easy way to update a serialized column in a data table en masse with raw SQL. The best way I can think of would be to do something like:
Car.find_each do |car|
mpg = car.car_details.delete("fuel_consumption")
car.car_details["mpg"] = mpg if mpg
car.save
end
This is assuming that you are using Active Record and your model is called "Car".

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

Having create fail when id already exists

I'm using a rake task to populate my database with some initial data. I want to create a bunch of entries in a table, with the first few IDs, so they're always present, and always have those ids. I don't mind if in a dev environment, someone adds/deletes/modifies records, but I always want the first 5 ids to have values. Here's a simplified version of my lib/tasks/bootstrap.rb file:
namespace :bootstrap do
desc "Create the default problem types"
task :default_problem_types => :environment do
ProblemType.create( :id => 1, :name => 'Wrong location', :description => 'blah' )
ProblemType.create( :id => 2, :name => 'Wrong name', :description => 'blah' )
ProblemType.create( :id => 3, :name => 'Wrong details', :description => 'blah' )
ProblemType.create( :id => 4, :name => 'Duplicate', :description => 'blah' )
ProblemType.create( :id => 5, :name => 'No longer exists', :description => 'blah' )
end
desc "Run all bootstrapping tasks"
task :all => [:default_problem_types]
end
This works fine on an empty database. It creates 5 new entries in the problem_types table:
1 - Wrong Location
2 - Wrong name
3 - Wrong details
4 - Duplicate
5 - No longer exists
The problem is that if I run it a second time, it creates 5 new records, with IDs 6, 7, 8, 9, 10. That's despite the fact that I provided ids to the create() call that already exist. I'm expecting those calls to fail, because if I try to do the following SQL:
insert into problem_types (id, name, description) values (1, 'foo', 'bar');
... it fails:
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
How do I get the create() method to fail if the ID already exists?
Thanks.
Use a dynamic find_or_create_by or find_or_initialize_by method. You can read more about these in the Rails guides for the Active Record query interface. They allows you to write something like this for each ProblemType without creating duplicates by name:
problem_type = ProblemType.find_or_initialize_by_name('Wrong location')
problem_type.description = "blah"
problem_type.save
I agree with #benchwarmer; it's best to avoid managing the primary key yourself. If you must have a numerical identifier that some semantic value, try adding a separate column and set that value accordingly.
Rule of thumb: You dont assign ID on your own. Let database do its work. If you want your problem names to be unique then you can add validates_uniqueness_of :name into model file and the records won't be created if the same name exists

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

How do I seed a belongs_to association?

I would like to seed my Products and assign them to a specific User and Store.
Product.rb
class Product < ActiveRecord::Base
belongs_to :user
belongs_to :store
def product_store=(id)
self.store_id = id
end
end
Note: Store belongs_to Business (:business_name)
Seed.rb
This is my basic setup:
user = User.create(:username => 'user', :email => 'user2#email.com')
store = Store.create(:business_name => 'store', :address => 'Japan')
I attempted these but they did not work:
# This gives random ID's ranging from 1 to 4425!?
user.products.create([{:name => "Apple", :product_store => Store.find_by_address('San Francisco, USA')}])
# This gives me undefined method 'walmart'.
user.store.products.create([ {:name => "Apple"} ])
Is there a way to set the ID's so I can associate my Products to a Store and User?
UPDATE -
I have tried the answers below and still came out unsuccessful. Does anyone know of another way to do this?
Although it sounds like you found a workaround, the solution may be of interested to others.
From your original seeds.rb
user = User.create(:username => 'user', :email => 'user2#email.com')
store = Store.create(:business_name => 'store', :address => 'Japan')
Create the store
Store.create({
user_id: user.id
store_id: store.id
}, without_protection: true)
In the original code snipped "user" and "store" variables are declared. The code assigns user_id / store_id (the model columns inferred by the belongs_to relationship in the Store model) to the id values that are present in the "user" and "store" variables.
"without_protection: true" turns off bulk assignment protection on the id fields. This is perfectly acceptable in a seeds file but should be used with extreme caution when dealing with user provided data.
Or alternatively create your stores.
Then extract the correct one
e.g.
store = Store.find_by_business_name('Test Store')
and then create it based on that
e.g.
store.products.create(:product_name => "Product Test", :price => '985.93')
This will then set the relationship id for you,
If I'm not mistaken, you're just trying to do this.
user = User.create(:username => 'usertwo', :email => 'user2#email.com')
walmart = Store.create(:business_name => 'Walmart', :address => 'San Francisco, USA')
user.products.create(:name => 'Apple', :store => walmart)
Anything else required here that I'm not seeing?
Try doing this
store_1 = Store.new(:business_name => 'Test Store',
:address => 'Test Address',
:phone_number => '555-555-555')
store_1.id = 1
store_1.save!
The trick is not to set the id within the hash as it is protected.
Scott
What I did was update the particular products to a certain user, see this question:
Can I update all of my products to a specific user when seeding?
You could just create a series of insert satements for this "seed migration", including the record Id for each user, store, product etc. You might have to update database sequences after this approach.
Another approach
Create the initial records in you Rails app, through the GUI / web.
Then use something like Yaml-db. So you can dump the data to a yaml file. You can now edit that file (if necessary) and use that same file to seed another instance of the db with "rake db:load"
Either way.... You know the Ids will not be shifting around on you when these objects are created in the new db instance.
I'm sure there are other ways to do this... Probably better ones, even.
Here is a link to a write-up I did a while back for using yaml_db to seed an oracle database
http://davidbharrison.com/database_seeding_oracle
Try this:
User.destroy_all
Product.destroy_all
user = User.create!([{:username => 'usertwo', :email =>'user2#email.com'},
{:username => 'userthree', :email => user3#email.com}])
user.each_with_index do |obj, index|
Product.create!([{ :product_name => 'product #{index}', :user_id => obj.id }])
end
The table would look like this:
Here's how I prefer to seed an association in rails 6
#teacher = Teacher.new(name: "John")
#student = #teacher.build_student(name: "Chris")
#teacher.save!
#student.save!

Resources