Josh M.
Josh M.

Reputation: 435

Validation fails, field must exist with allow_nil

I'm creating a blog where articles can be responses to other articles. Articles can also be part of groups. However articles do not have to be in a group or be a response to another article.

I'm attempting to follow the Rails docs to create articles as self-joined records.

I created user, group and article scaffolds:

bin/rails g scaffold user username:string email:string birthday:date

bin/rails g scaffold group name:string user:references

bin/rails g scaffold article user:references parent:references title:string subheading:string body:text pub_date:datetime group:references hidden:boolean prompt:boolean

I'm trying to use allow_nil in the model validation.

class Article < ApplicationRecord
  belongs_to :user
  belongs_to :parent, class_name: "Article"
  has_many :replies, class_name: "Article", foreign_key: "parent_id"
  belongs_to :group

  validates :parent, length: {minimum: 1}, allow_nil: true
  validates :group, length: {minimum: 1}, allow_nil: true
end

However when I run the db:seed:

user1 = User.create(username: "pete01",email: "[email protected]",
    birthday:"1980-01-30")

article1 = Article.create!(user:user1, parent:nil, title:"My First Article",
        subheading:"This is important", body:"The body of my first article",
        pub_date:"2015-12-26", group:nil, hidden:false, prompt:false)

I get this error:

ActiveRecord::RecordInvalid: Validation failed: Parent must exist, Group must exist

Is there somewhere else where I should be telling Rails it does not need to validate Group and Parent?

Upvotes: 2

Views: 9635

Answers (5)

Matthias Winkelmann
Matthias Winkelmann

Reputation: 16394

This is most likely to be the result of migrating to Rails 5, where the default for belongs_to relations changed. See this answer for details,

Upvotes: 2

郭友进
郭友进

Reputation: 111

Solution is to find the file new_framework_defaults.rb, change this to false:

Rails.application.config.active_record.belongs_to_required_by_default = false

Upvotes: 7

Richard Peck
Richard Peck

Reputation: 76784

#app/models/article.rb
class Article < ActiveRecord::Base
   belongs_to :parent, class_name: "Article"
   belongs_to :group

   validates :parent, presence: true, allow_nil: true
   validates :group, presence: true, allow_nil: true
end

Several problems for you:

You're validating parent & group -- these are associative objects.

Your error says "[Object] must exist", which means your validation works -- Rails cannot find a "nil" association (it's expecting objects).

What you should have is either to validate parent_id & group_id, or to validate the presence of the associative object with something like presence.

I've included the following validations I would use:

validates :parent, presence: true, allow_nil: true
validates :group, presence: true, allow_nil: true

You could also try:

validates :parent_id, length: { minimum: 1 }, allow_nil: true
validates :group_id, length: { minimum: 1 }, allow_nil: true

Articles can also be part of groups

You'll probably want to use a has_and_belongs_to_many association for it then:

#app/models/article.rb
class Article < ActiveRecord::Base
   has_and_belongs_to_many :groups
end

#app/models/group.rb
class Group < ActiveRecord::Base
   has_and_belongs_to_many :articles
end

You'll need a join table called articles_groups with the columns article_id & group_id:

enter image description here

You can create the migration as follows:

$ rails g migration CreateArticlesGroups

# db/migrate/create_articles_groups__________.rb
class CreateArticlesGroups < ActiveRecord::Migration
  def change
    create_table :articles_groups, id: false do |t|
      t.belongs_to :article, index: true
      t.belongs_to :group, index: true
    end
  end
end

$ rake db:migrate

This will allow you to populate the associative objects like this:

@article = Article.find params[:article_id]
@group = Group.find params[:id]

@article.groups << group

Upvotes: 3

Raffael
Raffael

Reputation: 2669

allow_nil: true is an option to a validator, not to the validates method. You are using the length: key which will call the LengthValidator with the {minimum: 1} hash as arguments (similar to using validates_length_of).

E.g., use

validates :parent, length: {minimum: 1, allow_nil: true}

instead of

validates :parent, length: {minimum: 1}, allow_nil: true


Note: If using multiple validators, you will need to specify allow_nil for each of them.

Upvotes: 1

Mihail Petkov
Mihail Petkov

Reputation: 1545

Try to remove your validations. Also check your migration for Article. Maybe you have null: false there for group_id and parent_id

class Article < ApplicationRecord
  belongs_to :user
  belongs_to :parent, class_name: "Article"
  has_many :replies, class_name: "Article", foreign_key: "parent_id"
  belongs_to :group
end

Upvotes: 0

Related Questions