Replacing values of a column in a migration - ruby-on-rails-3

I have a column called "p_type" which is string, since I thought to have types A, B and C as strings
now I realize that it is better to have that as a bitmask so I can have A and C together.
If I do
change_column :cars, :p_type, :integer
I will either lose all my existing p_type data or something will go horribly wrong and migration will distort the data somehow ( I am afraid to try)
Is there a way to change the column type to :integer and have some kind of function in the migration file that will do
if A
p_type = 1
elsif B
p_type = 2
elsif C
p_type = 4
end
so the migration would change the data type and the data itself?

You can run one rake task first to update the p_type column,
Model_name.each { |obj|
if obj.p_type == "A"
p_type = "1"
elsif obj.p_type == "B"
p_type = "2"
elsif obj.p_type == "C"
p_type = "4"
end
obj.save
}
Hope this will help.

solved it with How do I change column type in Heroku?
rename_column :cars, :p_type , :p_type_string
add_column :cars, :p_type , :integer
Car.reset_column_information
Car.find_each { |c| c.update_attribute(:p_type , c.p_type_string) }
remove_column :cars, :p_type_string

Related

Rails ActiveRecord where clause

I want to select Cars from database with where clause looking for best DRY approach for my issue.
for example I have this two parameters
params[:car_model_id] (int)
params[:transmission_id] (int)
params[:from_date]
params[:to_date]
but I dont know which one will be null
if params[:car_model_id].nil? && !params[:transmission_id].nil?
if params[:from_date].nil? && params[:from_date].nil?
return Car.where(:transmission_id => params[:transmission_id])
else
return Car.where(:transmission_id => params[:transmission_id], :date => params[:from_date]..params[:to_date])
end
elseif !params[:car_model_id].nil? && params[:transmission_id].nil?
if params[:from_date].nil? && params[:from_date].nil?
return Car.where(:car_model_id=> params[:car_model_id])
else
return Car.where(:car_model_id=> params[:car_model_id], :date => params[:from_date]..params[:to_date])
end
else
return Car.where(:car_model_id=> params[:car_model_id], :transmission_id => params[:transmission_id], :date => params[:from_date]..params[:to_date])
end
what is best approach to avoid such bad code and check if parameter is nil inline(in where)
You can do:
car_params = params.slice(:car_model_id, :transmission_id).reject{|k, v| v.nil? }
and then:
Car.where(car_params)
Explanation: Since, you're checking if the particular key i.e.: :car_model_id and transmission_id exists in params. The above code would be something like this when you have just :transimission_id in params:
Car.where(:transmission_id => '1')
or this when you have :car_model_id in params:
Car.where(:car_model_id => '3')
or this when you'll have both:
Car.where(:transmission_id => '1', :car_model_id => '3')
NOTE: This will work only when you have params keys as the column names for which you're trying to run queries for. If you intend to have a different key in params which doesn't match with the column name then I'd suggest you change it's key to the column name in controller itself before slice.
UPDATE: Since, OP has edited his question and introduced more if.. else conditions now. One way to go about solving that and to always keep one thing in mind is to have your user_params correct values for which you want to run your queries on the model class, here it's Car. So, in this case:
car_params = params.slice(:car_model_id, :transmission_id).reject{|k, v| v.nil? }
if params[:from_date].present? && params[:from_date].present?
car_params.merge!(date: params[:from_date]..params[:to_date])
end
and then:
Car.where(car_params)
what is best approach to avoid such bad code and check if parameter is
nil inline(in where)
Good Question !
I will make implementation with two extra boolean variables (transmission_id_is_valid and
car_model_id_is_valid)
transmission_id_is_valid = params[:car_model_id].nil? && !params[:transmission_id].nil?
car_model_id_is_valid = !params[:car_model_id].nil? && params[:transmission_id].nil?
if transmission_id_is_valid
return Car.where(:transmission_id => params[:transmission_id])
elseif car_model_id_is_valid
return Car.where(:car_model_id=> params[:car_model_id])
....
end
I think now is more human readable.
First, I would change this code to Car model, and I think there is no need to check if params doesn't exists.
# using Rails 4 methods
class Car < ActiveRecord::Base
def self.find_by_transmission_id_or_model_id(trasmission_id, model_id)
if transmission_id
find_by trasmission_id: trasmission_id
elsif model_id
find_by model_id: model_id
end
end
end
In controller:
def action
car = Car.find_by_transmission_id_or_model_id params[:trasmission_id], params[:car_model_id]
end
edit:
This code is fine while you have only two parameters. For many conditional parameters, look at ransack gem.

Rails 3 validates_uniquess_of virtual field with scope

I am working on a Rails 3 app that is needing to run a validation on a virtual field to see if the record already exists... here is my model code:
#mass-assignment
attr_accessible :first_name, :last_name, :dob, :gender
#validations
validates_presence_of :first_name, :last_name, :gender, :dob
validates :fullname, :uniqueness => { :scope => [:dob] }
def fullname
"#{first_name} #{last_name}"
end
I am not getting any errors, its passing the validation.
I don't think you can do something like that with standard validates. Reason is, it usually is a "simple" sql look-up for uniqueness. But to check uniqueness of what's sent back from your method, it'd have to :
Get all entries of your scope
For each, execute the fullname method.
Add every result in an array while testing for uniqueness
Simple when the data set is small, but then it'd just take ages to do as soon as you reach 10K plus matches in your scope.
I'd personally do this with a standard before_save using the dob scope
before_save :check_full_name_uniqueness
def check_full_name_uniqueness
query_id = id || 0
return !Patient.where("first_name = ? and last_name = ? and dob = ? and id != ?", first_name, last_name, dob, query_id).exists?
end
Add error message (before the return):
errors.add(:fullname, 'Your error')
Back in the controller, get the error :
your_object.errors.each do |k, v| #k is the key of the hash, here fullname, and v the error message
#Do whatever you have to do
end

