How to make a model's column optional in rails5 - ruby-on-rails

I created a model(post) having two parents Community and User.
Now i want that It could be possible that community_id field may or maynot be filled so i run a migration stating change_column_null and change_column_default for community_id 's null to be allowed and default value to be zero
Now error is that community should exist so should i create a community of id=0 in rails console,is it a good practice?
or Am I doing something wrong in following code below,in allowing null true???
Post.rb(model)
class Post < ApplicationRecord
belongs_to :user
belongs_to :community
end
migration:
class Postcommunityidallownull < ActiveRecord::Migration[5.1]
def change
change_column_null :posts, :community_id, true
change_column_default :posts,:community_id, 0
end
end

When you want an optional belongs_to:
1) Just don't add in the field's migration
null: false
2) In the model use
belongs_to :community, optional: true

Related

Is it advisable to use :foreign_key in my migrations rather than just adding user_id?

I am using rails 4.2, I just want to know if there would be any difference if I use the :foreign_key keyword in my migrations rather than just adding a user_id column to add relationship to my models ?
YES
The key difference is not on the application layer but on the database layer - foreign keys are used to make the database enforce referential integrity.
Lets look at an example:
class User < ActiveRecord::Base
has_many :things
end
class Thing < ActiveRecord::Base
belongs_to :user
end
If we declare things.user_id without a foreign key:
class CreateThings < ActiveRecord::Migration
def change
create_table :things do |t|
t.integer :user_id
t.timestamps null: false
end
end
end
ActiveRecord will happily allow us to orphan rows on the things table:
user = User.create(name: 'Max')
thing = user.things.create
user.destroy
thing.user.name # Boom! - 'undefined method :name for NilClass'
While if we had a foreign key the database would not allow us to destroy user since it leaves an orphaned record.
class CreateThings < ActiveRecord::Migration
def change
create_table :things do |t|
t.belongs_to :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
user = User.create(name: 'Max')
thing = user.things.create
user.destroy # DB says hell no
While you can simply regulate this with callbacks having the DB enforce referential integrity is usually a good idea.
# using a callback to remove associated records first
class User < ActiveRecord::Base
has_many :things, dependent: :destroy
end

'NOT NULL constraint failed' after generating Scaffold

I am running into an error after generating a new scaffold for a table with an existing model: NOT NULL constraint failed: questions.question_text.
This has been covered before, but I have not seen an answer for when there is a set null value, as I have done below.
First, I had already generated a model/migration for this table entitled Questions, which looks like:
..._create_questions.rb
class CreateQuestions < ActiveRecord::Migration
def change
create_table :questions do |t|
t.belongs_to :category, index: true
t.string :question_text, :null => false
t.timestamps
end
end
end
Notice here that I am specifying the null => false. To save some time, I ran a Scaffold command to allow me to enter data into Questions easily:
rails generate scaffold Questions --skip
After restarting the server, I am running into the error above. Since I am directly addressing the null value, I am unclear of why it triggers an error when I reach the block in QuestionsController#create (in other words, when I try to create a Question).
In case it helps, here is my Question.rb model as well:
class Question < ActiveRecord::Base
has_many :quiz_questions
has_many :quizzes, through: :quiz_questions
has_many :answers
belongs_to :category
accepts_nested_attributes_for :answers
end
What am I doing wrong?
If you are using not-null as a form of validation than you would want to add a model validation which enforces this rule as well:
class Question < ActiveRecord::Base
# ...
validates_presence_of :question_text
end
This will prevent database driver level exceptions and provide user feedback.
Since you ran the scaffold generator without any attributes I'm guessing that the params whitelist might be empty as well which would cause the above validation to fail since the input is never actually passed to the initializer.
class QuestionsController < ApplicationController
def create
#question = Question.create(question_params)
# ...
end
private
def question_params
params.require(:question)
.permit(
:category_id,
:question_text
answers_attributes: [:foo, :bar, :baz]
)
end
end

after adding associations and index do I need to explicitly use foreign_key keyword to make a column foreign key in ruby-on-rails app

