Fly
Fly

Reputation: 11

Rails 5.1.4 Validation: How to accept blank attributes without saving them to the db?

I'm pretty new to Rails and I couldn't find a solution to this problem (I hope I haven't missed something obvious).

how can I make my application accept blank fields, but not saving the empty value to the db?

I mean, if I do

validates :name, presence: true

I will get an error message, that name can't be blank. But I just want it to ignore blank/nil with not saving it into db.

How would I do that? (I hope asked that question comprehensible, tell me if not)

Update:

ok, so here is my code:

Models:

class Voting < ApplicationRecord
  has_many :options
  accepts_nested_attributes_for :options
  validates :content, presence: true    
end

and

class Option < ApplicationRecord
  belongs_to :voting
  validates_associated :voting

  validates :vote, presence: { message: "at least two Options required." }, if: :option_counter

  def option_counter
      voting.options.count < 2
  end    
end

OptionsController:

class OptionsController < ApplicationController
  def index
    @voting  = Voting.find(params[:voting_id])
    @options = @voting.options
  end

  def create
    @voting = Voting.find(params[:voting_id])
    @option = @voting.options
    @option.create(option_params)
    if @voting.valid?
      flash[:notice] = "Voting created!"
      redirect_to voting_options_path
    else
      render 'votings/show'
    end

  end

  private
  def option_params
    ...
  end
end

Views: votings/show.html.erb

<strong>Your Voting:</strong>
<p><%= @voting.content %></p>

<% if @voting.errors.any? %>
    <div>
        <% @voting.errors.full_messages.each do |msg| %>
            <li><%= msg %></li>
        <% end %>
    </div>
<% end %>

<ul>
  <%= form_with(model: [ @voting, @voting.options.build ], local: true) do |form| %>
      <p>
        <%= form.label :vote %><br>
        <% 3.times do %>
          <input type="string" name="option[][vote]" /><br><br>
        <% end %>
      </p>
      <p>
        <%= form.submit %>
      </p>

  <% end %>
</ul>

I think, this should be the interesting stuff. I hope it helps to understand the situation.

Upvotes: 0

Views: 2162

Answers (3)

Pablo
Pablo

Reputation: 3005

Validations occur when saving to the database. So it is possible to accept empty fields even with your validation. But you must provide a value before saving. If you are getting an error it’s because your application is trying to save the record. So your code is not doing what you say (accepting empty fields without saving). You are saving.

The question is. Which line of code raises the error? Check your logs.

Validations are explained here: http://guides.rubyonrails.org/v2.3.11/activerecord_validations_callbacks.html (or any other version)

Edit 1

If you want to prevent this record from saving, but don’t inform the user, you could define a method save_unless_empty

def save_unless_empty
  if empty_fields? and new_record?
    delete
  else
    save
  end 
end

You must define the empty_fields? method so that you check the fields you need.

Edit 2

Thanks for adding the code. I see you are creating many options in the OptionsController#Create. This method is supposed to create only one option. You should create or update many options in VotingsController#create (or #update), given that all options belong to a voting object.

Another issue: You are building a new option object in the form (@voting.options.build), but you are not using it (you should use fields_for :option, @voting.options.build do.... This could be inside the 3.times loop. Even better, this can be done in the controller, before showing the view

And another one. Your validation regarding at least 2 options should be in the Voting model.

Some refactoring (although a lot is still missing):

In the view

<%= form_with @voting do |form| %>
  <%= form.label :vote %><br>
  <% @options.each do |option| %>
    <%= form.fields_for option do |form_opt| %>
      <%= form_opt.input_field :vote %><br><br> #question: is vote really the name of the attribute in the option model? or should this be a check_box showing the option name?
    <% end %>
  <% end %>
  <%= form.submit %>
<% end %>

Then you should put the saving logic in VotingsController:

class VotingsController < ApplicationController

  def new
    @voting = Voting.new
    3.times do 
      @voting.options.build
    end
    @options = @voting.options
  end

  def edit
    @voting = Voting.find_by(id: params[:id])
    @options = @voting.options
  end

  def create
    @voting = Voting.create_without_null_options(voting_params)
    #etc
  end

  def edit
    @voting = Voting.find_by(id: params[:id])
    @voting.update_without_null_options(voting_params)
    #etc
  end

  private
    def voting_params
      ...
    end
  end

In the Voting Model

def self.create_without_null_options params
  #Create the voting without its options
  @voting = Voting.create(params.except[:options_attributes])

  #create options but save only non empty options
  params[:options_attributes].each do |option_att|
    @option = @voting.options.build(option_att)
    @option.save_unless_empty
  end
end

def update_without_null_options params
  #Save the voting without its options
  update_attributes(params.except[:options_attributes])

  #create options but save only non empty options
  params[:options_attributes].each do |option_att|
    @option = options.build(option_att)
    @option.save_unless_empty
  end
end

Upvotes: 1

rantingsonrails
rantingsonrails

Reputation: 568

There is no such thing as saving specific database fields. An entire record is stored/received.

If the name field is not required, remove the validates :name, presence: true on the model.

If there's a default value that you want in your database instead of nil/"", you can set it in the migration; for example a single space

add_column :name, :string, :default => " "

If you need to preserve those records and modify that database column, you can use change_column in a new migration

change_column :users, :name, :string, :default => " "

I used users as an example table name, because from your code above I could not understand which table the name column was on.

Upvotes: 0

ThorTL67
ThorTL67

Reputation: 528

What that validation is doing is checking that the :name field has a value (if it does not have a value it will show the error message).

If you wanted to allow a blank value then I'm pretty sure you would just need to remove that validation. If a blank value (ie a string of ' ') is being saved in your database then I would check the 'name' column (in the database) to see if null is allowed.

Upvotes: 0

Related Questions