Rails 3 - Column Names (Legacy Database) [duplicate] - mysql

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Alias for column names in Rails
I'm writing a module of ActiveRecord models to access a MySQL legacy database. My problem is that the database has a column naming convention that looks like this: groupID, metaGroupName, flagName. Is there an easy way to make the models play nice with this naming convention?
Update:
I'd like to avoid the alias_attribute method if possible. It's not a small database, and that would be a lot of columns to alias. Is there a method in ActiveRecord that interprets column names that I can override, perhaps?

You don't want to keep repeating yourself—that's good. Fortunately, Ruby makes it rather easy. Try something like the following:
class Model < ActiveRecord::Base
column_names.each do |cn|
alias_attribute cn.underscore.intern, cn
end
end
I'd then factor that out into a Module maybe like so:
module AliasLegacyColumns
extend ActiveSupport::Concern
included do
column_names.each {|cn|
alias_attribute cn.underscore.intern, cn
}
end
end
Then just include that in your models as necessary. For further refinement I would provide an alias_columns class-level method that accepts parameters such as :include or :exclude so you can monkey-patch ActiveRecord::Base with said module but only call alias_columns as needed per model.

Just add the following to your model
alias_attribute :new_column_name, :myLegacyColumnName

Related

Model with namespace - wrong table name (without namespace)

I found a problem in one of legacy applications (outdated rails-3.0.20).
This application has lots of components and nested models. Problem existed only on one of production servers (same environments like other productions and mine dev).
There was model with name space which looks like
module Great
class Item
end
end
Table name was named great_items.
When i debug/open it on server with fault i've found that calculated table name was items istead of great_items.
$ Great::Item.all
#=> ActiveRecord::StatementInvalid: No attribute named `name` exists for table `items`
So i thought mby there is simmilar class with same namespace, I've checked it and it wasn't. My 2nd thought was to set table name explicit i tried
self.table_name = 'great_items'
# &
set_table_name 'great_items'
After this changes i run rails c and table name was setted fine:
$ Great::Item.table_name
#=> 'great_items'
But when i tried to obtain some items there was a freak error, which i could not understand till now!
$ Great::Item.all
#=> ActiveRecord::StatementInvalid: Mysql2::Error: Table 'db.items' doesn't exist: SELECT `great_items`.* FROM `items` WHERE `great_items`.`some_default_scope` = 0
As you can see in above example table has correct name for select values and in where statement, but in from value is incorrect.
I was curious so I've checked ActiveRecord::Base mysql adapter and there was some kind of catching table name so i tried to reset_table_name. Reset helped to setup expected name('great_items') but above errors didn't missed.
Problem dissapeared when i turn of class catching in production enviroment - but it wasn't solution.
Finally I kinda 'solved' this using reset_column_information after set_table_name, but i think it isn't good solution either.
My question is do you know what really could cause this issue and how to solve it without reloading class cache?
Assumed table names dont take into account the modules, as you noticed.
But as you already know, you can set it yourself, using:
self.table_name = 'great_items'
According to this doc, because you use 3.0.x, you have to use:
set_table_name "great_items"
This must be put on top of your class definition
Try this
class RenameOldTableToNewTable< ActiveRecord::Migration
def self.up
rename_table :old_table_name, :new_table_name
end
def self.down
rename_table :new_table_name, :old_table_name
end
end

Changing FROM in all queries for an ActiveRecord model

I'm working on a rails project that is connected to a third-party MySQL database that I cannot change the schema for. So far, I've been able to shoe-horn everything into rails and make it play nice, but I've come across an interesting problem.
I have a table, we'll call it foos. I have an ActiveRecord model called Foo that uses this table. The problem is that this table represents two similar but distinct types of record. We'll call them Foo type A and Foo type B. To get around this, I've created two classes, FooTypeA and FooTypeB that inherit from Foo and have default scopes so that they only contain records of their respective types.
My code looks something like this:
class Foo < ActiveRecord::Base
# methods common to both types
end
class FooTypeA < Foo
default_scope -> { where is_type_a: true }
# methods for type A
end
class FooTypeB < Foo
default_scope -> { where is_type_a: false }
# methods for type B
end
For the most part, this works pretty well, except for the fact that sometimes an association chain joins over both of these models. Since they come from the same table, this causes ambiguity problems, and generates exploding SQL queries. I've been writing custom join queries to get around this, but it's quickly becoming cumbersome.
I know I can change the default table name for a model with the self.table_name value, but is there a way that I can tell rails to change the FROM portion of the SQL query for a model so that I can make all queries from FooTypeA read as: SELECT foo_as.* FROM foos AS foo_as ...
I'm open to other suggestions, but this seems like the easiest solution if it's possible.
Wouldn't the ActiveRecord .from method solve your problem?
You could also create two views (depending on mysql version) and use those for table sources but unless you only read from the tables, you can get into writable view issues which I would try and avoid.

