63

I have been (hours) trouble with associations in Rails. I found a lot of similar problems, but I couldn't apply for my case:

City's class:

class City < ApplicationRecord
  has_many :users
end

User's class:

class User < ApplicationRecord
  belongs_to :city

  validates :name, presence: true, length: { maximum: 80 }
  validates :city_id, presence: true
end

Users Controller:

def create
    Rails.logger.debug user_params.inspect
    @user = User.new(user_params)
    if @user.save!
      flash[:success] = "Works!"
      redirect_to '/index'
    else
      render 'new'
    end
 end

def user_params
  params.require(:user).permit(:name, :citys_id)
end

Users View:

<%= form_for(:user, url: '/user/new') do |f| %>
  <%= render 'shared/error_messages' %>

  <%= f.label :name %>
  <%= f.text_field :name %>

  <%= f.label :citys_id, "City" %>
  <select name="city">
    <% @city.all.each do |t| %>
      <option value="<%= t.id %>"><%= t.city %></option>
    <% end %>
  </select>
end

Migrate:

class CreateUser < ActiveRecord::Migration[5.0]
  def change
    create_table :user do |t|
      t.string :name, limit: 80, null: false
      t.belongs_to :citys, null: false
      t.timestamps
  end
end

Message from console and browser:

ActiveRecord::RecordInvalid (Validation failed: City must exist):

Well, the problem is, the attributes from User's model that aren't FK they are accept by User.save method, and the FK attributes like citys_id are not. Then it gives me error message in browser saying that "Validation failed City must exist".

Thanks

Pedro Gabriel Lima
  • 1,122
  • 1
  • 9
  • 24

8 Answers8

121

Try the following:

belongs_to :city, optional: true

According to the new docs:

4.1.2.11 :optional

If you set the :optional option to true, then the presence of the associated object won't be validated. By default, this option is set to false.

Dave Powers
  • 2,051
  • 2
  • 30
  • 34
Igor_Marques
  • 1,742
  • 2
  • 16
  • 24
  • 1
    Hey Igor.. it's nice to see a BR here :) Thank you for your answer. It worked! I found this answer earlier, but I didn't have tried until now. The reason was that it seems to me this **option** invalidate the attributer's validation in a server level. So I checked to be sure, when I give a wrong value to the foreign key city_id the error message just show up after the DB tried to make a insertion query, it seems to me a DB level validation. Anyways, I will make my own validation in a server level to solve this. I would like to give you a UPVOTE, but I don't have enough score :( haha – Pedro Gabriel Lima Aug 17 '16 at 03:11
  • 1
    ohhh, I didn't know that! hahaha nice :) – Pedro Gabriel Lima Aug 18 '16 at 04:50
  • 4
    Argh this is quite an annoying gotcha. In `FactoryGirl`, if you simply make a factory for `User` without that snippet, in the `belongs_to` class, you will get that error. Good catch! – Ka Mok Oct 05 '16 at 02:42
15

This comes a bit late but this is how to turn off this by default in rails 5:

config/initializers/new_framework_defaults.rb

Rails.application.config.active_record.belongs_to_required_by_default = false

In case you don't want to add optional: true to all your belongs_to.

I hope this helps!

Jeremie
  • 2,241
  • 2
  • 17
  • 25
  • I upgraded to Mac OS Mojave yesterday and now I'm getting these validation errors, even though my project has set this to false (since we upgraded to Rails 5 way back when). Anyone know what makes this happen? – Sunil D. Nov 27 '18 at 17:14
4

You need to add the following to the end of the belongs_to relationship statement:

optional: true

It is possible to set this on a global level so that it works in the same way as older versions of rails, but I would recommend taking the time to manually add it to the relationships that really need it as this will cause less pain in the future.

David Kane
  • 51
  • 2
3

I found out a solution to the problem "Validation failed: Class must exist" and it's better than use:

belongs_to :city, optional: true

4.1.2.11 :optional

If you set the :optional option to true, then the presence of the associated object won't be validated. By default, this option is set to false.

cause you still make a validation in application level. I solve the problem making my own validation in create method and changing user_params method:

def create

  @city = City.find(params[:city_id])

  Rails.logger.debug user_params.inspect
  @user = User.new(user_params)

  @user.city_id = @city.id

  if @user.save!
    flash[:success] = "Works!"
    redirect_to '/index'
  else
    render 'new'
  end
end

def user_params
  params.require(:user).permit(:name)
end

I didn't test this code, but it works in another project mine. I hope it can help others!

Pedro Gabriel Lima
  • 1,122
  • 1
  • 9
  • 24
2

Rails 5

If you have a belongs_to relationship to :parent then you have to pass an existing parent object or create a new one then assign to children object.

Kick Buttowski
  • 6,709
  • 13
  • 37
  • 58
ricks
  • 3,154
  • 31
  • 51
1
belongs_to :city, required: false
Purkhalo Alex
  • 3,309
  • 29
  • 27
  • 3
    Please try to give an explanation to answer the question and not just post code. Thanks. – loki Jul 25 '17 at 14:37
1
params.require(:user).permit(:name, :citys_id)

It's a mistake, isn't it? (citys_id vs city_id)

v.sheldeshov
  • 178
  • 6
0
Rails.application.config.active_record.belongs_to_required_by_default = false

This works because the Rails 5 has true by default to disable you go under Initilizers then click on the New_frame-work and turn the true to false

Clara
  • 2,677
  • 4
  • 16
  • 31