Hi im trying to create a relation one-to-many in my rails app.
Fist i create my models
class Produto < ActiveRecord::Base
attr_accessible :compra, :descricao, :estoque, :venda
has_many :precos
accepts_nested_attributes_for :precos
end
class Preco < ActiveRecord::Base
attr_accessible :compra_decimal, :produto_id, :venda_decimal
belongs_to :produto
end
Then i created my controller
class ProdutosController < ApplicationController
def new
#produto = Produto.new
#produto.precos.build
end
def create
#produto = Produto.new(params[:produto])
if #produto.save?
redirect_to produtos_path
end
end
end
After this i created my .html.erb pages:
_form
<%= form_for #produto do |f| %>
<p>
<%= f.label :descricao %><br/>
<%= f.text_field :descricao %>
</p>
<p>
<%= f.label :compra %><br/>
<%= f.text_field :compra %>
</p>
<p>
<%= f.label :venda %><br/>
<%= f.text_field :venda %>
</p>
<p>
<%= f.label :estoque %><br/>
<%= f.text_field :estoque %>
</p>
<%= f.fields_for :precos do |builder| %>
<%= render "precos", :f => builder %>
<% end %>
<p><%= f.submit %></p>
<% end %>
_precos
<p>
<%= f.label :venda_decimal %><br/>
<%= f.text_field :venda_decimal %>
</p>
<p>
<%= f.label :compra_decimal %><br/>
<%= f.text_field :compra_decimal %>
</p>
new
<%= render "form" %>
then, when i submit the form this error appears:
ActiveModel::MassAssignmentSecurity::Error in ProdutosController#create
Can't mass-assign protected attributes: precos_attributes
does anyone have any idea about it?
Just change your model:
class Produto < ActiveRecord::Base
attr_accessible :compra, :descricao, :estoque, :venda, :precos_attributes
has_many :precos
accepts_nested_attributes_for :precos
end
Related
I have an Orders model that has many Items and Customers. For some reason, and I have looked high and low for a solution, I am unable to save the attributes for Items and Customers in their respected tables when using a nested form.
I'm relatively new to Rails, so my apologies if I've missed something obvious.
Started POST "/orders" for ::1 at 2017-05-16 16:12:17 -0600 Processing
by OrdersController#create as HTML Parameters: {"utf8"=>"✓",
"authenticity_token"=>"k1QPQ560j44Mx0SNeCMBQAUGLQfEi4g0QpDfHuYeP4Zd7nVgcHJf3qXNsVQv29dV+5G0oJ7wiaoQ3Idw+DP+iw==",
"order"=>{"customer"=>{"name"=>"Test", "email"=>"Test",
"phone"=>"Test"}, "item"=>{"name"=>"Test", "ref_num"=>"Test",
"retail"=>"122"}, "status"=>"Test", "arrival"=>"06/06/19",
"tracking"=>"Test439"}, "commit"=>"Save Order"} Unpermitted
parameters: customer, item
Orders Controller
def create
#order = Order.new(order_params)
#order.items.build
#order.customers.build
#order.save
redirect_to #order
end
private
def order_params
params.require(:order).permit(:status, :arrival, :tracking,
customers_attributes: [:name, :phone, :email],
items_attributes: [:name, :ref_num, :retail])
end
Orders Model
class Order < ApplicationRecord
has_many :items
has_many :customers
accepts_nested_attributes_for :items, :customers
end
Orders Form (New)
<%= form_for :order, url: orders_path do |f| %>
<%# customer parameters%>
<%= f.fields_for :customers do |cu| %>
<p>
<%= cu.label :customer_name %><br>
<%= cu.text_field :name %>
</p>
<p>
<%= cu.label :email %><br>
<%= cu.text_field :email %>
</p>
<p>
<%= cu.label :phone %><br>
<%= cu.text_field :phone %>
</p>
<% end %>
<%# Item parameters %>
<%= f.fields_for :items do |it| %>
<p>
<%= it.label :item_name %><br>
<%= it.text_field :name %>
</p>
<p>
<%= it.label :reference_number %><br>
<%= it.text_field :ref_num %>
</p>
<p>
<%= it.label :retail %><br>
<%= it.text_field :retail%>
</p>
<% end %>
<%# order parameters%>
<p>
<%= f.label :status %><br>
<%= f.text_field :status %>
</p>
<p>
<%= f.label :est_arrival %><br>
<%= f.text_field :arrival %>
</p>
<p>
<%= f.label :tracking %><br>
<%= f.text_field :tracking %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', orders_path %>
This is happening because form_for :order doesn't make reference to your model object, it makes a simple :order param, which doesn't takes into consideration your model's accepts_nested_attributes_for, thus not building f.fields_for :customers with _attributes suffix. You should instantiate a Order object for the expected behavior to happen in your form_for #object instead of just referencing a :object which renders only the name of the param.
I have the following models:
class Person < ApplicationRecord
has_many :interests, dependent: :destroy
accepts_nested_attributes_for :interests
validates_presence_of :email
validates_inclusion_of :gender, :in => %w(M F), message: "Gender can only be in M or F"
has_secure_password
def name
"#{first_name} #{last_name}"
end
def interests_concatenated
interests.map { |i| i.interest }.join(", ")
end
end
class Interest < ApplicationRecord
belongs_to :person
end
My controller is as follows:
class PeopleController < ApplicationController
def index
#person = Person.all
end
def new
#person = Person.new
#person.interests.build
end
def create
#person = Person.new(people_params)
if #person.save
session[:user_id] = #person.id
redirect_to(people_path)
else
flash = "Email or gender can't be blank!"
render 'new'
end
end
private
def people_params
params.require(:person).permit(:email, :first_name, :last_name, :gender, :password,:password_confirmation, interests_attributes: [:hobby])
end
end
My form is as follows:
<%= form_for #person do |f| %>
<p>
<%= f.label :email %> <br>
<%= f.text_field :email %>
</p>
<p>
<%= f.label :first_name %> <br>
<%= f.text_field :first_name %>
</p>
<p>
<%= f.label :last_name %> <br>
<%= f.text_field :last_name %>
</p>
<p>
<%= f.label :gender %> <br>
<%= f.label(:gender_male, "Male") %>
<%= f.radio_button(:gender, "M") %> <br>
<%= f.label(:gender_female, "Female") %>
<%= f.radio_button(:gender, "F") %> <br>
</p>
<p>
<%= f.label :password %> <br>
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %> <br>
<%= f.password_field :password_confirmation %>
</p>
<p>
<%= f.fields_for :interests do |i| %>
<%= i.label :hobby %>
<%= i.text_field :hobby %>
<% end %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Here is the byebug console log when I run it:
Very stumped why it's not working. Could it be something to do with the parameters?
Here is the log file when I submit the form:
Instead of:
#interests = #person.interests.new
try
#interests = #person.interests.build
new creates a fresh, clean, completely empty new object... but build is the special Rails association method that will fill it with appropriate defaults (like, eg the right person_id)
I found a working solution by adding this in my interests model:
class Interest < ApplicationRecord
belongs_to :person, **optional: true**
end
Since #person fails to save each time, the biggest clue was in the error message "Interest person must exist", I found this StackOverflow solution to be helpful. Also this blog post on why this is needed was helpful in shedding light on the issue.
Thanks to everyone that weighed in on it!
I have a model Event that has one Payoption, which is a STI model. Payoption could be BankPayoption, CashPayoption etc, each of them has totally different fields.
The models, Payoption just have string attributes:
class Event < ActiveRecord::Base
has_one :payoption
end
class Payoption < ActiveRecord::Base
belongs_to :event
end
class BankPayoption < Payoption
end
class CashPayoption < Payoption
end
Event controller:
class EventsController < ApplicationController
def new
end
def create
#event = Event.new(post_params)
#event.user_id = current_user.id
#event.save
redirect_to #event
end
private
def post_params
params.require(:event).permit(:title, :text, :code)
end
end
This is the new Event view:
<%= form_for :event, url: events_path do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.label :code %><br>
<%= f.text_field :code %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
This code works fine but it's obviously not creating a Payoption association, I'm not sure how to implement this in the current form_for. I want to be able to pick on of the Payoption types with a select element and then the correct fields should show. I know the field show/hide action is done by javascript but the real problem is, how do I make a nested form that creates the chosen subclass and associates that with the event object?
Thanks
very simple do it this way
class EventsController < ApplicationController
def new
#event = Event.new
#event.build_payoption
end
end
<%= form_for(#event) do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.label :code %><br>
<%= f.text_field :code %>
</p>
<%= f.fields_for :payoption do |p| %>
<%= p.label :payoption_type %>
<%= p.select(:payoption_type, Payoption::PAY_OPTION , {:prompt => "Select"}, {class: "payoption"}) %>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
class Event < ActiveRecord::Base
has_one :payoption, dependent: :destroy
accepts_nested_attributes_for :payoption
end
class Payoption < ActiveRecord::Base
belongs_to :event
PAY_OPTION = ["option1", "option2", "option3"]
end
m assuming payoption_type is a field in your Payoption model
This is my first question. I hope you can help me. I have two models.
class Cliente < ActiveRecord::Base
attr_accessible :cedula, :direccion, :nombres, :telefono
validates :cedula, :direccion, :nombres, :telefono, :presence => true
validates :cedula, :uniqueness => { :message => "Cedula ya en uso" }
has_many :facturas
class Factura < ActiveRecord::Base
attr_accessible :cliente_attributes, :iva, :numero, :subtotal, :total, :created_at
belongs_to :cliente
accepts_nested_attributes_for :cliente
I want in the facturas#new view can create or edit Cliente. If exists update or if not exists create. I am using nested attributes. If exists I uses javascript to fill text fields. If not exists I fill text field and save when Factura save. This is facturas#new view.
<h1>Nueva Factura</h1>
<%= form_for #factura do |f| %>
<% if #factura.errors.any? %>
<h2>Errores:</h2>
<ul>
<% #factura.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
<p>
<div class = "contenedor_factura">
<%= f.label :numero %><br />
<%= f.number_field :numero %><br />
<%= f.label :fecha %><br />
<%= f.date_select :created_at %><br />
</div>
<div class = "contenedor_cliente">
<%= f.fields_for #cliente do |builder| %>
<%= builder.label :cedula, "Cédula" %>
<%= builder.text_field :cedula %>
<%= builder.label :nombres, "Nombres" %>
<%= builder.text_field :nombres %>
<%= builder.label :direccion, "Dirección" %><br />
<%= builder.text_field :direccion %>
<%= builder.label :telefono, "Teléfono" %><br />
<%= builder.text_field :telefono %>
<%= builder.hidden_field :id%>
<% end %>
</div>
<div class = "contenedor_productos">
</div>
<%= f.label :subtotal %><br />
<%= f.text_field :subtotal %>
<br />
<%= f.label :iva %><br />
<%= f.text_field :iva %>
</p>
<p>
<%= f.submit "Agregar Nueva Factura" %>
</p>
<% end %>
When the Cliente is new i have no problem, it saves, but if Cliente exists i have this message
ActiveRecord::RecordNotFound in FacturasController#create
Couldn't find Cliente with ID=6 for Factura with ID=
What is my problem?
EDIT:
This is my FacturasController
def new
#factura = Factura.new
#cliente = #factura.build_cliente
end
def create
#factura = Factura.new(params[:factura])
if #factura.save
redirect_to facturas_path, :notice => "Factura Guardada"
else
render "new"
end
end
I'm not sure if nested_attrubes handles already created models. So we can just not depend on it.
This should work
def create
cliente_attrs = params[:factura].delete :cliente
#cliente = cliente_attrs[:id].present? ? Cliente.find(cliente_attrs[:id]) : User.create(user_attrs)
#factura = cliente.facturas.build(params[:factura])
if #factura.save
redirect_to facturas_path, :notice => "Factura Guardada"
else
render "new"
end
end
You can now delete the line accepts_nested_attributes_for :cliente
I have these two models
class Invoice < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :items
...
end
class Item < ActiveRecord::Base
belongs_to :invoice
def total
price * quantity
end
...
end
and this nested (!) form that posts to both models:
<h1>Add an Invoice</h1>
<%= form_for #invoice do |f| %>
<p>
<%= f.label :recipient %>
<%= f.text_field :recipient %> </p>
<p>
<%= f.label :date %>
<%= f.text_area :date %>
</p>
<h2>Items</h2>
<p>
<%= f.fields_for(:items) do |f| %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :price %>
<%= f.text_field :price %>
<%= f.label :quantity %>
<%= f.text_field :quantity %>
<%= f.label :total %>
<%= f.total %><!-- this method call is not working! -->
<% end %>
</p>
<%= f.submit %>
<% end %>
How can I do calculations on my items within the form?
In my Items model I have this method:
def total
price * quantity
end
However, in the form I can't get it to work with f.total. I keep getting this error:
undefined method `total' for #<ActionView::Helpers::FormBuilder:0x10ec05558>
What am I missing here?
You are calling a method not on your model object, but on f, which is a form helper (ActionView::Helpers::FormBuilder). Error message gives a hint to this.
To call on the item, you need to replace
<%= f.total %>
with
<%= f.object.total %>