Failed to save the new associated seo_setting on has_one association - ruby-on-rails

I have 2 models:
class Page < ApplicationRecord
enum page_type: STATIC_PAGE_TYPES, _suffix: true
has_one :seo_setting
accepts_nested_attributes_for :seo_setting, update_only: true
validates :title, :subtitle, length: { maximum: 50 }
validates :page_type, uniqueness: true
def to_param
"#{id}-#{page_type}".parameterize
end
end
and
class SeoSetting < ApplicationRecord
mount_uploader :og_image, SeoSettingsOgImageUploader
belongs_to :page
validates :seo_title, :seo_description, :og_title, :og_description, :og_image, presence: true
end
My Page objects are created from the seeds.rb file, and when I want to edit them, I get an error: Failed to save the new associated seo_setting.
In the form I have this:
<div class="card-body">
<%= form_for([:admin, #page]) do |f| %>
<%= render 'shared/admin/error-messages', object: #page %>
<div class="form-group">
<%= f.label :title, t('admin.shared.title') %>
<%= f.text_field :title, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :subtitle, t('admin.shared.subtitle') %>
<%= f.text_field :subtitle, class: 'form-control' %>
</div>
<h3>SEO Settings</h3>
<%= f.fields_for :seo_setting, f.object.seo_setting ||= f.object.build_seo_setting do |form| %>
<div class="form-group">
<%= form.label :seo_title, t('admin.shared.seo_title') %>
<%= form.text_field :seo_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :seo_description, t('admin.shared.seo_description') %>
<%= form.text_area :seo_description, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_title, t('admin.shared.og_title') %>
<%= form.text_field :og_title, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_description, t('admin.shared.og_description') %>
<%= form.text_area :og_description, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :og_image, t('admin.shared.og_image') %>
<div class="row">
<div class="col-lg-12">
<%= image_tag(form.object.og_image.url, style: 'width: 100px') if form.object.og_image? %>
</div>
</div>
<%= form.file_field :og_image %>
<%= form.hidden_field :og_image_cache %>
</div>
<% end %>
<div class="form-group">
<%= f.submit t('admin.actions.submit'), class: 'btn btn-success' %>
<%= link_to t('admin.actions.cancel'), admin_page_path(#page) , class: 'btn btn-default' %>
</div>
<% end %>
</div>
If I remove validations from my SeoSetting model, everything is working. It seems Rails doesn't like this part: f.object.build_seo_setting, because it creates a record in my database. Any ideas of how can I solve this issue? Thanks ahead.

Just had to change this line:
<%= f.fields_for :seo_setting, f.object.seo_setting ||= f.object.build_seo_setting do |form| %>
for this one:
<%= f.fields_for :seo_setting, #page.seo_setting.nil? ? #page.build_seo_setting : #page.seo_setting do |form| %>

Looks as if the problem lies here:
accepts_nested_attributes_for :seo_setting, update_only: true
in that you only allow the updating of the seo_setting on update.
Then, when you use this code:
f.object.seo_setting ||= f.object.build_seo_setting
You're falling back to a new seo_setting if the associated object is missing.
For this to work, you'll need to either remove the update_only: true, or only render the fields for the association if the seo_setting already exists.

Accepted answer, a conditional inside fields_for line, by OP, works also with polymorphic association and nested form (fields_for). Thnx Alex.

Related

Rails error messages not showing?

I'm new to rails...but I'm trying to do an app on my own to "practice" what I've learned.
I have a new form with model validations, but the error messages aren't showing. Here is what I have:
seed.rb (Model)
class Seed < ApplicationRecord
validates :name, presence: true
validates :category, presence: true
validates :latin, presence: true
validates :maturity, presence: true
validates :sun, presence: true
validates :sow, presence: true
validates :cycle, presence: true
validates :description, presence: true, length: { minimum: 5, maximum: 500 }
mount_uploader :seedimage, SeedImageUploader
end
seed_controller.rb (Controller)
class SeedsController < ApplicationController
def index
#seeds = Seed.all
end
def new
#seed = Seed.new
end
def create
#seed = Seed.new(seed_params)
if #seed.save
redirect_to seeds_path
else
render 'new'
end
end
def edit
end
def update
#seed = Seed.find(params[:id])
if #seed.update(seed_params)
redirect_to #seed
else
render 'edit'
end
end
def show
#seed = Seed.find(params[:id])
end
def destroy
#seed = Seed.find(params[:id])
#seed.destroy
redirect_to seeds_path
end
def seed_params
params.require(:seed).permit(:name, :category, :latin, :maturity, :sun, :sow, :cycle, :description, :seedimage)
end
end
_form.html.erb (Form for 'New') (new.html.erb just has <% render 'form' %>
<%= form_with model: #seed, class: "form-horizontal" do |f| %>
<% if #seed.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#seed.errors.count, "error") %> prohibited
this seed from being saved:
</h2>
<ul>
<% #seed.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: "form-control", placeholder: "Seed Name" %>
</div>
<div class="form-group">
<%= f.label :category %>
<%= f.text_field :category, class: "form-control", placeholder: "Category: 'beans'" %>
</div>
<div class="control-label">
<%= f.label :latin %>
<%= f.text_field :latin, class: "form-control", placeholder: "Latin Name" %>
</div>
<div class="form-group">
<%= f.label :maturity %>
<%= f.number_field :maturity, class: "form-control", placeholder: "Maturity Time" %>
</div>
<div class="form-group">
<%= f.label :sun %>
<%= f.select(:sun, options_for_select([['Full Sun'], ['Partial Sun'], ['Full Shade']]), {}, { class: "custom-select"}) %>
</div>
<div class="form-group">
<%= f.label :sow %>
<%= f.text_field :sow, class: "form-control", placeholder: "Plant Indoors/Sow Outdoors/etc.." %>
</div>
<div class="form-group">
<%= f.label :cycle %>
<%= f.text_field :cycle, class: "form-control", placeholder: "Annual/Perennial/etc.." %>
</div>
<div class="form-group">
<%= f.label :description %>
<%= f.text_area :description, size: "60x12", class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :seedimage %>
<%= f.file_field :seedimage, class: "form-control" %>
</div>
<div class="form-group">
<%= f.submit "Create", class: "btn btn-primary btn-lg" %>
</div>
<% end %>
I'm a bit confused why this doesn't work? Right now when I hit the create button it flashes, but no error messages. I can CONFIRM that the model is using the validations because if I try to do a Seed.create() and check messages against that it doesn't indeed work....so I'm a bit confused?
From what I can tell the .any? isn't happening, since if I do a ! to that statement it'll at least display 0 messages.
You are not seeing any error messages because all the forms generated by form_with tag are remote: true by default and send xhr(ajax) requests to the server. If you want to see the error messages, as of your current setup, you have to add local: true wich makes the submission normal.
Replacing
<%= form_with model: #seed, class: "form-horizontal" do |f| %>
with
<%= form_with model: #seed, local: true, class: "form-horizontal" do |f| %>
will do the trick. Hope this will help.

Type mismatch on association, f.select rails

I have a problem in my form, i want to create an entry in my "members" table, each member are connected to a "year" but I keep getting mismatches on the selection of the year.
This is my form:
<div class="field">
<%= f.label :name %><br>
<%= f.text_area :name %>
</div>
<div class="field">
<%= f.label :nickname %><br>
<%= f.text_area :nickname %>
</div>
<div class="field">
<%= f.label :year %>
<%= f.select :year, options_for_select(#years.all.map{|y| [y.year,y.id]}) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
And here are the models:
class Member < ApplicationRecord
belongs_to :year
mount_uploader :image, ImageUploader
end
class Year < ApplicationRecord
has_many :members, dependent: :destroy
end
When I try to submit I get the following error:
Year(#70050157849460) expected, got "1" which is an instance of String(#9412380)
Where did I go wrong?
EDIT:
Here is the code for the controller
class MembersController < ApplicationController
def home
#members = Member.all
end
def new
#member = Member.new
#years = Year.all
end
def create
#member = Member.new(member_params)
if #member.save
flash[:success] = "Member Created"
redirect_to root_path
else
render 'form'
end
end
private
def member_params
params.require(:member).permit(:name,:nick,:position,:image,:year)
end
end
<div class="field">
<%= f.label :name %><br>
<%= f.text_area :name %>
</div>
<div class="field">
<%= f.label :nickname %><br>
<%= f.text_area :nickname %>
</div>
<div class="field">
<%= f.label :year %>
<%= f.select :year_id, options_for_select(#years.all.map{|y| [y.year,y.id]}) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
or
<div class="field">
<%= f.label :name %><br>
<%= f.text_area :name %>
</div>
<div class="field">
<%= f.label :nickname %><br>
<%= f.text_area :nickname %>
</div>
<div class="field">
<%= f.label :year %>
<%= f.select :year, options_for_select(#years.all.map{|y| [y.year,y]}) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
This second I did not check but first will work you need to say year_id because when you did map on collection you set id as parameter that is being passed
but on form select you basically told form to expect active record object.

How to Force the User to Complete the Whole Form?

I am creating a Sing Up form. I need the user to complete every field of the form, but the rails only blocks the creation when there is no password. What I have to do to force the user to complete the whole form before submiting?
Here's my form code:
<%= form_for(user) do |f| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :first_name %>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.label :fav_team, "Favorite Team" %>
<%= collection_select(:user, :fav_team, League.order(:name), :id, :name, {:include_blank => "Select a League"}, { :id => "leagues_select"}) %>
<%= grouped_collection_select(:user, :fav_team, League.order(:name), :teams, :name, :id, :name, {:include_blank => true}, {}) %>
</div>
<div class="field">
<%= f.label :net_worth, "Net Worth (USD)" %>
<%= f.text_field :net_worth, :readonly => true, :value => "100" %>
</div>
<div class="field">
<%= f.label :country %>
<%= f.select :country, options_for_Countrys, :include_blank => true %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You have to write validations in your models to make sure the data is valid and present.
In your case you have to do something like this
class User < ApplicationRecord
validates :first_name, presence: true, length: { maximum: 50 }
validates :last_name, presence: true, length: { maximum: 50 }
end
You can check here for more information

Updating comments and state from the same form

I'm trying to build a small expense tracking app using Rails 4.1. When a user submits the expense request, it's state is marked as pending by default. The admin has to approve the request. I'm using state_machine gem to do this.
I just added comment functionality using acts_as_commentable gem, which works fine on its own. I wanted to combine the approval drop down and the comment box in the same form and used the following code in the expense show page:
<div class="row">
<div class="col-md-8">
<%= form_for [#expense, Comment.new] do |f| %>
<div class="form-group">
<%= f.label :state %><br />
<%= f.collection_select :state, #expense.state_transitions, :event, :human_to_name, :include_blank => #expense.human_state_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :comment %><br />
<%= f.text_area :comment, class: "form-control" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
<br>
The problem is I get the "NoMethodError in Expenses#show - undefined method `state' for #". Is there a way I can update both the approval status and comment in one go?
The updated show page with nested attributes:
<div class="row">
<div class="col-md-8">
<%= nested_form_for (#expense) do |f| %>
<div class="form-group">
<%= f.label :state %><br />
<%= f.collection_select :state, #expense.state_transitions, :event, :human_to_name, :include_blank => #expense.human_state_name, class: "form-control" %>
</div>
<%= f.fields_for :comments do |comment| %>
<div class="form-group">
<%= comment.label :comment%>
<%= comment.text_area :comment, class: "form-control" %>
</div>
<% end %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
Nested attributes are your friend. They are so easy to implement I'd recommend avoiding acts_as_commentable to remove an unnecessary dependency and so you understand what's going on.
Use them like this:
# expense.rb
class Expense < ActiveRecord::Base
has_many :comments # << if polymorphic add `, as: :commentable`
accepts_nested_attributes_for :comments
end
# expenses_controller.rb
def new
#expense = Expense.new
#expense.comments.build
end
# expenses/new.html.haml
= form_for(#expense) do |f|
- # expense inputs...
= f.nested_fields_for(:comments) do |comment|
= comment.text_area(:body)
= f.submit
There are many options, so check the docs for more details (they're quite good).

Fields_for disappear when adding accepts_nested_attributes_for

I'm doing a nested form in Rails 3.2.5, but when I add the accepts_nested_attributes_for my fields_for disappear (they just stop showing in my form).
This are my models:
class Product < ActiveRecord::Base
attr_accessible :title, :description, :variants_attributes
has_many :variants
accepts_nested_attributes_for :variants
validates :title, presence: true
end
My second model is
class Variant < ActiveRecord::Base
belongs_to :product
attr_accessible :price, :sku, :weight, :inventory_quantity, :product_id
end
And in my view I have
<%= form_for [:admin, #product], :html => {:multipart => true} do |f| %>
<%= render 'admin/shared/error_messages', object: f.object %>
<fieldset>
<legend>Producto Nuevo</legend>
<div class="control-group">
<%= f.label :title, 'Título', class: 'control-label' %>
<div class="controls">
<%= f.text_field :title %>
</div>
</div>
**<%= f.fields_for :variants do |variant| %>
<%= render 'inventory_fields', f: variant %>
<% end %>**
<div class="actions">
<%= f.submit 'Guardar', class: 'btn btn-primary' %>
</div>
<% end %>
_inventory_fields.html.erb
<div class="control-group">
<%= f.label :inventory_quantity, 'How many are in stock?', class: 'control-label' %>
<div class="controls">
<%= f.text_field :inventory_quantity, class: 'span1', value: '1' %>
</div>
</div>
The part between the ** is the one that is not being printed. And when I remove accepts_nested_attributes_for in my Product model fields_for start showing again but my form won't work.
What's happening?!?!
In the controller new action call
#product.varients.build
That should create 1 in memory varient in the product varient collection and it should bind to the fields for

Resources