I have a model named end. It works fine in my development environment where I use SQLite.
But in production I get an error because of PostgreSQL where end is a reserved word.
I don't want to rename the field in the model, because there are too many files to edit.
Instead, I want to declare a mapping rule so that field name in model stay "end" but name of this field in database became end_date.
How I can do it?
Your best bet, long-term, is almost certainly to suck it up and change all your Ruby code to use end_date. Obviously, that's going to be tedious because end is a Ruby keyword too, meaning Search & Replace won't Just Work; so if you really can't face it try this.
Change the name in the database, then add the following two methods to your model:
class Widget < ActiveRecord::Base
def end
end_date
end
def end=(val)
self.end_date = val
end
end
Related
I have a simple model for students but I want to also have unique student code for each student, I know I can use the student_id but I would like student code to be something like 'STU0001', 'STU0002. How can I implement something like this so that whenever I create a student, it will create the student code and add it to the DB?
Also how can I search students by their ids in this case?
You can do it using before-create hook in your model, something like follow -
class Student < ActiveRecord::Base
# Callbacks
before_create :set_student_id
def set_student_id
self.student_id = "STU" + "%04d" % self.id
end
end
output -
$> student = Student.create()
$> puts student.student_id
$> STU0001
IMHO if you want id to be prefixed as STU just for a nice display purpose then I would suggest not to store that way in database.
Let current implementation of auto incrementing id be untouched.
Just while displaying you can prepend it with STU.
For handy use you can create method to wrap this logic as
def formatted_id # OR friendly_id or whatever you like
"STU#{'%04d' % self.id}"
end
Reasons for not storing prefixed id:
You will have to write a setter
Your column data type will change to string
Your performance will go down as compared to integer column
In future if you decide to change the prefix, then you will have to update existing ids. With my solution you can change all (existing and future) ids formatting anytime by just changing formatting in the method.
Searching depends on exactly what is your usecase. If you can explain that then I can guide you better.
To search students you can write a handy method like below in model (i assume it will be Student or User)
def self.find_by_formatted_id(formatted_id)
self.find(formatted_id.gsub(/^STU/, ''))
end
def self.find_by_formatted_ids(formatted_ids) # formatted_ids will be an array
self.where(id: formatted_ids.map{ |fid| fid.gsub(/^STU/, '') })
end
I'm changing the inheritance_column value of a base model, which is extended using STI, in an existing app. How can I write a migration in order to make the existing columns conform with the new inheritance_column?
Here's my first attempt:
class MigrateStoryTypes < ActiveRecord::Migration
def self.up
Story.all.each { |story|
new_story_type = story.story_type.camelize + 'Story'
puts "changing #{story.id}'s story_type from #{story.story_type} to #{new_story_type}"
story.update_column :story_type, new_story_type
}
end
def self.down
Story.all.each { |story|
new_story_type = story.story_type.underscore.gsub /_story/, ''
puts "changing #{story.id}'s story_type from #{story.story_type} to #{new_story_type}"
story.update_column :story_type, new_story_type
}
end
end
However, this fails with:
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism
failed to locate the subclass: 'clean_slate'. This error is raised
because the column ' story_type' is reserved for storing the class in
case of inheritance. Please rename this column if you didn't intend it
to be used for storing the inheritance class or overwrite
Story.inheritance_column to use another column for that information.
Is there a straight-forward way of doing this through ActiveRecord or do I need to use a temporary column, SQL, etc.?
Using models inside migrations is generally a bad idea as the model classes assume they know what the database structure is but migrations are intended to manipulate the database structure. Your error message is just one case of the model classes not be in sync with the database. As soon as Story.all tries to instantiate a model, you get your ActiveRecord::SubclassNotFound STI exception because ActiveRecord expects to find the class name in story_type but you still have your old string types in story_type: you can't fix your database using models until your database is fixed.
I'd recommend that you pretend that your models don't exist at all in migrations, you'll have a better time if you work with the database directly. You only have two story_type values so the SQL is pretty straightforward:
def up
connection.execute(%q{
update stories
set story_type = case story_type
when 'whatever1' then 'Whatever1Story'
when 'whatever2' then 'Whatever2Story'
end
})
end
There's only two values and you know what they are so don't waste time trying to be clever.
New to Rails and Ruby and trying to do things correctly.
Here are my models. Everything works fine, but I want to do things the "right" way so to speak.
I have an import process that takes a CSV and tries to either create a new record or update an existing one.
So the process is 1.) parse csv row 2.) find or create record 3.) save record
I have this working perfectly, but the code seems like it could be improved. If ParcelType wasn't involved it would be fine, since I'm creating/retrieving a parcel FROM the Manufacturer, that foreign key is pre-populated for me. But the ParcelType isn't. Anyway to have both Type and Manufacturer pre-populated since I'm using them both in the search?
CSV row can have multiple manufacturers per row (results in 2 almost identical rows, just with diff mfr_id) so that's what the .each is about
manufacturer_id.split(";").each do |mfr_string|
mfr = Manufacturer.find_by_name(mfr_string)
# If it's a mfr we don't care about, don't put it in the db
next if mfr.nil?
# Unique parcel is defined by it's manufacturer, it's type, it's model number, and it's reference_number
parcel = mfr.parcels.of_type('FR').find_or_initialize_by_model_number_and_reference_number(attributes[:model_number], attributes[:reference_number])
parcel.assign_attributes(attributes)
# this line in particular is a bummer. if it finds a parcel and I'm updating, this line is superfulous, only necessary when it's a new parcel
parcel.parcel_type = ParcelType.find_by_code('FR')
parcel.save!
end
class Parcel < ActiveRecord::Base
belongs_to :parcel_type
belongs_to :manufacturer
def self.of_type(type)
joins(:parcel_type).where(:parcel_types => {:code => type.upcase}).readonly(false) unless type.nil?
end
end
class Manufacturer < ActiveRecord::Base
has_many :parcels
end
class ParcelType < ActiveRecord::Base
has_many :parcels
end
It sounds like the new_record? method is what you're looking for.
new_record?() public
Returns true if this object hasn’t been saved yet — that is, a record
for the object doesn’t exist yet; otherwise, returns false.
The following will only execute if the parcel object is indeed a new record:
parcel.parcel_type = ParcelType.find_by_code('FR') if parcel.new_record?
What about 'find_or_create'?
I have wanted to use this from a long time, check these links.
Usage:
http://rubyquicktips.com/post/344181578/find-or-create-an-object-in-one-command
Several attributes:
Rails find_or_create by more than one attribute?
Extra:
How can I pass multiple attributes to find_or_create_by in Rails 3?
From the pointview of rails best practices, what is the best place to manipulate form data before saving?
For instace, on a contact form, I want to make sure that all data is saved in capitalized form ( don't you hate when PEOPLE SHOUT AT YOU in their "please contact me" form submission? :-) )
is it better to do manipulation in controller? I could either do it in create, or move it into some sort of private method , that will capitalize all string attributes of the object before saving / updating?
Or
is it better do in the model before_save?
It makes sense to me that it should be done in the model since I probably want that to be the same for all records, no matter whether I manipulate on them in a rake task or through the web interface.
Bonus:
Also where would I place it if I want that that on ALL my models, with the ability to override default on a case by case basis? Application controller?
There might be some special cases where you want to save value without capitalizing - i.e. brand name products that don't capitalize (i.e. utorrent) or a last name that should have multiple caps in the name (i.e. Irish & Scottish names like McDonald)
Thank you!
the easiest place to put this is in your model. I would suggest using either before_save or even before_validation if you feel that fits better. Something like this would do the trick:
before_save :upcase_content
def upcase_content
self.content = self.content.upcase
end
Additionally if you wanted to allow for exceptions of a case by case basis you could add an attr_accessor to your model.
class MyModel < ActiveRecord::Base
attr_accessor :dont_upcase
before_save :upcase_content, :unless => :dont_upcase
...
end
then when you create a model set the accessor to true
#model = Model.new(:brand_name => utorrent)
#model.dont_upcase = true
#model.save!
The best place to put this is in your model, that way you have a fat model and a skinny controller, which is a "good thing".
If you want to have this be available for all of your models my suggestion is to use a module which contains your shared functionality and then include that in all the models you want to have the default behavior.
Ok based on the suggestions from other replies I came up with this solution:
lib/clean_strings.rb
module ActiveRecord
class Base
attr_accessor :dont_capitlize, :dont_strip
before_save :_capitalize_strings, :unless => :dont_capitlize
before_save :_strip_whitespaces, :unless => :dont_strip
def _capitalize_strings
self.attributes.each_pair do |key, value|
self[key] = value.capitalize if value.respond_to?('capitalize')
end
end
def _strip_whitespaces
self.attributes.each_pair do |key, value|
self[key] = value.strip if value.respond_to?('strip')
end
end
end
end
in environment.rb addded
require "clean_strings"
Now whenever I do
#a.dont_capitalize = true
#a.save!
it cleans it before saving according to my rules ( it will strip whitespace, but not capitalize it ). Obviously it needs more fine tuning, but i think it's a good way to define format rules for commonplace things. This way I don't need to sanitize every and each form input for things like extra whitespaces, or people who don't know where the CAPS LOCK is !!!
Thank you all for your input ( all upvoted).
Is it possible in ActiveRecord to customize/override the name of an attribute so that it does not match the column name in the database?
My specific case involves a legacy column, "revision", that I can't remove at this time. The column name conflicts with acts_as_audited. Which of course errors out the legacy code that I need until my migrations are complete.
My desired solution would be to override the attribute name for this column, and update the few areas that call it. Thus allowing the legacy column to live alongside acts_as_audited.
I haven't used acts_as_audited, but I'm assuming its implementation is overriding the accessor for that column. In that case, you should be able to just do something like this:
class ActiveRecord::Base
def self.name_column(column_name, new_name)
define_method(new_name) {read_attribute column_name}
define_method("#{new_name}=") {|value| write_attribute column_name, value}
define_method("#{new_name}?") {attribute_present? column_name}
end
end
These will directly access the column named in column_name without going through the overridden accessor.
Oh, bonus duplication-destroying metaprogramming answer:
class ActiveRecord::Base
def self.name_column(column_name, new_name)
{ '' => :read_attribute,
'=' => :write_attribute,
'?' => :attribute_present? }.each do |suffix,method|
define_method("#{new_name}#{suffix}") {|*args| send method, column_name, *args}
end
end
end
Just because I like to show how it can be done.
Create a migration to rename the column from revision to whatever-you-want.
Then you can declare an attr_accessor :revision and use it without the need to map the attribute to a database field.