I need to need to make a column as foreign key.
I did lot of research on it.
I understood that I need to add associations. I am clear on belongs_to , has_one and has_many options.
After creating associations rails know that there is a foreign key association.If I remove the main record then dependent record will be deleted by rails app.
I was reading on migrations and I came across http://edgeguides.rubyonrails.org/active_record_migrations.html
where it is mentioned:
$ bin/rails generate migration AddUserRefToProducts user:references
will generate:
class AddUserRefToProducts < ActiveRecord::Migration
def change
add_reference :products, :user, index: true, foreign_key: true
end
end
Now on website: http://guides.rubyonrails.org/active_record_migrations.html
where it is mentioned:
$ bin/rails generate migration AddUserRefToProducts user:references
will generate:
class AddUserRefToProducts < ActiveRecord::Migration
def change
add_reference :products, :user, index: true
end
end
I understand the creation of index.
Do I need to have foreign_key: true explicitly or not? what difference will it make?
If you specify the foreign key in your migration, Rails will add a foreign key constraint at the database level. If you leave it out, no constraint will be added and it will behave like all previous versions of Rails e.g. you can delete records with the risk of orphaned records being left behind.
You can do something like
class AddUserRefToProducts < ActiveRecord::Migration
def change
add_reference :products, :user, index: true
add_foreign_key :products, :user
end
end
Similar solution here
First of all according to my knowledge http://edgeguides.rubyonrails.org/active_record_migrations.html is yet to be released. so the change foreign_key: true in your migration is not applicable yet so it wont make difference.
See this http://edgeguides.rubyonrails.org/index.html.
For stable versions, use http://guides.rubyonrails.org/
Now in http://guides.rubyonrails.org/active_record_migrations.html there is no option like foreign_key: true.
Even if you pass it, it wont make difference because according to the method add_reference
http://apidock.com/rails/v4.0.2/ActiveRecord/ConnectionAdapters/SchemaStatements/add_reference
It is not expecting foreign_key to be as option.
And lastly add_reference is basically calling http://apidock.com/rails/v4.0.2/ActiveRecord/ConnectionAdapters/SchemaStatements/add_column add_column method in it, which is also not expecting foreign_key to be passed as parameter, so it is not at all necessary.
Hope it makes sense
Actually, you don't need the option if you defined the association on the model. You migration could look like this:
class AddUserRefToProducts < ActiveRecord::Migration
def change
add_column :products, :user_id, :integer
end
end
If the Product model has the association, it should work
class Product < ActiveRecord::Base
belongs_to :user
end

Rails Migration: add_reference to Table but Different Column Name For Foreign Key Than Rails Convention

