unknown attribute Rails on import CSV uninitialized constant - ruby-on-rails

I am getting the error ActiveRecord::UnknownAttributeError in ProductsController#import, while trying to import a large csv file.
The migration is done and the table is created.
When i try to process the file I get,
unknown attribute 'CO_NO' for Product.
Extracted source (around line #14):
Model:
class Product < ActiveRecord::Base
attr_accessor :co_no, :parcel_id, :file_t, :asmt_yr
require 'csv'
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
product_hash = row.to_hash # exclude the price field
product = Product.where(id: product_hash["id"])
if product.count == 1
product.first.update_attributes(product_hash)
else
Product.create!(product_hash)
end # end if !product.nil?
end # end CSV.foreach
end # end self.import(file)
end # end class
controller:
classProductsController<ApplicationController
def index
#products=Product.all
end
def import
Product.import(params[:file])
redirect_toroot_url,notice:"Productsimported."
end
end
csv header:
co_no,parcel_id,file_t,asmnt_yr,bas_strt plus many more
27,"0000009500001010","R",2014
irb output:
Product.all
NameError: uninitialized constant Product
from (irb):2
from /Users/david/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'

Just a hunch without trying out the code, but the error message you are showing...
unknown attribute 'CO_NO' for Product.
indicates an input attribute that is "CO_NO", but you've declared "co_no". Since Ruby is case sensitive for variable names is probably failing to find attribute "CO_NO" (as opposed to "co_no").

By adding quotes around the cvs header, I was able to get past the error.
"co_no","parcel_id","file_t","asmnt_yr","bas_strt"
Also, I discovered coming from Rails 3 to Rails 4 that attr_accessor is now handled in the controller. After these 2 tweaks the data imported correctly

Related

How to solve NameError for QueryAssociationColumn on Redmine Plugin?

I'm new to Ruby and Rails and was trying to create a simple patch plugin that includes another plugin's extra property as a column available when listing issues.
Based on what I've read so far I wrote a new file at lib/issue_query_patch.rb containing:
require_dependency 'issue_query'
module IssueQueryPatch
def self.included(base) # :nodoc:
base.send(:include, InstanceMethods)
base.class_eval do
alias_method_chain :available_columns, :story_points
end
end
module InstanceMethods
# Adds the story points column to default the redmine issue query
def available_columns_with_story_points
columns = available_columns_without_story_points
columns << get_story_points_column
return columns
end
def get_story_points_column
return #story_points_column if #story_points_column
#story_points_column = QueryAssociationColumn.new(:agile_data, :story_points, :caption => :label_agile_story_points)
#story_points_column
end
end
end
IssueQuery.send(:include, IssueQueryPatch)
But when I try to list my issues I get:
NameError (uninitialized constant IssueQueryPatch::InstanceMethods::QueryAssociationColumn):
A similar approach using QueryColumn does not raise a NameError.
Since both classes are declared at app/models/query.rb I'm clueless to why this is happening.
How can I get rid of this error?

Rails - Import csv file into nested route

I have successfully gotten CSV files to be imported following the steps outlined in the Railscast 396 episode. However, now I'm trying to import a csv that is nested. I have seen other posts where people are trying to import both the parent and the child data but in this case I'm trying to only import the child data on the econ_report show view.
For example, I have econ_results that I'm trying to import that is nested under an econ_report. The rake file shows the following link.
import_econ_report_econ_results
When I try to load the show page for the econ_report I get the following error
No route matches {:action=>"import", :controller=>"econ_results", :id=>"1"} missing required keys: [:econ_report_id]
I have the following code in the econ_result.rd file:
def self.to_csv
CSV.generate do |csv|
csv << column_names
all.each do |sub|
csv << sub.attributes.values_at(*column_names)
end
end
end
def self.import(file)
CSV.foreach(file.path,headers: true) do |row|
EconResult.create! row.to_hash
end
end
Lastly, in the econ_results_controller I have the following code:
before_action :set_econ_report
def set_econ_report
#econ_report = EconReport.find(params[:econ_report_id])
end
def import
EconResult.import(params[:file])
end
def create
#econ_result = #econ_report.econ_results.create(econ_result_params)
redirect_to #econ_report
end
Is the designation of #econ_report causing the conflict in the controller?
You need to tell rails that the record you are passing is for the econ_report_id param. The default is that the route helper will use a passed record for the id param.
<%= link_to import_econ_report_econ_results_path(econ_report_id: #econ_report) %>
I would write a helper method since the name is ridiculously long.

Rails 4 - Uninitialized constant Mongo Model

I am attempting to setup MongoHQ using Heroku and rails 4. I have everything setup correctly to my knowledge, but I'm now getting this error:
uninitialized constant Job::TempEmailContactStoreCsv
This is the Job model where error is happening:
class Job < ActiveRecord::Base
belongs_to :user
def store_email_contact_csv(file)
contact_array = csv_to_array(file)
TempEmailContactStoreCsv.create(email_contact_array: contact_array, job_id: id)
end
end
And my mongo model:
class TempEmailContactStoreCsv
include Mongoid::Document
field :email_contact_array, type: Array
field :job_id
def self.store(job_id, email_contact_array)
r = TempEmailContactStoreCsv.find_by(job_id: job_id)
if (r.nil?)
TempEmailContactStoreCsv.create!(job_id: job_id, email_contact_array: email_contact_array)
end
end
def self.exists?(job_id)
r = TempEmailContactStoreCsv.find_by(job_id: job_id)
return r.nil? == false
end
def self.retrieve(job_id)
return TempEmailContactStoreCsv.find_by(job_id: job_id)
end
def self.delete(job_id)
r = TempEmailContactStoreCsv.find_by(job_id: job_id)
r.destroy unless r.nil?
end
end
So it seems that my mongo model is not being initialized, and the namespacing seems weird to me also.
Any thoughts as to what is causing this error and how to fix it?
For rails to load a class automatically, the file must be within rails load path (which includes app/models so you are fine there) and the file name should be the camelcased version of the class name.
In your case the file name should be temp_email_contact_store_csv.rb not temp_email_store_csv.rb

undefined local variable or method

I am following this code https://stackoverflow.com/a/17886089/692622, here is my client.rb model file and client_controller.rb file
# app/models/client.rb
before_create :add_unsubscribe_hash
private
def add_unsubscribe_hash
self.unsubscribe_hash = SecureRandom.hex
end
# app/controllers/clients_controller.rb
def unsubscribe
client = Client.find_by_unsubscribe_hash(params[:unsubscribe_hash])
client.update_attribute(:subscription, false)
end
but when I am trying to add clients through /clients/new (I have all the 7 methods in controller file too), I am getting error
undefined local variable or method `add_unsubscribe_hash'
The error is coming while saving client in create method
respond_to do |format|
if #client.save
any idea what is wrong since everything looks alright
EDIT - I have added the model code at pastebin http://pastebin.com/jkegLsaE
Notice that in line 40 of your Pastebin, you've opened a foreach loop that should terminate on line 42, but doesn't. Instead, the foreach loop encompasses the entire add_unsubscribe_hash function declaration, so it's not callable by the :before_create callback.
Resolve this by concluding the loop within the function it should be closed in (and ensure that you remove the extraneous end tag at the end of the file):
# app/models/contact.rb
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Contact.create! row.to_hash
end
end

Legacy table with column named "class" in Rails

I've got a legacy table that my rails application shares with another application. It has a column called "class". The first time I reference any attribute in that model, I get an error. Subsequent references to attributes work. Is there a good workaround for this, or should I just go modify the other application that uses this table (ugh)?
>> Member::Ssg.find(:first)
=> #<Member::Ssg ssg_key: #<BigDecimal:10b169688,'0.253E3',4(8)>, org_id: 2, academic_year: 2006, class: true, next_due_date: "2011-06-01", submitted_date: "2006-02-13", notes: nil, owner_id: "1">
>> Member::Ssg.find(:first).notes
NoMethodError: undefined method `generated_methods' for true:TrueClass
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/attribute_methods.rb:247:in `method_missing'
from (irb):2
>> Member::Ssg.find(:first).notes
=> nil
SOLUTION:
I went with a combination of the Bellmyer solution and adding the code below to my model
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
NOTE: Please see the updated solution at the end of this answer. Leaving the original outdated solution for historic reasons.
This has come up often enough (legacy column names interfering with ruby/rails) that I might just make a plugin out of this. Here's how you can fix it right away, though. Create this file in your app:
# lib/bellmyer/create_alias.rb
module Bellmyer
module CreateAlias
def self.included(base)
base.extend CreateAliasMethods
end
module CreateAliasMethods
def create_alias old_name, new_name
define_method new_name.to_s do
self.read_attribute old_name.to_s
end
define_method new_name.to_s + "=" do |value|
self.write_attribute old_name.to_s, value
end
end
end
end
end
And now, in your model:
class Member < ActiveRecord::Base
include Bellmyer::CreateAlias
create_alias 'class', 'class_name'
end
The first parameter to create_alias is the old method name, and the second parameter is the new name you want to call it, that won't interfere with rails. It basically uses the read_attribute and write_attribute methods to interact with the column instead of the ruby methods that get defined by ActiveRecord. Just be sure to use the new name for the field everywhere, like so:
member.class_name = 'helper'
This works with ruby 1.8, but I haven't tested with ruby 1.9 yet. I hope this helps!
UPDATE: I've found a better solution that works in Rails 3, the safe_attributes gem. I've written a blog post explaining how to use it, with example code snippets, and a full sample app you can download from github and play around with. Here's the link:
Legacy Database Column Names in Rails 3
The following works in Rails 6.0.2.2
class ReasonCode < ApplicationRecord
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
def as_json(options={})
add_class = attributes.keys.include?('class')
if add_class
if options[:only]
add_class = Array(options[:only]).map(&:to_s).include?('class')
elsif Array(options[:except])
add_class = Array(options[:except]).map(&:to_s).exclude?('class')
end
end
options[:except] = Array(options[:except])
options[:except].push('class')
json = super(options)
json['class'] = attributes['class'] if add_class
json
end
end
Adapted from this answer https://www.ruby-forum.com/t/activerecord-column-with-reserved-name-class/125705/2. The as_json method was added because rendering the record as json gave a SystemStackError (stack level too deep). I followed the serialization code in the Rails repo to only render the class attribute if specified in the as_json options.

Resources