Rails - Model Validations doesn't apply on mysql insert/update command

For the reason, I've used mysql cmd insert into table_name (....) update custom_reports ...and hence I miss out on Model validations
validates_uniqueness_of :name
validates_presence_of :name, :description
How to validate now in rails way? Or, use the mysql way to validate(needs help in this way too)?
Rails validation and other ActiveRecord and ActiveModel magic don't work if you only execute custom SQL command. None of your model classes is even instantized then.
For Mysql (or any sql like DB), you can modify the column attribute to:
Unique (this would validate uniqueness)
Not null (this would validate presence)
I know doing the above with OCI8 and oracle would result in exceptions which I am guessing should be same with ActiveRecord and Mysql, so you should be handling your exceptions correctly
But as #Marek as said you should be relying on Active record and be doing things like
Model.create()
OR
model_instance.save()
If you want to find (and perhaps handle) the entries in your db that are not valid, try the following in the rails console:
ModelName.find_each do |item|
unless item.valid?
puts "Item ##{item.id} is invalid"
# code to fix the problem...
end
end
valid? runs the Validations again, but does not alter the data.

Add a column to a table

how to add a column to my table Users.
because I ran the migration, I have to do something like:
rails generate migration AddShowmsgColumnToUsers show_msg:boolean
and then:
rake db:migrate
but I'm not sure about "AddShowmsgColumnToUsers". how can I know how it suppose to be? why not: AddShow_msgColumnToUsers? if the problem was pluralization and singularization, I can run the rails console and check that, but how can I know about the uppercase letter: ShowMsg/Show_msg/Show_Msg/Showmsg? is there a command that helps me to check it?
In answer to your first question, it doesn't matter, as long as the table name is correct - Rails uses the arguments you specify for the columns rather than the name of the migration.
Also, you should only really be asking one question at a time... ;-)
If you generate a migration to add an column, you should use either camelcase or underscores. Besides you dont have to put "Column" inside your migration generator, with Add...To... the migration already knows you are adding a column.
So either:
rails generate migration AddShowMsgToUsers show_msg:boolean
or:
rails generate migration add_show_msg_to_users show_msg:boolean
Is the way to go. The migration-generator will result in the following migration:
class AddShowMsgToUsers < ActiveRecord::Migration
def change
add_column :users, :show_msg, :boolean
end
end
Of course you could also do it all manually, but the whole point of generators is that you don't need to write everything yourself.

Rails database query - is it possible to give a column an alias?

I've got to produce a json feed for an old mobile phone app and some of the labels need to be different from my database column names.
I think the most efficient way of doing this would be to do a create an alias at the database level. So I'm doing things like
Site.where( mobile_visible: true ).select("non_clashing_id AS clientID")
which produces the SQL
SELECT non_clashing_id AS clientID FROM `sites` WHERE `sites`.`mobile_visible` = 1 ORDER BY site_name
If I run this query in MYSQL workbench it produces a column with the heading ClientID as I expect, with the required values.
But if I show the object in a rails view I get {"clientID":null},{"clientID":null},{"clientID":null}
What am I doing wrong? Is there a better way of doing this?
This shows how to access the variable
sites = Site.where( mobile_visible: true ).select("non_clashing_id AS clientID")
sites.each do |site|
puts site.clientID
end
I think by default, activerecord loads column definitions from the database. And, it should load value into existing columns only.
Site.columns
I guess you could add one more item to that array. Or you could use the normal query without alias column name, then add alias_attribute like MurifoX did and overwrite as_json method:
class Site < ActiveRecord::Base
alias_attribute :client_id, :non_clashing_id
def as_json(options={})
options[:methods] = [:client_id]
options[:only] = [:client_id]
super
end
end
Try putting this in your model in addition to the database alias:
class model < ActiveRecord::Base
alias_attribute :non_clashing_id, :client_id
...
end