Original code looks like this:
# app/models/sso_configuration.rb
class SsoConfiguration < ActiveRecord::Base
end
# db/schema.rb
create_table "sso_configurations", force: true do |t|
...
end
I have to rename the model and add namespace so that I'll have Sso::SamlConfiguration. I changed model and database table.
# db/migrate20160225144615_rename_sso_configurations_to_sso_saml_configurations.rb
class RenameSsoConfigurationsToSsoSamlConfigurations < ActiveRecord::Migration
def change
rename_table :sso_configurations, :sso_saml_configurations
end
end
# db/schema.rb
create_table "sso_saml_configurations", force: true do |t|
...
end
# app/models/sso/saml_configuration.rb
module Sso
class SamlConfiguration < ActiveRecord::Base
end
end
When I open my rails console, the following happens.
> Sso::SamlConfiguration
=> Sso::SamlConfiguration(Table doesn't exist)
> Sso::SamlConfiguration.new
=> PG::UndefinedTable: ERROR: relation "saml_configurations" does not exist
My original thinking was that namespaced models should, by convention, have the snakecase name as the table name such that Foo::Bar should have a corresponding foo_bars table. Am I missing something with my setup?
rename_table :sso_configurations, :sso_saml_configurations
would imply this SsoSamlConfiguration.all when your trying to do this Sso::SamlConfiguration.all
Simply rollback your migration and change this line
rename_table :sso_configurations, :sso_saml_configurations
to this
rename_table :sso_configurations, :saml_configurations
and now this should work
Sso::SamlConfiguration.all
PG::UndefinedTable: ERROR: relation "saml_configurations" does not
exist
Rails by default looks for the table whose name is plural name of model i.e, in your case it looks for saml_configurations as the model name is saml_configuration.
You need to explicitly map the model to a different table by using self.table_name
# app/models/sso/saml_configuration.rb
module Sso
class SamlConfiguration < ActiveRecord::Base
self.table_name = "sso_saml_configurations"
end
end
I figured out the solution by copying what rails would do if I let it generate a namespaced model for me
rails g model sso/test
invoke active_record
create db/migrate/20160226074853_create_sso_tests.rb
create app/models/sso/test.rb
create app/models/sso.rb
invoke rspec
create spec/models/sso/test_spec.rb
invoke factory_girl
create spec/factories/sso_tests.rb
I checked all the path and name conventions in these new files and the only one I missed was the file app/models/sso.rb.
Creating the following solved my problem:
# app/models/sso.rb
module Sso
def self.table_name_prefix
'sso_'
end
end
Then
rails d model sso/test
Related
I want to create a many to many relationship between two models, and I'd like to know step by step what to do. I'd like to have a explanation on HOW to do the migrations and HOW to ideally create the models. The way i'm trying to do right now is:
I create two models in the Ruby command line:
rails g model Location name:string
rails g model Datetime date:datetime
Now I have to open the recently created models and add:
//models/location.rb
class Location < ActiveRecord::Base
has_and_belong_to_many :datetimes
end
//models/datetime.rb
class Datetime< ActiveRecord::Base
has_and_belong_to_many :locations
end
Now apparently I have to do a migration, but I don't understand what it is, and the I guess some sources for oldest versions really got me confused. Can someone please explain in details?
Obs: There are some similar questions but they do not answer my question because they don't explain in depth what to do.
As suggested by ruby tutorial, we generate a new migration:
rails g migration CreateDatetimesAndLocations
Inside this migration i have:
class CreateDatetimesAndLocations < ActiveRecord::Migration
def change
create_table :locations_datetimes, id:false do |t|
t.belongs_to :datetime, index: true
t.belongs_to :location, index: true
end
end
end
This is done exactly like the ruby tutorial. Now I have this controller, that i'm testing on and it goes like that:
class WeatherController < ApplicationController
def data
#location = Location.new
#location.name = "test"
#datetime = Datetime.new
#datetime.date = DateTime.new(2001,2,3)
#location.datetimes << #datetime // **PROBLEM ON THIS LINE**
#location.save
#location = Location.new
#location.name = "teste2"
#location.locations << #location
#locations = Location.all //(show locations in view)
end
end
The problem I was having was because the locations_datetimes have to be datetimes_locations (in alphabetic order apparently).
I have added monetize and demonetize helpers inside ActiveRecord::Migration, ActiveRecord::ConnectionAdapters::TableDefinition and ActiveRecord::ConnectionAdapters::Table by that pull request.
That file shows usage examples. So you will understand invented changes at glance. (It works)
But I have no idea how to test my helpers. What way can I write specs for them? All my attempts of writing migrations in spec files and running them manually failed. Migration manual run did not change table (or I was unable to detect changes) and did not throw any exception.
Example of my attempt:
describe 'monetize' do
class MonetizeMigration < ActiveRecord::Migration
def change
create_table :items
monetize :items, :price
end
end
class Item < ActiveRecord::Base; end
it 'should monetize items' do
MonetizeMigration.up #=> nil
Item #=> Item(has no table)
end
end
This worked for me in the console:
[4667]foo#bar:~/dev/ror/foo$ rails c
Loading development environment (Rails 3.2.9)
irb(main):001:0> class MyMigration def change
irb(main):003:2> create_table :foo
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> MyMigration.new.change
-- create_table(:foo)
(4.5ms) select sqlite_version(*)
(133.2ms) CREATE TABLE "foo" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)
-> 0.2362s
=> []
You can execute any migration helper methods right on your database connection:
ActiveRecord::Base.connection.create_table :items
Thanks to #happy_user for showing my mistake in first attempt. I think someone may use my latest solution in the future, so I'll leave it here.
I'm on Windows XP...
Ruby 1.9.2
Rails 3.0.9
MS SQL Server 2005
I'm using tiny_tds + activerecord-sqlserver-adapter
In my database I have table named t4.
I have created a model like this: rails generate model t4
Here is my simple model t4.rb:
class T4 < ActiveRecord::Base
end
Here is migration file 20111013125957_create_t4s.rb:
class CreateT4s < ActiveRecord::Migration
def self.up
create_table :t4s do |t|
t.timestamps
end
end
def self.down
drop_table :t4s
end
end
I have schema.rb:
...
create_table "t4", :force => true do |t|
t.string "title", :limit => 50
end
...
Problem:
When I try T4.select("title").where(["id = 3"]) I get error message:
ActiveRecord::StatementInvalid: TinyTds::Error: Invalid object name 't4s'.: SELECT title FROM [t4s] WHERE (id = 3)
P.S.:
I have another some tables named Adddocs and Eclaims.
There are not problems with queries to them.
I guess that problem is that T4.select("title").where(["id = 3"]) maps to SELECT title FROM [t4s] WHERE (id = 3) (T4 to t4s).
Why? I don't know
But when I edited file config/initializers/inflections.rb like this:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 't4', 't4'
end
Everything works! But it's not suitable solution (bad tone i think) :(((
By convention, if you create a model named album, rails will create a table called albums.
To override this (which isn't usually done) and use a custom defined table name you can use set_table_name method like this in your model:
class Album < ActiveRecord::Base
self.table_name="album" * define here whatever table name you have
end
When you create a new model it is the singular of the object your storing, whereas when it then generates the table, it uses the plural as there are multiple stored within the table. Example...
If you create a model named user, you'll end up with app/models/user.rb but the table it makes will be called users.
If you don't like that you have a few options...
You can override the inflections as you've done, however you should only do this if Rails is getting the pluralization wrong, rather than forcing it to use a certain word.
You can override the table name using the set_table_name method in the model file, providing it with a parameter of what your table name actually is.
You could disable the whole pluralized table names with config.active_record.pluralize_table_names = false in your config/application.rb file, however I strongly recommend you don't do this.
Ultimately I would suggest using more descriptive table names which can be pluralized over ones that can't. T4 as a model and table name don't explain to me what's stored within, where as Post does.
I want to rename a table... (any table.)
I tried this line of code:
ActiveRecord::ConnectionAdapters::SchemaStatements.rename_table(old_name, new_name)
Here's the weird thing. I know I got it working the first time, but now I get this error: undefined method `rename_table' for ActiveRecord::ConnectionAdapters::SchemaStatements:Module
Was there something I need to set?
Remember that in Rails >= 3.1 you can use the change method.
class RenameOldTableToNewTable < ActiveRecord::Migration
def change
rename_table :old_table_name, :new_table_name
end
end
You would typically do this sort of thing in a migration:
class RenameFoo < ActiveRecord::Migration
def self.up
rename_table :foo, :bar
end
def self.down
rename_table :bar, :foo
end
end
.rename_table is an instance method, not a class method, so calling Class.method isn't going to work. Instead you'll have to create an instance of the class, and call the method on the instance, like this: Class.new.method.
[EDIT]
In this instance, ActiveRecord::ConnectionAdapters::SchemaStatements isn't even a class (as pointed out by cam), which means that you can't even create an instance of it as per what I said above. And even if you used cam's example of class Foo; include ActiveRecord::ConnectionAdapters::SchemaStatements; def bar; rename_table; end; end;, it still wouldn't work as rename_table raises an exception.
On the other hand, ActiveRecord::ConnectionAdapters::MysqlAdapter is a class, and it is likely this class you'd have to use to rename your table (or SQLite or PostgreSQL, depending on what database you're using). Now, as it happens, ActiveRecord::ConnectionAdapters::MysqlAdapter is already accessible through Model.connection, so you should be completely able to do Model.connection.rename_table, using any model in your application.
[/EDIT]
However, if you wish to permanently rename a table, I would suggest using a migration to do it. It's easy and the preferred way of manipulating your database structure with Rails. Here's how to do it:
# Commandline
rails generate migration rename_my_table
# In db/migrate/[timestamp]_rename_my_table.rb:
class RenameMyTable < ActiveRecord::Migration
def self.up
rename_table :my_table, :my_new_table
end
def self.down
rename_table :my_new_table, :my_table
end
end
Then, you can run your migration with rake db:migrate (which calls the self.up method), and use rake db:rollback (which calls self.down) to undo the migration.
ActiveRecord::Migration.rename_table(:old_table_name, :new_table_name)
I'd like to know which is the preferred way to add records to a database table in a Rails Migration. I've read on Ola Bini's book (Jruby on Rails) that he does something like this:
class CreateProductCategories < ActiveRecord::Migration
#defines the AR class
class ProductType < ActiveRecord::Base; end
def self.up
#CREATE THE TABLES...
load_data
end
def self.load_data
#Use AR object to create default data
ProductType.create(:name => "type")
end
end
This is nice and clean but for some reason, doesn't work on the lasts versions of rails...
The question is, how do you populate the database with default data (like users or something)?
Thanks!
The Rails API documentation for migrations shows a simpler way to achieve this.
http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
class CreateProductCategories < ActiveRecord::Migration
def self.up
create_table "product_categories" do |t|
t.string name
# etc.
end
# Now populate the category list with default data
ProductCategory.create :name => 'Books', ...
ProductCategory.create :name => 'Games', ... # Etc.
# The "down" method takes care of the data because it
# drops the whole table.
end
def self.down
drop_table "product_categories"
end
end
Tested on Rails 2.3.0, but this should work for many earlier versions too.
You could use fixtures for that. It means having a yaml file somewhere with the data you want to insert.
Here is a changeset I committed for this in one of my app:
db/migrate/004_load_profiles.rb
require 'active_record/fixtures'
class LoadProfiles < ActiveRecord::Migration
def self.up
down()
directory = File.join(File.dirname(__FILE__), "init_data")
Fixtures.create_fixtures(directory, "profiles")
end
def self.down
Profile.delete_all
end
end
db/migrate/init_data/profiles.yaml
admin:
name: Admin
value: 1
normal:
name: Normal user
value: 2
You could also define in your seeds.rb file, for instance:
Grid.create :ref_code => 'one' , :name => 'Grade Única'
and after run:
rake db:seed
your migrations have access to all your models, so you shouldn't be creating a class inside the migration.
I am using the latest rails, and I can confirm that the example you posted definitely OUGHT to work.
However, migrations are a special beast. As long as you are clear, I don't see anything wrong with an ActiveRecord::Base.connection.execute("INSERT INTO product_types (name) VALUES ('type1'), ('type2')").
The advantage to this is, you can easily generate it by using some kind of GUI or web front-end to populate your starting data, and then doing a mysqldump -uroot database_name.product_types.
Whatever makes things easiest for the kind of person who's going to be executing your migrations and maintaining the product.
You should really not use
ProductType.create
in your migrations.
I have done similar but in the long run they are not guaranteed to work.
When you run the migration the model class you are using is the one at the time you run the migration, not the one at the time you created the migration. You will have to be sure you never change your model in such a way to stop you migration from running.
You are much better off running SQL for example:
[{name: 'Type', ..}, .. ].each do |type|
execute("INSERT INTO product_types (name) VALUES ('#{type[:name]} .. )
end