I have the following two Models:
class Store < ActiveRecord::Base
belongs_to :person
end
class Person < ActiveRecord::Base
has_one :store
end
Here is the issue: I am trying to create a migration to create the foreign key within the people table. However, the column referring to the foreign key of Store is not named store_id as would be rails convention but is instead named foo_bar_store_id.
If I was following the rails convention I would do the migration like this:
class AddReferencesToPeople < ActiveRecord::Migration
def change
add_reference :people, :store, index: true
end
end
However this will not work because the column name is not store_id but is foo_bar_store_id. So how do I specify that the foreign key name is just different, but still maintain index: true to maintain fast performance?
in rails 5.x you can add a foreign key to a table with a different name like this:
class AddFooBarStoreToPeople < ActiveRecord::Migration[5.0]
def change
add_reference :people, :foo_bar_store, foreign_key: { to_table: :stores }
end
end
Inside a create_table block
t.references :feature, foreign_key: {to_table: :product_features}
In Rails 4.2, you can also set up the model or migration with a custom foreign key name. In your example, the migration would be:
class AddReferencesToPeople < ActiveRecord::Migration
def change
add_column :people, :foo_bar_store_id, :integer, index: true
add_foreign_key :people, :stores, column: :foo_bar_store_id
end
end
Here is an interesting blog post on this topic. Here is the semi-cryptic section in the Rails Guides. The blog post definitely helped me.
As for associations, explicitly state the foreign key or class name like this (I think your original associations were switched as the 'belongs_to' goes in the class with the foreign key):
class Store < ActiveRecord::Base
has_one :person, foreign_key: :foo_bar_store_id
end
class Person < ActiveRecord::Base
belongs_to :foo_bar_store, class_name: 'Store'
end
Note that the class_name item must be a string. The foreign_key item can be either a string or symbol. This essentially allows you to access the nifty ActiveRecord shortcuts with your semantically-named associations, like so:
person = Person.first
person.foo_bar_store
# returns the instance of store equal to person's foo_bar_store_id
See more about the association options in the documentation for belongs_to and has_one.
To expand on schpet's answer, this works in a create_table Rails 5 migration directive like so:
create_table :chapter do |t|
t.references :novel, foreign_key: {to_table: :books}
t.timestamps
end
EDIT: For those that see the tick and don't continue reading!
While this answer achieves the goal of having an unconventional foreign key column name, with indexing, it does not add a fk constraint to the database. See the other answers for more appropriate solutions using add_foreign_key and/or 'add_reference'.
Note: ALWAYS look at the other answers, the accepted one is not always the best!
Original answer:
In your AddReferencesToPeople migration you can manually add the field and index using:
add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id
And then let your model know the foreign key like so:
class Person < ActiveRecord::Base
has_one :store, foreign_key: 'foo_bar_store_id'
end
# Migration
change_table :people do |t|
t.references :foo_bar_store, references: :store #-> foo_bar_store_id
end
# Model
# app/models/person.rb
class Person < ActiveRecord::Base
has_one :foo_bar_store, class_name: "Store"
end
Under the covers add_reference is just delegating to add_column and add_index so you just need to take care of it yourself:
add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id
foreign key with different column name
add_reference(:products, :supplier, foreign_key: { to_table: :firms })
refer the documentation Docs

Trouble changing to has_many through from has_many and belongs_to

I've done about a billion searches and tried a number of things here but I'm still getting errors. I've recently changed to a has many through via a model called joinable(maybe thats the problem) and I can't seem to get things working straight. Part of me thinks it something small as I get the idea of it all but I'm not sure I've done it correctly. I'm also using devise.
Here is what I think are all the relevant portions
User
class User < ActiveRecord::Base
acts_as_voter
has_many :joinables
has_many :pits, through: :joinables
has_many :comments
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
class Pit < ActiveRecord::Base
validates :topic, :author, :summary, presence: true
acts_as_taggable
acts_as_votable
has_many :comments
has_many :joinables
has_many :users, through: :joinables
mount_uploader :image, ImageUploader
I created a separate table called "joinable" and now I'm stuck figuring out how to populate it. I can create a user, but can't create a pit. Do I need to revamp my controllers or is their something small I may be missing? I get the idea but some of the little details are fuzzy based on all that I've read so far. I even tried a HABTM with a join table called Pit_Users.
I'm currently getting "Could not find table 'joinables"
coming from here in my controller
def create
#pit = current_user.pits.create(pit_params)
recent migration
class Joinable < ActiveRecord::Migration
create_table :joinable do |t|
t.integer :pit_id, :user_id
t.timestamps
end
end
I've tried a number of combinations all with similar errors. Many of the tutorials/guides are good with the basics but then seem to be leaving out a few details. That or I'm just missing them. Anyways. Would love it if someone more knowledgeable could point out what are probably obvious mistakes. Thanks.
In the migration file, it should be:
class Joinables < ActiveRecord::Migration
create_table :joinables do |t|
t.integer :pit_id
t.integer :user_id
end
end
And in the app/models/joinable.rb, there should be:
class Joinable < ActiveRecord::Base
belongs_to :user
belongs_to :pit
end
You can verify if it is working at the Rails console. Try this to get a Pit record with the association:
user_1 = User.create( ... )
pit_1 = user_1.pits.create!( ... )
pit_1.users.first # should give you user as user_1
Solution is to run rails generator for model
Run from console
rails generate model Joinable pit:references user:references
And delete your migration file for
class Joinable < ActiveRecord::Migration
create_table :joinable do |t|
t.integer :pit_id, :user_id
t.timestamps
end
end
After running rails generator you will get model named Joinable that is required for relations when using through and it will create appropriate migration for you.

Resources