What is the best way to define a fixed-length SQL column (CHAR(12) for instance) through a Rails migration ?
Why this should not handled by the model is because of the performance of char() vs varchar(), and I'd like to avoid injecting raw SQL in the database.
Edit : I know the :limit modifier, however the field is still varchar (which is bad for performance) and does not allow a minimum size.
If Rails doesn’t understand the column type, it’ll pass it straight through to the database. So if you want a char instead of varchar, just replace:
t.column :token, :string
With:
t.column :token, "char(12)"
Of course, this may or may not make your migrations non-portable to another database.
(credit to http://laurelfan.com/2010/1/26/special-mysql-types-in-rails-migrations)
def self.up
add_column("admin_users", "username", :string, :limit => 25)
end
def self.down
remove_column("admin_users", "username")
end
You can use string type with limit option in your migration file like this:
t.string :name, :limit => 12, :null => false
For a database specific type, we can now use:
t.column(:column_name, 'char(12)')
And for a complete example:
class Foo < ActiveRecord::Migration
def change
create_table :foo do |t|
t.column(:column_name, 'custom_type')
t.timestamps
end
end
end
Related
I understand what is happening on the ruby level but when a model is represented as a foreign key on its own table, as in "a comment can have many comments" are new sql tables beign created to represent those comments under the hood? I apologize if my question is unclear. Any and all answers are much appreciated.
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :content, {null: false}
t.integer :commentable_id, {null: false}
t.string :commentable_type, {null: false}
t.references :commenter, null: false
t.timestamps(null: false)
end
end
end
Firstly in your migration you can use polymorphic: true to create the polymorphic references:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :content, {null: false}
t.references :commentable, polymorphic: true, index: true
t.references :commenter, null: false
t.timestamps(null: false)
end
end
end
As you have already determined this will simply generate the :commentable_id and :commentable_type columns in your comments table that represent the polymorphic association - no special tables or other magic going on.
The commentable_type column is simply a string that stores the name of the model that the row belongs_to. The commentable_id is naturally an integer that holds the id of the model instance associated with this polymorphic association.
However, if you really want to find out whats going on under the hood just connect to your database and look at what you have.
Some useful commands to get you started (assuming you have connected to your database using psql) :
\dt (equivalent to SHOW TABLES)
\d+ tablename (equivalent to DESC tablename - where tablename is the name of the table you want information on)
Active Record uses the commentable_type column to constantize it and find the associated record by the commentable_id:
i.e.
commentable_type = "Post" # for example
commentable_type.constantize # returns the Post class model
commentable_type.constantize.find commentable_id # find the Post record
# the above line is equivalent to:
Post.find commentable_id
The problem that i have is when i run my migrations the updates is applied to the database, but rails does not do the same.
To be more accurate. I have a address model with a house number. Recetly i was told that the house number should be able to contain letters, like (35B). Therefore i would like to convert the integer colum to a column of strings. This is no problem in any case with my data, red. only integers.
The migration that i applied works as expected. It changes the type of the column in the postgres database and preserves the data content. I was using this migration.
class ConvertIntToStringOnNumber < ActiveRecord::Migration
def change
change_table :addresses do |t|
t.change :number, :string
end
end
end
Migration result with this schema.rb
create_table "addresses", :force => true do |t|
t.string "street"
t.string "number"
t.integer "zip"
t.string "floor"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
After running the migration on my heroku server i am not able to query the database using data from the form, this was no problem before. Rails is trying to look for an integer, but the database is containing strings.
Rails is trying to run this query, even though the schema.rb says something different. This is where the party stops.
SELECT "addresses".* FROM "addresses" WHERE "addresses"."street" = 'xxxx' AND "addresses"."number" = 63 AND "addresses"."floor" = '' AND "addresses"."zip" = 9000 LIMIT 1):
I have seen a lot of problems with forigen keys, but this is NOT one of those problems.
Did you restart your application after running the migration?
ActiveRecord loads information about your tables into each class when they are instantiated. See #columns for more info. Since Heroku runs your app in production mode, your classes won't be automatically reloaded on each request.
Try running heroku restart on your application - Rails should pick up the changes then.
I have an Audit class which is used to store action, by and on attributes.
class Audit < ActiveRecord::Base
attr_accessible :activity
belongs_to :by, :polymorphic => true
belongs_to :on, :polymorphic => true
attr_accessible :by, :on
end
The polymorphic association for :by and :on is used to store any kind of objects which should be audited. Mainly because polymorphic is broken down into type and id in the schema and thus should be able to store all my model objects.
create_table "audits", :force => true do |t|
t.string "activity"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "on_id"
t.string "on_type"
t.integer "by_id"
t.string "by_type"
end
The problem I'm having comes from that all of the objects being audited are also using the paranoia gem. The paranoia gem introduces a deleted_at column in each models table which is checked for every query on that model through its default_scope which is set to something like "where(deleted_at is null)". The paranoia gem also provides a method .with_deleted which allows a direct query by turning of the default_scope and as a result also return objects which have beend paranoid / soft deleted.
However if I have any items that are deleted and I try to get all my Audited items listed using.
Audit.all
I can't figure out how to tell Rails to run the lookup query for each polymorphic :by and :on object adding the .with_deleted call. My guess is that rails looks up the the object of a polymorphic relation by
eval(type).find(id)
Which in my case would give me the object with the default_scope of the paranoia gem applied.
I have tried to override self.find_by_sql in Audit but with no luck. I get stuck in some Arel methods which I need to read up on more before I can move on.
I see the following solutions but I can't figure out how to do them.
Overwrite the polymorphic lookup. How?
Get the raw SQL as string before it's evaluated and sub/gsub the Where deleted_at is null part.
Any and all suggestions on how to approach this would be greatly appreciated.
Try this:
def on!
if on_type && on_id
on_type.constantize.with_deleted.find(on_id)
end
end
This will raise an ActiveRecord::RecordNotFound error if the record has been truly deleted, but otherwise will return the "on" object even if it is marked as deleted.
I am currently trying to run this migration:
class AddDroppedProjectsCountToUser < ActiveRecord::Migration
def self.up
add_column :users, :dropped_projects, :integer, {:default=>0, :required=>true}
end
def self.down
remove_column :users, :dropped_projects
end
end
The column is added correctly, but none of the old records are populated with 0. They are nil. I have tried using default=>'0' as well, to no avail. Any idea why this might be happening? (Rails 3.0.3)
Edited to add: When I create a new user it works fine, and everything looks correct. It's just the old users that still have nil for that value in the table.
What happens if you say:
def self.up
add_column :users, :dropped_projects, :integer, :null => false, :default => 0
end
instead? Without the :null=>false you're still allowing NULL in dropped_projects so there's no reason for PostgreSQL to make them 0. Also, I don't think :required is a valid option for add_column; since the options are just a simple Hash and add_column only looks for options it knows about, your stray :required option is silently ignored.
you could do this:
(taken from http://apidock.com/rails/ActiveRecord/Migration)
Using a model after changing its table
Sometimes you’ll want to add a column in a migration and populate it
immediately after. In that case, you’ll need to make a call to
Base#reset_column_information in order to ensure that the model has
the latest column data from after the new column was added. Example:
class AddPeopleSalary < ActiveRecord::Migration
def up
add_column :people, :salary, :integer
Person.reset_column_information
Person.all.each do |p|
p.update_column :salary, SalaryCalculator.compute(p)
end
end
end
I believe this is due to the fact that you are changing the old migration, instead of creating a new.
In this case, the solution is to check the schema file (schema.rb). It does not change automatically, and add
t.integer "dropped_projects", default: 0, null: false
class CreateScrapes < ActiveRecord::Migration
def self.up
create_table :scrapes do |t|
t.text :saved_characters
t.text :sanitized_characters
t.string :href
t.timestamps
end
end
def self.down
drop_table :scrapes
end
end
I'm about to rake db:migrate and I'm think about the attribute type if I should be using text or string. Since saved_characters and sanitized_characters will be arrays with thousands of unicode values, its basically comma delimited data, I'm not sure if `:text' is really the right way to go here. What would you do?
Assuming you're on MySQL, the real difference between :string and :text is length. Rails uses the varchar column type for :string columns, and sets a 255 character limit on :string columns. :text, usuprisingly, uses the text column.
To me, this suggests that :string would be a really bad choice for your columns, as they are likely to exceed 255 characters.
:string is only 255 characters. you probably want :text since you mention thousands.