How to convert postgresql column into a float for a scoped search in rails

I am trying to search my postgresql db in rails. I followed the Railscasts #111 Advanced Search tutorial and it is working for the name and category of my items column in plain text. However, I want to set a min/max price on my search as well which is where I come into my problem. In my db my price is stored as a string in the format "AU $49.95". Can I convert this into a float on the fly in my scoped search? If so how? If not, what should I do?
Here is the code:
search.rb
class Search < ActiveRecord::Base
attr_accessible :keywords, :catagory, :minimum_price, :maximum_price
def items
#items ||= find_items
end
private
def find_items
scope = Item.scoped({})
scope = scope.scoped :conditions => ["to_tsvector('english', items.name) ## plainto_tsquery(?)", "%#{keywords}%"] unless keywords.blank?
scope = scope.scoped :conditions => ["items.price >= ?", "AU \$#{minimum_price.to_s}"] unless minimum_price.blank?
# scope = scope.scoped :conditions => ["items.price <= ?", "AU \$#{maximum_price.to_s}"] unless maximum_price.blank?
scope = scope.scoped :conditions => ["to_tsvector('english', items.catagory) ## ?", catagory] unless catagory.blank?
scope
end
end
searches_controller.rb
class SearchesController < ApplicationController
def new
#search = Search.new
end
def create
#search = Search.new(params[:search])
if #search.save
redirect_to #search, :notice => "Successfully created search."
else
render :action => 'new'
end
end
def show
#search = Search.find(params[:id])
end
end
Thanks for reading this far!
Use the data type numeric or money for exact numerical calculation without rounding errors - and sorting as a number (not as text).
Converting string literal to numeric should not be a performance problem at all.

How do I insert a record in Rails when there's no model and without using SQL

I have created a table that implements an n-to-n relation using the following statement:
create_table :capabilities_roles, :id => false do |t|
t.integer :capability_id, :null => false
t.integer :role_id, :null => false
end
There is no model for this table. How do I insert records without resorting to SQL?
I found this in the ActiveRecord::ConnectionAdapters::DatabaseStatements module:
insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
and also:
insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
I have no idea what arel means. Can someone give me an example of a valid insert? I would like to use stuff like :role_id => Role.find_by_name('Business user') in it.
If you're going to be manipulating the database records via Rails, then there should be a model for it. Just create a role.rb in your models directory with the lines
class Role < ActiveRecord::Base
end
And you're as good as gold.
It looks like a join table for has and belongs to many relationship between Capability and Role models. You should let the Rails handle it for you. First define required associations:
class Capability < ActiveRecord::Base
has_and_belongs_to_many :roles
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :capabilities
end
Then just add instance of Role model to roles array of an instance of Capability model (or vice versa):
capability.roles << role
role.capabilities << capability
Removing records from join table is done via removing object from an array:
capability.roles -= [role]
In our project we have many meta tables which don't have models. To generate active record models on the fly we use follow module:
module EntityModel
module_function
ACCESS_MODELS = {}
def for(table_name)
return ACCESS_MODELS[table_name] if ACCESS_MODELS.has_key?(table_name.id)
ACCESS_MODELS[table_name] = create_access_model(table_name)
end
def create_access_model(table_name)
Class.new(ActiveRecord::Base) do
self.table_name = table_name
end
end
end
This create anonymous models and store it in the global hash for performance purposes.
Uses as:
EntityModel.for(:users)

pulling one field of a record into an array in a Rails 3 app

Rails 3 noob here. Currently the code in my controller below is getting the whole record in my database. I am trying to populate the array with one integer, not the whole record. The integer is contained in a table "answers" and the field name is "score". How can i modify this line in my controller to get just the one field?
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id )
end
UPDATE FOR CLARIFICATION: The :score can be any integer from 0 to 5. I would like to populate #ans[s.position] with the integer.
Thanks.
You're very close
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? and user_id = ?",s.q_id,current_user.id).select(:score).first.try(:score)
end
You need to select "score" from Answer, then you need to retrieve it from the object.
Since where could potentially return many Answers, use first to pull off the first Answer, and then score to project out the score field.
answers = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id )
score = answers.first.score
All together it would be:
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id ).first.score
end
As an optimization to only retrieve score from the database, instead of answers.*, you could use select(:score).
#questions.each do |s|
#ans[s.position] = Answer.where("question_id = ? AND user_id = ?", s.q_id, current_user.id ).select(:score).first.score
end
See Active Record Query Interface for more about using select.
Just include them.
#questions = Question.includes(:answers).select(:position)
Or use this
#questions = Question.select(:position)
#questions.each{ |s| #ans[s.position] = s.answers.where(:user_id => current_user.id) }