What does "null: false" really do? - sql

I recently created a rails migration to add a Username to my devise Users.
add_column :users, :username, :string
I created a test account and signed up without entering a username. The registration went through and created a user with no user name. I had to fix this since I need a username to set the profile url. So I updated my migration to:
add_column :users, :username, :string, null: false
I thought this would prevent a user from creating a account with a null username - didn't work! I was still about to register with no username.
Eventually adding...
validates_presence_of :username
...to my user.rb model fixed the issue. But why didn't null:false stop it? Why should I keep it and how does it work?

This statement:
add_column :users, :username, :string, null: false
translates exactly to SQL DDL (Data Definition Language) standard:
ALTER TABLE users ADD username VARCHAR(255) NOT NULL;
The trailing NOT NULL is effect of null: false. You can use other column modifiers which some of them are directly translated into DDL.
Remember that if you already apply the migration to the database the further modifications should be done with a new migration file. Updates to migration Ruby files are not propagated to database schema when the migration was already applied. Eventually you can rollback last migrations and rerun them but then you may loose existing data during the schema rollback. See Changing Existing Migrations

Related

Devise allow email uniqueness within scope

I'm in the process of upgrading a large project from rails 2 to rails 3, as a part of that upgrade I'm replacing a very old restful_athentication with devise.
The problem I'm having is that in the existing users table emails are validated like this.
validates_uniqueness_of :email, :scope => :account_id # No dupes within account
So if I add the index from the migration to add devise to users it WILL fail.
Is there a way that I can use
add_index :users, [:email,:account_id]
And have devise work properly?
I managed to get this working on my own, I added the following to config/initializer/devise.rb
config.authentication_keys = [ :email , :account_id]

RoR: how can I add columns to my database on my live heroku app?

I am trying to add :price, :location, and :product to the columns for my microposts table. I have already done a bunch of other migrations and I have heard that rolling back all of migrations and redoing them is error prone. So I guess the other option is the schema file? I have heard that the schema file is just to be read and not edited. I have been looking at http://guides.rubyonrails.org/migrations.html but can't find the right info. They briefly talk about change_table which I think could be useful but it doesn't go into depth. Is this what I am looking for?
Just create a new standalone migration:
rails g migration add_price_location_and_product_to_microposts
It will create a file in the db/migrate folder, edit it:
def change
add_column :microposts, :price, :float # dont forget to change the type to the columns
add_column :microposts, :location, :string
add_column :microposts, :product, :integer
end
(You can define the change method, instead of up and down because add_column is a reversible command.)
And then, run rake db:migrate

how to set default value to column in rails while creating migration

I am new to Model in rails. I know how to create model & how to add column to them. Now I want to set default value to a column but I am not getting that how exactly I can do it.
I generated new model
rails g model User
then added column to it
rails generate migration AddNotificationEmailToUsers notification_email:boolean
Now I want to set value of Notification column default as true.
Please guide me how to write the migration for the same. Thank you!!!
You can't do this from the command line - you'll have to edit the migration file and change the corresponding line to something like
add_column :users, :notification_email, :boolean, :default => true
Best approach here is to use change_column in your migration. It is advertised to change type but you can use it to attach a default to existing column.
I had
location :integer
in schema and I wanted to default to zero, so I wrote a migration as such:
change_column :player_states, :location, :integer, :default => 0
That did the trick.
Frederick Cheung is correct you will need to edit the migration file for this.
Just a minor update add comma after the data type before specifying the default value.
add_column :users, :notification_email, :boolean, :default => true
As of now there is no way around to specify default value defined through terminal in rails migration.
you can execute below steps in order to specify default value for a column
1). Execute
$ rails generate migration AddNotificationEmailToUsers notification_email:boolean
2). Specify the new column default value to TRUE/FALSE by editing the new migration file created.
class AddNotificationEmailToUsers < ActiveRecord::Migration
def change
add_column :users, :notification_email, :boolean, default: true
end
end
3).Run above generated migration by Executing.
$ rake db:migrate

Rails 3 validate uniqueness ignores default scope on model

I'm using the permanent_records gem in my rails 3.0.10 app, to prevent hard deletes and it seems rails is ignoring my default scope in checking uniqueness
# user.rb
class User < AR::Base
default_scope where(:deleted_at => nil)
validates_uniqueness_of :email # done by devise
end
in my rails console trying to find a user by email that has been deleted results in null, but when signing up for a new account with a deleted email address results in a validation error on the email field.
This is also the case for another model in my app
# group.rb
class Group < AR::Base
default_scope where(:deleted_at => nil)
validates_uniqueness_of :class_name
end
and that is the same case as before, deleting a group then trying to find it by class name results in nil, however when I try to create a group with a known deleted class name it fails validation.
Does anyone know if I am doing something wrong or should I just write custom validators for this behavior?
Try scoping the uniqueness check with deleted_at
validates_uniqueness_of : email, :scope => :deleted_at
This can allow two records with the same email value as long as deleted_at field is different for both. As long as deleted at is populated with the correct timestamp, which I guess permanent_records gem does, this should work.

model missing required attr_accessor for 'photo_file_name' when uploading with paperclip and S3 on heroku

Setting up paperclip with S3 in my linux dev environment was a snap -- everything works out of the box. However, I can't get it to work on Heroku.
When I try to do an upload, the log shows:
Processing ItemsController#create (for 72.177.97.9 at 2010-08-26 16:35:14) [POST]
Parameters: {"commit"=>"Create", "authenticity_token"=>"0Hy3qvQBHE1gvFVaq32HMy2ZIopelV0BHbrSeHkO1Qw=", "item"=>{"photo"=>#<File:/home/slugs/270862_4aa601b_4b6f/mnt/tmp/RackMultipart20100826-6286-1256pvc-0>, "price"=>"342", "name"=>"a new item", "description"=>"a new item", "sold"=>"0"}}
Paperclip::PaperclipError (Item model missing required attr_accessor for 'photo_file_name'):
I found one blog post that referenced this error, and it said to add this to my model:
attr_accessor :photo_file_name
attr_accessor :photo_content_type
attr_accessor :photo_file_size
attr_accessor :photo_updated_at
That does indeed make the model missing required attr_accessor for 'photo_file_name' error go away, but it still doesn't work. See my other question for details. As I have figured out that with the attr_accessor lines added to my model the uploads fail even on my dev system, I suspect that is not the right answer.
Found the problem: needed to update the database.
heroku run rake:db:migrate
heroku restart
I had done what I thought would have accomplished the same thing already:
heroku rake db:schema:load
but perhaps that doesn't work or something went wrong in the process.
Error like this occurs if you create wrong column type in migration. When you define new table migration for paperclip, you need to specify t.attachment :name insted of t.string :name. Or add_attachment :table, :name when you add new paperclip column in existed table. And now you don't need to add these attributes in attr_accessor in model.
Well, this message seems to be because the columns it's missing. Try create a migration creating the columns:
class AddPhotoToEvent < ActiveRecord::Migration
def change
add_column :events, :photo_file_name, :string
add_column :events, :photo_content_type, :string
add_column :events, :photo_file_size, :integer
add_column :events, :photo_updated_at, :datetime
end
end
This work for me, here i have a table events with photo