importing csv into ruby but need to create relationship - ruby-on-rails

I am wanting to import 2 files (County & State). When I import I need to create a relationship with County to State.
My State CSV file has:
id
state e.g. "Connecticut"
abbreviation e.g. "CT"
My County CSV file has:
id
name e.g. "Hartford"
market_state e.g. "Connecticut"
The attributes for MarketCounty are:
create_table "market_counties", force: :cascade do |t|
t.string "name"
t.integer "market_state_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The attributes for MarketState are:
create_table "market_states", force: :cascade do |t|
t.string "name"
t.string "abbreviation"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
My models have these relationships:
MarketState:
class MarketState < ActiveRecord::Base
has_many :market_reports, as: :location
has_many :market_cities
has_many :market_zips
has_many :market_counties
end
MarketCounty:
class MarketCounty < ActiveRecord::Base
has_many :market_reports, as: :location
has_many :market_cities
has_many :market_zips
belongs_to :market_state
end
My Rake task:
def import_market_states
MarketState.create!(name: "Connecticut", abbreviation: "CT")
end
def import_market_counties
path = Rails.root.join("config/csv/locations/counties.csv")
CSV.foreach(path, headers: true) do |row|
MarketCounty.create! row.to_hash
end
My question is in the the method import_market_counties, row.to_hash does not work.
ActiveRecord::AssociationTypeMismatch: MarketState(#70157557268540)
expected, got String(#70157439770720)
I set it up manually:
MarketCity.create!(name: "Hartford", market_state: MarketState.find_by(abbreviation: "CT")
How can I do a "lookup" in a loop when importing County file (to associate it to the MarketState model)?
To clarify, the issue is that when I am creating the County, I am not trying to populate state with CT. I need to populate state_id with the association.
I found this SO: When importing a CSV, how do I handle data in a row that corresponds to an association? ---
And thinking it may be similar to what I am trying to do - just not sure how to apply it here.

Assuming your MarketState has a name column that is the name of the state:
def import_market_counties
path = Rails.root.join("config/csv/locations/counties.csv")
CSV.foreach(path, headers: true) do |row|
MarketState.find_by_name( row['market_state'].strip )
end
you can also use find_or_create_by if you want to create the state if it doesn't exist.

Related

Rails does not save reference ID to record error: "Class must exist"

Issue is I can't find why reference column id can't be inserted when create new record.
I have 3 table shop_plan, shop and app
Below is tables schema:
create_table "shop_plans", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shops", force: :cascade do |t|
t.string "url"
t.bigint "plan_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["plan_id"], name: "index_shops_on_plan_id"
end
create_table "apps", force: :cascade do |t|
t.bigint "shop_id"
t.binint "amount"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["app_id"], name: "index_apps_on_shop_id"
end
add_foreign_key "shops", "shop_plans", column: "plan_id"
add_foreign_key "apps", "shops"
And below is Model
class ShopPlan < ApplicationRecord
has_many :shop
end
class Shop < ApplicationRecord
belongs_to :shop_plan, class_name: 'ShopPlan', foreign_key: :plan_id
has_many :app
end
class App < ApplicationRecord
belongs_to :shop, class_name: 'Shop', foreign_key: :shop_id
end
There will be 1 default record added in seed.db for table shop_plan
ShopPlan.create(name: 'Basic')
ShopPlan and Shop are linked by plan_id column in Shop
Shop and App are linked by shop_id column in App
I pre-insert some value when user access index:
#basic_plan
#basicPlan = ShopPlan.where(name: "Basic").first
# if new shop registered, add to database
unless Shop.where(url: #shop_session.url).any?
shop = Shop.new
shop.url = #shop_session.url
shop.plan_id = #basicPlan.id
shop.save
end
This insert works well, however, when i run 2nd insert:
#shop= Shop.where(url: #shop_session.url).first
unless App.where(shop_id: #shop.id).any?
app = App.new
app.shop_id = #shop.id,
app.amount = 10
app.save
end
error occurs as somehow app.shop_id will not add in my #shop.id and it will return will error: {"shop":["must exist"]}
I even try hard-code app.shop_id =1 but it does not help and when I add in optional: true to app.db model, it will insert null
Appreciate if anyone can help point out why I get this error
EDIT: #arieljuod to be clear
1) I have to specific exact column class due to between Shop And Shop_Plan, i'm using a manual plan_id instead of using default shopplans_id columns.
2) I have update 1 column inside App and all that unless is just to do checking when debugging.
First of all, like #David pointed out, your associations names are not right. You have to set has_many :shops and has_many :apps so activerecord knows how to find the correct classes.
Second, you don't have to specify the class_name option if the class can be infered from the association name, so it can be belongs_to :shop and belongs_to :shop_plan, foreign_key: :plan_id. It works just fine with your setup, it's just a suggestion to remove unnecesary code.
Now, for your relationships, I think you shouldn't do those first any? new block manually, rails can handle those for you.
you could do something like
#basicPlan = ShopPlan.find_by(name: "Basic")
#this gives you the first record or creates a new one
#shop = #basicPlan.shops.where(url: #shop_session.url).first_or_create
#this will return the "app" of the shop if it already exists, and, if nil, it will create a new one
#app = #shop.app or #shop.create_app
I have found out the silly reason why my code does not work.
It's not because as_many :shops and has_many :app and also not because my code when creating the record.
It just due to silly comma ',' when creating new record in App at app.shop_id = #shop.id,, as I was keep switching between Ruby and JavaScript. Thank you #arieljuod and #David for your effort

Rails InvalidForeignKey

I am developing a portfolio for my website, I decided to add skills to each portfolio item.
class PortfolioSkill < ApplicationRecord
belongs_to :portfolio
belongs_to :skill
end
class Portfolio < ApplicationRecord
has_many :portfolio_skills
has_many :skills, through: :portfolio_skills
def all_tags=(names)
self.skills = names.split(",").map do |name|
Skill.where(name: name.strip).first_or_create!
end
end
def all_tags
self.skills.map(&:name).join(", ")
end
def remove_skill_tags
PortfolioSkill.where(portfolio_id: id).destroy_all
end
end
create_table "portfolio_skills", force: :cascade do |t|
t.integer "portfolio_id"
t.integer "skill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["portfolio_id"], name: "index_portfolio_skills_on_portfolio_id"
t.index ["skill_id"], name: "index_portfolio_skills_on_skill_id"
end
create_table "portfolios", force: :cascade do |t|
t.string "name"
t.string "client"
t.date "completed"
t.text "about"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "long_landscape"
t.string "cover"
t.integer "category_id"
t.index ["category_id"], name: "index_portfolios_on_category_id"
end
When I click destroy on the index page I get the
SQLite3::ConstraintException: FOREIGN KEY constraint failed: DELETE FROM "portfolios" WHERE "portfolios"."id" = ?
error. All the associations look right. I used this same pattern for my tags on other models and it worked with no issues. Any help would be great.
You are deleting from portfolios table, but table portfolio_skills has a column referencing it as foreign key. Hence the error.
Trying to delete a parent without checking and deleting its associated children can lead to data inconsistency. This exception is in place to prevent that.
Rails dependent destroy will take care of removing associated children rows while removing a parent.
Try using a dependent destroy:-
class Portfolio < ApplicationRecord
has_many :portfolio_skills, :dependent => :destroy
...
end

Can't write unknown attribute using rails_admin

Im' using rails_admin to save a project that has a category within. I didn't define project_id and category_id because I thought they should be created by rails. The problem I got were using the method def category_id=(id) defined in project model (see below). The error is:
can't write unknown attribute `project_id`
My models are:
Category
class Category < ActiveRecord::Base
belongs_to :project, :inverse_of => :category
end
Project
class Project < ActiveRecord::Base
has_one :category, :dependent => :destroy, :inverse_of => :project
def category_id
self.category.try :id
end
def category_id=(id)
self.category = Category.find_by_id(id)
end
end
My schema:
create_table "categories", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "projects", force: :cascade do |t|
t.string "title"
t.text "text"
t.string "url"
t.string "key_feature"
t.string "image_1"
t.string "image_2"
t.string "image_3"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
How do you connect Projects to Categories?
Base on your schema Projects table has no category_id.
Neither you Category table has project_id
I would add category_id to Projects table.
rails g migration add_category_id_to_projects category_id:integer
rake db:migrate
The solution at the end was to manually add the project_id to categories table.
rails g migration add_project_id_to_categories project_id:integer
Thanks #Misha for you suggestion.

Rails 4 HABTM how to set multiple ids in console?

schema.rb:
ActiveRecord::Schema.define(version: 20150324012404) do
create_table "groups", force: :cascade do |t|
t.string "title"
t.integer "teacher_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "groups_students", id: false, force: :cascade do |t|
t.integer "group_id"
t.integer "student_id"
end
add_index "groups_students", ["group_id"], name: "index_groups_students_on_group_id"
add_index "groups_students", ["student_id"], name: "index_groups_students_on_student_id"
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "admin", default: false
t.string "type"
t.integer "group_id"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
group.rb:
class Group < ActiveRecord::Base
belongs_to :teacher
has_and_belongs_to_many :students
end
student.rb:
class Student < User
has_and_belongs_to_many :groups
end
I could have set a simple belongs_to and a has_many relationship between the student and group models, but I want students to be able to belong to more than one group, so I set up a HABTM association and corresponding join table.
I think I that right?
The question is, how do I, in the console, set a Student to belong to more than one group?
I have setup a User with 'type: Student' and I have two Groups. So...
In the console I do:
student = Student.first
Then, I want to set 'student' to belong to both Groups, but I don't know how to do this.
To set it to belong to one group I can do:
student.update_attributes(group_id: 1)
But how do make it belong to both groups? It would have two group_id's wouldn't it? I don't know how to set this.
If you need to see any of the other files, it's the 'handcode' branch here:
https://github.com/Yorkshireman/sebcoles/tree/handcode
The answers others have already provided are correct. But if you're working with id's you can also do something like this
student = Student.first
student.group_ids = 1,2,3,4
You don't need to set group_id for the User, the association is handled by the join table and the HABTM statement. You should remove group_id from the users table in the schema.
From memory you should be able to do something like this:
student = Student.first
groups = Group.all
student.groups << groups
student.save
See the active record guide on HABTM associations - specfically 4.4.1.3
Instead of habtm, just use the normal through and your life becomes easy. Make sure an id is generated for the association table (remove id:false)
create_table "group_students", force: :cascade do |t|
t.integer :group_id, nil:false
t.integer :student_id, nil:false
end
class Group < ActiveRecord::Base
has_many :group_students, dependent: :destroy, inverse_of :group
has_many :students, through :group_students
end
class Student < ActiveRecord::Base
has_many :group_students, dependent: :destroy, inverse_of :student
has_many :groups, through: :group_students
end
class GroupStudent < ActiveRecord::Base
belongs_to :group,
belongs_to :student
validates_presence_of :group, :student
end
Group.last.students << Student.last
or..
Student.last.groups << Group.last
Student.last.groups = [Group.find(1), Group.find(2)]
etc....
Ok, so it took me 3 days of all kinds of pain to work this out.
There was nothing wrong with my original code, except that I needed to remove the group_id from the user table.
roo's answer was correct, except that using 'group' as a variable name in the console confused Rails. This had led me to believe there was something wrong with my code, but there wasn't. You learn the hard way.
So, Students can be pushed into Groups like this:
To push a student into one group:
student = Student.first
OR
student = Student.find(1)
(or whatever number the id is)
group1 = Group.first
OR
group1 = Group.find(1)
student.groups << group1
To push into multiple groups (which was the original goal of this whole debacle:
student = Student.first
OR
student = Student.find(1)
allclasses = Group.all
student.groups << allclasses
To view your handywork:
student.groups
Works beautifully. The only problem I can see with my code is that it's possible to push the same student into a group twice, resulting in two duplicates of that student in one group. If anyone knows how to prevent this happening, I'm all ears.

Polymorphic Association and extra id field

I need some help with polymorphic associations. Below is my structure:
class Category < ActiveRecord::Base
has_ancestry
has_many :categorisations
end
class Categorisation < ActiveRecord::Base
belongs_to :category
belongs_to :categorisable, polymorphic: true
end
class classifiedAd < ActiveRecord::Base
belongs_to :user
has_one :categorisation, as: :categorisable
end
And here is my schema.rb
create_table "classifiedads", force: true do |t|
t.string "title"
t.text "body"
t.decimal "price"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "classifiedads", ["user_id"], name: "index_classifiedads_on_user_id", using: :btree
create_table "categories", force: true do |t|
t.string "name"
t.string "ancestry"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categories", ["ancestry"], name: "index_categories_on_ancestry", using: :btree
create_table "categorisations", force: true do |t|
t.integer "category_id"
t.integer "categorisable_id"
t.string "categorisable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
It seems like the associations is correct as when I'm in the console I can do the appropriate commands and all seems to return the right results, for example: Category.first.categorisations or ClassifedAd.first.categorisation. But what I don't understand is saving the association from the Create and editing the record via the Update actions. I'm using simple_form to create my forms and when looking at the params I get the below:
{"title"=>"Tiger",
"body"=>"Huge Helicopter",
"price"=>"550.0",
"categorisation"=>"5"}
and this fails to update or even create as I get this error : Categorisation(#70249667986640) expected, got String(#70249634794540) My controller actions code are below:
def create
#classified = Classifiedad.new(classifiedad_params)
#classified.user = current_user
#classified.save
end
def update
#classified.update(classifiedad_params)
end
def classifiedad_params
params.require(:classifiedad).permit(:title, :body, :price)
end
I think it has something to do with the params as categorisation should be within a sub hash of results, is this right? Also, I need to do something extra within the actions, but what? What the params[:categorisation] value needs to do is save the number into the Categorisations.category_id table column and also save the polymorphic association. This table will be used across other models, which will also be a has_one association, as the user will only be able to select one category for each record. I really hope someone can help me here as the more I look into it the more I get confused :S Please let me know if you ned anymore info from me.
I'm using Rails 4 and Ruby 2
EDIT 2
I managed to get something working but I'm still not sure if its right. Below is the update code for the Create and Update actions. Would be good to know if there is a better way of doing this?
def create
#classified = Classifiedad.new(classifiedad_params)
#classified.user = current_user
**** NEW
cat = Categorisation.new(category_id: params[:classified][:categorisation])
#classified.categorisation = cat
**** END NEW
#classified.save
end
def update
**** NEW
#classified.categorisation.update_attribute(:category_id, params[:classified][:categorisation])
**** END NEW
#classified.update(classifiedad_params)
end

Resources