I have an issue where a validation is suddenly not working. The field is a select box, and it's not setting the value. So when I try to submit the form, it says that Payee can't be blank. This is a 1 to 1 relationship between Expense and Payee. What happened? Do you see any issues here?
The field in question. It's in a helper but I've tried putting it back into the form with no luck.
def expense_payee_id_field(f)
f.select :payee_id, Payee.all.collect { |p| [p.display_name, p.id] }, { prompt: "Choose Payee"},{class:"fluid ui dropdown"}
end
The form:
<%= form_for #expense, html: { :class => "ui form segment" }, :remote => true do |f|%>
<div class="field">
<%= f.label :date%>
<div class="ui small input">
<%= f.date_field :date %>
</div>
</div>
<div class="field">
<%= f.label :amount %>
<div class="ui small input">
<%= f.text_field :amount %>
</div>
</div>
<div class="field">
<%= f.label :check_number %>
<div class="ui small input">
<%= f.text_field :check_number %>
</div>
</div>
<div class="field">
<%= f.label :payee %>
<div class="ui input">
<%= expense_payee_id_field(f)%>
</div>
</div>
<%= f.submit class: "ui blue button" %>
<% end %>
My models:
expense.rb
class Expense < ApplicationRecord
has_one :payee
monetize :amount_cents
has_many :expense_expense_categories, inverse_of: :expense
has_many :expense_categories, through: :expense_expense_categories, :dependent => :destroy
accepts_nested_attributes_for :expense_expense_categories,:allow_destroy => true
validates_associated :expense_expense_categories
validates :date, presence: true
validates_numericality_of :amount, presence: true, greater_than: 0
validates :expense_expense_category_ids, presence: true
validates_presence_of :payee_id
end
payee.rb
class Payee < ApplicationRecord
belongs_to :expense
validate :company_or_name
def full_name
[first_name, last_name].join(" ")
end
def display_name
if company.blank?
full_name
else
company
end
end
private
def company_or_name
if company.blank? && first_name.blank?
errors[:base] << "You must specify company or name"
end
end
end
Related
I have three models -> locations, products and stocks.
Stocks is a join table of locations and products.
When creating a new product i used fields_for to show locations and even though i got it to work, for some reason now it does not seem to work anymore and it gives me the above error.
<div class="input-field">
<%= f.label :product_name %>
<%= f.text_field :name, autofocus: true %>
</div>
<div class="input-field">
<%= f.label :price %>
<%= f.text_field :price, autofocus: true %>
</div>
<% if !#edit %>
<%= f.fields_for :stocks do |ff| %>
<div class="input-field margin-top x-4">
<%= ff.collection_select :location_id, Location.all, :id, :structured_location , {:prompt => "Please Select Locations for Product"}, {multiple: true} %>
<%= ff.label :locations %>
</div>
<div class="input-field">
<%= ff.label :quantity %>
<%= ff.text_field :quantity %>
</div>
<div class="input-field">
<%= ff.label :threshold_quantity %>
<%= ff.text_field :threshold_quantity %>
</div>
<% end %>
<% else %>
<%= collection_select :product, :location_ids, Location.all, :id, :structured_location , {:prompt => "Please Select Locations for Product"}, {multiple: true} %>
<% end %>
<div class="row margin-top x-4">
<div class="col s12 center-align">
<%= f.submit "#{current_page?(new_product_path) ? "Create Product" : "Update Product"}", class: "btn wave-effect pink darken-1 btn-large" %>
</div>
</div>
controller
class ProductsController < ApplicationController
helper_method :sort_column, :sort_direction
def index
#products = Product.order(sort_column + " " + sort_direction)
end
def new
#product = Product.new
#product.stocks.build
end
def create
#product = Product.new(product_params)
if #product.save!
flash[:notice] = "Successfully saved..."
redirect_to products_path
else
flash[:alert] = "Something went wrong, please check the values you entered"
redirect_to :back
end
end
private
def product_params
params.require(:product).permit(:name,:price, location_ids: [], stocks_attributes: [:id, :quantity, :threshold_quantity, location_id: []])
end
end
product model
class Product < ApplicationRecord
has_many :stocks, dependent: :destroy
has_many :locations, :through => :stocks
accepts_nested_attributes_for :stocks
end
parameters in rails console
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"l1BFhrdyB2QMO5k3+60GNiPphFfF+DXDGPbUU3V2Op2aekObjgIe13k8uoedmDIEZgIeXPZUeS/0VxQXkKa1Uw==",
"product"=>{"name"=>"Soap", "price"=>"10", "location_ids"=>["", "1",
"2"], "stocks_attributes"=>{"0"=>{"quantity"=>"100",
"threshold_quantity"=>"100"}}}, "commit"=>"Create Product"}
After hours of searching i stumbled upon this post
BigBinary
it seems that Rails 5 made belongs_to relationship IDs required by default thats why i had validations failed and i couldn't find anything about it.
simply adding optional: true in my stock model worked!
class Stock < ApplicationRecord
belongs_to :location, optional: true
belongs_to :product, optional: true
accepts_nested_attributes_for :product, allow_destroy: true
accepts_nested_attributes_for :location, allow_destroy: true
end
I have the following models in my rails app:
class SaleContact < ActiveRecord::Base
validates :key_contact_id, presence: true, uniqueness: { scope: :sales_opportunity_id, message: "Contact already added!" }
validates :sales_opportunity_id, presence: true
belongs_to :key_contact, inverse_of: :sale_contacts
belongs_to :sales_opportunity, inverse_of: :sale_contacts
has_many :phone_numbers, :through => :key_contact
accepts_nested_attributes_for :phone_numbers
end
I'm trying to create a sale_contact from the sales_opportunity screen by selecting a key_contact (assume a key_contact already exists). I would also like the ability to add a phone_number at the same time using fields_for and nested attributes.
class KeyContact < ActiveRecord::Base
validates :first_name, :last_name, :company_id, presence: true
has_many :phone_numbers, dependent: :destroy
belongs_to :company
has_many :sales_opportunities, :through => :sale_contacts
has_many :sale_contacts, dependent: :destroy
end
Assume I've already created the key_contact and assigned it to the company that owns it.
class PhoneNumber < ActiveRecord::Base
validates :number, :key_contact_id, presence: true
belongs_to :key_contact
end
Nothing magical here - just a very basic model for a phone number.
On the sales_opportunity page I can load a modal that adds a new sale_contact. It's a bootstrap modal loaded by AJAX, but I don't think that matters much (I've only included the form parts for brevity):
<%= form_for(#sale_contact, :html => {role: :form, 'data-model' => 'sale_contact'}, remote: true) do |f| %>
<% if #sale_contact.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#sale_contact.errors.count, "error") %> prohibited this sale_contact from being saved:</h2>
<ul>
<% #sale_contact.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group" id= "contact_error">
<%= f.label :key_contact_id, :class => "control-label" %>
<div id="contact_select">
<%= f.collection_select :key_contact_id, #sales_opportunity.company.key_contacts(:full_name), :id, :full_name %>
</div>
<span class="warning-block"></span>
<span class="help-block"></span>
</div>
<div class="form-group">
<%= f.label :role, :class => "control-label" %>
</br>
<%= f.select(:role, options_for_select(#roles.collect { |r| [r[0].humanize, r[0]] }, selected: #sale_contact.role), {}) %>
<span class="help-block"></span>
</div>
<div class="form-group">
<%= f.label :preference, :class => "control-label" %>
</br>
<%= f.select(:preference, options_for_select(#preferences.collect { |r| [r[0].humanize, r[0]] }, selected: #sale_contact.preference), {}) %>
<span class="help-block"></span>
</div>
<%= f.fields_for(:phone_numbers) do |phone| %>
<div class="form-group">
<%= phone.label :number, "Phone Number", :class => "control-label" %>
</br>
<%= phone.text_field :number, :placeholder => "Enter phone number (optional)" %>
<span class="help-block"></span>
</div>
<div>
<%= phone.hidden_field :key_contact_id %>
</div>
<% end %>
<div class="form-group">
<%= f.hidden_field :sales_opportunity_id, :value => #sales_opportunity.id %>
</div>
<%= f.submit "Save", class: "btn btn-large btn-success", data: { disable_with: "Submitting..." }%>
<% end %>
And from my sale_contact_controller new action:
def new
#sale_contact = SaleContact.new
#sale_contact.phone_numbers.build
#sales_opportunity = SalesOpportunity.find(params[:sales_opportunity_id])
#company = #sales_opportunity.company
#roles = SaleContact.roles
#preferences = SaleContact.preferences
render :modal_form
end
def sale_contact_params
params.require(:sale_contact).permit(:key_contact_id, :sales_opportunity_id, :role, :preference, phone_numbers_attributes: [:number, :id])
end
I run a javascript snippet when the modal is being retrieved via AJAX or the key_contact select dropdown changes to bring in the key_contact_id to the phone_numbers_attributes; if I don't do that I get a 422 unprocessable entity error for not sending through my key_contact_id.
Using this setup the modal pops into view and has the correct fields (both for sale_contact and phone_number), but will not save either of these models - I get a 500 error:
Completed 500 Internal Server Error in 17ms
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection (Cannot modify association 'SaleContact#phone_numbers' because the source reflection class 'PhoneNumber' is associated to 'KeyContact' via :has_many.):
app/controllers/sale_contacts_controller.rb:50:in `block in create'
app/controllers/sale_contacts_controller.rb:49:in `create'
I've tried other methods, such as setting the key_contact_id in the sale_contact create method, but I get the same result. I can't work out why this setup complains when I don't pass a key_contact_id (422 error) and complains when I do (500 internal server error as the key_contact_id is being set internally by the rails associations).
What does Rails want me to do here?
I had a similar issue with my form, but I was able to get passed it by using nested field_for tags
Proposal.rb
has_many :proposal_section_reviews, :through => :proposal_review_status
has_one :proposal_review_status, :dependent => :destroy
attr_accessible :proposal_review_status_attributes, :proposal_review_status_attributes, :proposal_section_reviews_attributes
accepts_nested_attributes_for :proposal_review_status, :proposal_section_reviews
ProposalSectionReview.rb
belongs_to :proposal_review_status
ProposalReviewStatus.rb
has_many :proposal_section_reviews, :dependent => :destroy
_form.html.haml
= form_for proposal do |f|
= f.fields_for :proposal_review_status do |ff|
= ff.fields_for :proposal_section_reviews
So in your instance I would try
<%= f.fields_for(:key_contact) do |key| %>
<%= key.fields_for(:phone_numbers) do |phone| %>
<div class="form-group">
<%= phone.label :number, "Phone Number", :class => "control-label" %>
</br>
<%= phone.text_field :number, :placeholder => "Enter phone number (optional)" %>
<span class="help-block"></span>
</div>
<div>
<%= phone.hidden_field :key_contact_id %>
</div>
<% end %>
<% end %>
Hope this helps
HI I am trying to implement the MTI in my application. I have a Person Model and 2 models inheriting from it: Client and TeamMember. When creating a Team Member I want to save to to database vallues for both person (first and last name, email etc) and team member(experience level, type of team, if lead or not). I am using the nested attributes form so in my team member form I am nesting the person fields. Unfortunatellly I am getting "Can't mass-assign protected attributes: person" error when trying to save. Can anyone tell me how this can be solved? Thanks!
Models:
UPDATED TeamMember class but still the same error
also tried people_attributes and persons_attributes and none of these worked
class TeamMember < ActiveRecord::Base
has_many :project_team_members
has_many :projects, through: :project_team_members
has_one :person, as: :profile, dependent: :destroy
accepts_nested_attributes_for :person
attr_accessible :person_attributes, :experience_level, :lead, :qualification, :team
end
class Person < ActiveRecord::Base
belongs_to :company
belongs_to :profile, polymorphic: true
attr_accessible :email, :first_name, :last_name, :phone_number, :profile_id, :profile_type
end
Controller as follows:
class TeamMembersController < ApplicationController
def create
person = Person.create! { |p| p.profile = TeamMember.create!(params[:team_member]) }
redirect_to root_url
end
and the view:
<%= form_for(#team_member) do |f| %>
<%= f.fields_for :person do |ff| %>
<div>
<%= ff.label :first_name %>
<%= ff.text_field :first_name %>
</div>
<div>
<%= ff.label :last_name %>
<%= ff.text_field :last_name %>
</div>
<div>
<%= ff.label :phone_number %>
<%= ff.text_field :phone_number %>
</div>
<div>
<%= ff.label :email %>
<%= ff.text_field :email %>
</div>
<div>
<%= ff.label :company_id %>
<%= ff.text_field :company_id %>
</div>
<% end %>
<div class="field">
<%= f.label :team %><br />
<%= f.text_field :team %>
</div>
<div class="field">
<%= f.label :experience_level %><br />
<%= f.text_field :experience_level %>
</div>
<div class="field">
<%= f.label :qualification %><br />
<%= f.text_field :qualification %>
</div>
<div class="field">
<%= f.label :lead %><br />
<%= f.check_box :lead %>
</div>
<div class="actions">
<%= f.submit %>
</div>
UPDATED TeamMembersController (Solution thanks to the courtesy of Tiago)
def new
#team_member = TeamMember.new
#team_member.build_person
respond_to do |format|
format.html # new.html.erb
format.json { render json: #team_member }
end
end
def create
#team_member = TeamMember.create!(params[:team_member])
redirect_to root_url
end
To mass assign attributes in a nested form, you'll need to specify:
class TeamMember < ActiveRecord::Base
has_many :project_team_members
has_many :projects, through: :project_team_members
has_one :person, as: :profile, dependent: :destroy
:experience_level, :lead, :qualification, :team #what is this line doing??
accepts_nested_attributes_for :person
attr_accessible :person_attributes
end
EDIT:
In the action called before the form you need to build person. Like:
#team_member = TeamMember.new
#team_member.build_person
Then you'll have one person (non-persisted) associated with #team_member.
I want to create a nested form with relations between Clientes has_many enderecos, but in my form nothing is show up.
Look my models:
class Cliente < ActiveRecord::Base
has_many :enderecos, dependent: :destroy
validates :nome, :sexo, presence: true
validates :cpf, :email, :username, presence:true, uniqueness: true
has_secure_password
accepts_nested_attributes_for :enderecos
end
And endereco:
class Endereco < ActiveRecord::Base
belongs_to :cliente
end
And my form:
<%= form_for(#cliente) do |f| %>
<div class="field">
<%= f.text_field :nome, placeholder: 'Nome completo', size: '50px' %>
</div>
<% f.fields_for :endereco do |endereco_form| %>
<div class="field">
<%= endereco_form.label :cep, 'placeholder: ' %>
<%= endereco_form.text_field :cep, placeholder: 'CEP' %>
</div>
<div class="field">
<%= endereco_form.text_field :numero, placeholder: 'Número' %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
But inside nested part nothing is show up, what's happening?
You forgot the '=' sign:
<%= f.fields_for :endereco do |endereco_form| %>
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