id field without autoincrement option in migration - ruby-on-rails

I have a DB migration like so:
class CreateParticipations < ActiveRecord::Migration
def self.up
create_table(:participations, :primary_key => 'Seat') do |t|
t.integer :Seat
t.string :Nickname
t.string :Clan
t.string :FirstName
t.string :LastName
t.string :Email
t.boolean :Payed
t.timestamps
end
end
def self.down
drop_table :participations
end
end
Now, seat is created with an Auto increment. However, I do not want that. I want it without an auto increment. I will define Seat myself in my Logic.
I have been looking around but I cannot find how to disable auto_increment.
How do I do this? Except for manually doing it in MySQL.

For the record, if you absolutely need to do this (it shouldn't happen often), here's the way to do a non-autoincrementing primary key with the Rails migration DSL:
create_table(:table_name, :id => false) do |t|
t.integer :id, :options => 'PRIMARY KEY'
end
That will work for MySQL anyway, if your DB uses different syntax to specify a primary key, replace the :options => 'PRIMARY KEY' with whatever that is.

This question is 3 years old, but incase anyone is wondering 3 years later, like I was, all you do is "change_column" in the event the table is already created:
change_column(:table_name, :id, :integer, :null => false)
This should work in Rails 2.x and 3.x.
O

Not saying its a good idea, but here's how I did it for SQLite3 - just replace that SQLiteAdapter with your DB's adapter - you might do this cleaner/short with a call to define_method
class AbstractAdapter
end
module ActiveRecord
module ConnectionAdapters
class SQLiteAdapter < AbstractAdapter
def supports_autoincrement?
false
end
end
end
end
<then you migration>
or
class SomeMigration < ActiveRecord::Migration
def change
create_table :table do |t|
ActiveRecord::ConnectionAdapters::SQLiteAdapter.send :define_method, :supports_autoincrement? do false end
t.integer etc
end
end
end
Of course just change the adapter for other db's

Is there a reason you can't use rails' id key and manually add an index called Seat?
I've seen some hacks just to get rails to -work- on non-increment-pk databases. I don't think it's an option. If I recall, that's how rails accesses all its per-row functionality.
Honestly, how -absolutely- do you need that slight boost in efficiency of ignoring rails' structure?
I think the real answer is "you can't." Activerecord has a few things it will not bend on.

Related

Rails - DB Level Validation for `:start_date` and `:end_date`

I have a question related to the following code in Rails:
class CreateEmployments < ActiveRecord::Migration[6.0]
def change
create_table :employments do |t|
t.string :title, null: false
t.string :company_name
t.datetime :start_date
t.datetime :end_date
t.integer :user_id
t.timestamps
end
end
end
I'm trying to disallow the db to accept any :start_date value greater than :end_date . I want :end_date to be always greater than :start_date and want to do it at the db level. Is there a way to do it?
I know I can use model field validations, but I want to implement it at db level too. Any tips?
Thanks in advance!
Rules on DB level are called constraints.
class AddEmploymentsDateConstraint < ActiveRecord::Migration
def self.up
execute "ALTER TABLE employments ADD CONSTRAINT employments_date_check CHECK (end_date > start_date)"
end
def self.down
execute "ALTER TABLE employments DROP CONSTRAINT employments_date_check"
end
end
It is important to know what DB is used. For instance, if it is used SQLite, can not use ALTER TABLE syntax to add constraint - only on CREATE TABLE can add constraint. See answer.
Additionaly, rails 6.1+ supports constraints, see another answer.

Why are the methods up and down not being created with my subclass of ActiveRecord::Migration?

I'm reading Rails 3 in Action and following the commands verbatim. However, when I run the commands
rails new things_i_bought
cd things_i_bought
bundle install
rails generate scaffold purchase name:string cost:float
The book says I should get this code:
class CreatePurchases < ActiveRecord::Migration
def self.up #not created in my code
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
def self.down # not created in my code
drop_table :purchases
end
end
I get this code instead:
class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
end
Why are the class methods up and down not being created for me? I'm using
rails 3.1.1 and ruby 1.9.2.
thanks for reading my book!
As JacobM and dbalatero have already explained, this is a new feature in Rails 3.1. This particular feature was added by Aaron Patterson as a way to simplify the migration syntax. In earlier versions of Rails, you would have to do as the book shows:
class CreatePurchases < ActiveRecord::Migration
def self.up
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
def self.down
drop_table :purchases
end
end
But that's kind of repeating yourself. Aaron created a migration syntax that looks good and is simpler, calling only the methods necessary for migrating forward, but also allowing the migrations backwards (known as a "rollback") too. The same migration written with the Rails 3.1 syntax is this:
class CreatePurchases < ActiveRecord::Migration
def change
create_table :purchases do |t|
t.string :name
t.float :cost
t.timestamps
end
end
end
So when this migration runs "forwards", Rails will create the purchases table with the fields. When you roll it back (or run it "backwards") then Rails will know to drop the table.
This syntax isn't entirely perfect however, and you'll run into problems with methods such as change_column. When that happens, it's best to stick with defining both the def up and def down methods in the migrations:
class CreatePurchases < ActiveRecord::Migration
def up
change_column :purchases, :cost, :integer
end
def down
change_column :purchases, :cost, :float
end
end
That's because in this example Rails won't know how to switch it back to the previous type. I hope this explains it better!
This is a new feature in Rails 3.1. For changes that Rails can figure out how to reverse, such as creating a table, you simply create a "change" method with the code that would have gone in "up", and it figures out how to do "down" on it's own.
You can also define "up" and "down" methods yourself -- for some changes (such as dropping a column) Rails won't be able to figure it out -- but the syntax is a bit different; it's not just def up instead of def self.up (they're now instance methods instead of class methods).
I believe in the new Rails 3.1, the database migration methods are self-aware about how to run an up/down migration.
Therefore, if you define a def change method, it will try to use those self-aware methods: in this case, create_table knows to do DROP TABLE in a down context, and CREATE TABLE in an up context.
If you want the old style, you can probably keep using it and define your own self.down and self.up methods as the book describes.
Edit: here's a link to the blog post on this, called "Reversible Migrations": http://www.edgerails.info/articles/what-s-new-in-edge-rails/2011/05/06/reversible-migrations/index.html

how to write this query of many-to-many base in rails

Hey guys
I'm new to rails, There's a lot of eye-opener for me, and I write some code and it seems no efficient, I paste my code below, could you help me find a better way to write this.
videos table:
class CreateVideos < ActiveRecord::Migration
def self.up
create_table :videos do |t|
t.string :title
t.string :desc
t.string :tudou
t.string :otherurl
t.timestamps
end
end
def self.down
drop_table :videos
end
end
drummers table:
class CreateDrummers < ActiveRecord::Migration
def self.up
create_table :drummers do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.string :nick_name
t.boolean :gender
t.timestamps
end
end
def self.down
drop_table :drummers
end
end
and I set them to simple many-to-many association
class CreateDrummersVideosJoin < ActiveRecord::Migration
def self.up
create_table :drummers_videos, :id => false do |t|
t.integer "drummer_id"
t.integer "video_id"
end
end
def self.down
drop_table :drummers_videos
end
end
I want to find all the title of drummer first name is "Jojo" last name is "Mayer"'s video
my code:
title = Drummer.where(:first_name => "Jojo", :last_name => "Mayer").first.videos.each {|t| t.title}
This return all the column's data, not the only the title I want
and since the there's only one result return named "Jojo Mayer", But the return value is activeRelation, I can't call videos, so my work around is using :first to get the video instance in order to call the videos. I know it's definitely not the way doing it
any suggestion?
You need a join table in between them like you infer. But that join table in Rails does not have to be created in a migration. It can be done exclusively in the models.
#drummer.rb
belongs_to :drummer_videos, :polymorphic => true
has_many :videos, :as => :drummer_videos
#video.rb
belongs_to :drummer_videos, :polymorphic => true
has_many :drummers, :as => :drummer_videos
Make sure that the drummer TABLE and video TABLE have a drummer_videos_id attribute.
Then you can call your Drummer..
Drummer.where(:first_name => "Jojo", :last_name => "Mayer").videos.each {|t| t.title}
First off, if you want to get only the title attribute from the videos you should use map or collect instead of each, so something like this:
Drummer.where(...).first.videos.map{ |t| t.title }
or even shorter:
.map(&:title)
Second, it seems to me that if you really want to get all the videos from a single Drummer object, than using first in some way or another, like you do, is a pretty good option.
Otherwise, if you want to get all videos from different Drummers according to a certain criteria, then you should probably call Video directly and then join or include the Drummer. Perhaps like this:
Video.joins(:drummers).where("drummers.first_name = 'jojo' AND drummers.last_name = 'Mayer'").map(&:title)

How do I add some inserts in rails migration?

After creating a table (by migration), I want to insert some entries directly. How must I write a migration for this?
thanks
Don't. If you're looking for seed data, you should use db/seeds.rb and rake db:seed instead. More info in this Railscast.
Side note: Always make sure that the code in db/seeds.rb is idempotent. i.e. It should always be safe to re-run your seeds.
But, if you must insert or modify data inside a migration (there are legitimate use-cases for this), it's best to use SQL statements instead. Your model class isn't guaranteed to still be around in the same form in a future version of your application, and running the migrations from scratch in the future might yield errors if you reference the model class directly.
execute "insert into system_settings (name, label, value) values ('notice', 'Use notice?', 1)"
Update:
This is the right answer: https://stackoverflow.com/a/2667747/7852
Here's an example from ruby on rails api:
class AddSystemSettings < ActiveRecord::Migration
# create the table
def self.up
create_table :system_settings do |t|
t.string :name
t.string :label
t.text :value
t.string :type
t.integer :position
end
# populate the table
SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
end
def self.down
drop_table :system_settings
end
end
Edit: PLEASE NOTE - Posters above are correct, you should not populate the DB inside migrations. Don't use this to add new data, only to modify data as part of changing the schema.
For many things, using raw SQL will be preferable, but if you need to insert data as part of a migration (for instance, doing data conversion when breaking out a table into multiple tables), and you want some default AR stuff like convenient DB-independent escaping, you can define a local version of the model class:
class MyMigrationSucksALittle < ActiveRecord::Migration
class MyModel < ActiveRecord::Base
# empty guard class, guaranteed to have basic AR behavior
end
### My Migration Stuff Here
### ...
end
Note that this works best for simple cases; since the new class is in a different namespace (MyMigrationSucksALittle::MyModel), polymorphic associations declared in the guard model won't work correctly.
A somewhat more detailed overview of available options is located here: http://railsguides.net/2014/01/30/change-data-in-migrations-like-a-boss/
create a new migration file like
047_add_rows_in_system_settings.rb
class AddRowsInAddSystemSettings < ActiveRecord::Migration
def self.up
SystemSetting.create{:name => "name1", :label => "Use notice?", :value => 1}
SystemSetting.create{:name => "name2", :label => "Use notice?", :value => 2}
end
def self.down
SystemSetting.delete_all
end
end
OR
while creating table
046_system_settings.rb
class AddSystemSettings < ActiveRecord::Migration
def self.up
create_table :system_settings do |t|
t.string :name
t.string :label
t.text :value
t.string :type
t.integer :position
end
SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
end
def self.down
drop_table :system_settings
end
end
Ref:- http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

Rails Migration

My migration is as follows:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.string :email
t.string :password
t.string :name
t.boolean :male
t.boolean :admin
t.timestamps
end
end
def self.down
drop_table :users
end
end
When I go to script/console and type "User", Rails does not recognize the class.
Did you run script/generate model User ... or script/generate migration CreateUser...?
If you do not generate the model, it won't be available in the console, as Rails doesn't know it exists.
Rails also does not create a modelname_id field, it simply creates an id field which autoincrements.
I hope this helps.
1) The migration will create an auto-incrementing "id" column. (I've never seen a migration create a class_id column unless it was specified).
2) You will need to declare this class in a app/model/user.rb file
class User < ActiveRecord::Base
#class methods go here
end
More importantly I want to recommend the restful_authentication plugin. It's the community standard for user authentication (meaning it's battle tested, regularly updated, and conforms to most use cases).

Resources