Form with nested resource: can't keep the association - ruby-on-rails

I have a problem with a form with nested resource. The data model is easy:
class Event < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
has_many :event_contents
end
class EventContent < ActiveRecord::Base
belongs_to :event
end
My form:
= simple_form_for([:admin, #event, #event.event_contents.new], remote: true) do |f|
.chat-form
.input-cont
= f.input :content, label: false, input_html: { class: 'form-control' }
.btn-cont
%span.arrow
= f.submit 'invia', class: 'btn blue icn-only'
The controller:
class Admin::EventContentsController < AdminController
def create
#event_content = EventContent.new event_content_params
#event_content.user_id = current_user.id if current_user
if #event_content.save
respond_to do |format|
format.js { render :nothing => true }
end
else
end
end
private
def event_content_params
params.require(:event_content).permit(
:content,
:event_id,
:user_id
)
end
end
When i submit the post in the params instead of event_id I have the event "slug"
pry(#<Admin::EventContentsController>)> params
=> {"utf8"=>"✓", "event_content"=>{"content"=>"uhm"}, "commit"=>"invia", "action"=>"create", "controller"=>"admin/event_contents", "event_id"=>"test-test-test"}
The record is created in the db, but event_id is nil so the association is broken.
Why instead of the event_id I have the event slug???
Update
The issue was the controller:
def create
#event = Event.find params[:event_id]
#event_content = #event.event_contents.build event_content_params
#event_content.user_id = current_user.id if current_user
if #event_content.save
respond_to do |format|
format.js
end
else
end
end

Maybe try doing the following:
in your Event model add accepts_nested_attributes_for :event_contents
in your form, when you are collecting the :event_contents replace
f.input :content, label: false, input_html: { class: 'form-control' }
with the following:
f.fields_for :event_contents do |content|
content.input :content
end
update your strong params in your Events controller to include {:event_contents_attributes} which might look something like below depending on the other params you need to pass through:
params.require(:event).permit(:name, {:event_contents_attributes => [:content]})
In your Events controller, update def new to include this line item event_content = #event.event_contents.build
All that to say, I believe you would want this to be routed to your Events controller and not your EventContents controller because the :event_contents are nested in the :event. It looks like your form is submitting to the EventContents controller currently. Also, I don't believe this argument in your simple_form_for #event.event_contents.new is necessary.
Here's what I built off of your question. It's not admin namespaced but might be helpful.
class EventsController < ApplicationController
def new
#event = Event.new
event_content = #event.event_contents.build
end
def create
#event = Event.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
format.js
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
private
def event_params
params.require(:event).permit(:name, {:event_contents_attributes => [:content]})
end
end
Event model:
class Event < ActiveRecord::Base
has_many :event_contents
accepts_nested_attributes_for :event_contents
end
And finally the form:
<%= form_for(#event) do |event| %>
<div class="field">
<%= event.label :name %><br>
<%= event.text_field :name %>
</div>
<%= event.fields_for :event_contents do |content| %>
<div class="field">
<%= content.label :content %>
<%= content.text_field :content %>
</div>
<% end %>
<div class="actions">
<%= event.submit %>
</div>
<% end %>

Related

simple_form not showing nested field

I am trying simple_form nested attributes as suggested in https://github.com/plataformatec/simple_form/wiki/Nested-Models
The thing is, when I render the form I just can see the submit button, but not the input field. What am I doing wrong?
_form.html.erb
<%= simple_form_for [:admin, #incident] do |f| %>
<%= f.error_notification %>
<%= f.simple_fields_for :comments do |builder| %>
<%= builder.input :info, label: "Informe de seguimiento" %>
<% end %>
<div class="form-actions">
<%= f.submit "Enviar", class: "btn btn-primary" %>
</div>
<% end %>
incidents_controller.rb
class Admin::IncidentsController < ApplicationController
before_action :set_incident, only: [:show, :edit, :update]
def index
#incidents = Incident.all
end
def show
end
def new
#incident = Incident.new
#incident.comments.build
end
def edit
end
def update
respond_to do |format|
if #incident.update(incident_params)
format.html { redirect_to #incident, notice: 'Incidencia actualizada actualizada con éxito.' }
format.json { render :show, status: :ok, location: #incident }
else
format.html { render :edit }
format.json { render json: #incident.errors, status: :unprocessable_entity }
end
end
end
private
def set_incident
#incident = Incident.find(params[:id])
end
def incident_params
params.require(:incident).permit(:info, :subject, :status, comments_attributes: [:info])
end
end
incident.rb
class Incident < ApplicationRecord
belongs_to :user, optional: true
has_many :comments, dependent: :destroy
accepts_nested_attributes_for :comments, allow_destroy: true, reject_if: proc { |attributes| attributes['info'].blank? }
enum status: [:abierto, :tramite, :pendiente, :cerrado]
after_initialize :set_default_status, :if => :new_record?
def set_default_status
self.status ||= :abierto
end
end
comment.rb
class Comment < ApplicationRecord
belongs_to :user, optional: true
belongs_to :incident
end
You need to add #incident.comments.build to the show action of Admin::IncidentsController. Now it has no comments, I suppose, so the form is empty.
And you need to add :id to comments_attributes, without it comment can't be saved. If you planning to have some 'Delete' checkbox for existing comments you also need to add :_destroy to the attributes array

Rails - Nested Form Not Saving

I have problem and I can't figure out how to solve this, I tried trace every character on codes but I think I didn't get mistake on code
I have 3 models : Product, AttributeProduct and AttributeProductRelation, and relations :
class Product < ActiveRecord::Base
has_many :attribute_product_relations
has_many :attribute_products, through: :attribute_product_relations
accepts_nested_attributes_for :attribute_product_relations, allow_destroy: true
end
class AttributeProduct < ActiveRecord::Base
has_many :attribute_product_relations
has_many :products, through: :attribute_product_relations
end
class AttributeProductRelation < ActiveRecord::Base
belongs_to :product
belongs_to :attribute_product
end
And on ProductsController I have this :
def new
#product = Product.new(id: generate_product_id)
2.times { #product.attribute_product_relations.build }
end
def create
#store = current_user.store
#product = #store.products.build(new_product_params)
respond_to do |format|
if #product.save
format.html { redirect_to red_path, notice: I18n.t('notice.create_product_success') }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
private
def new_product_params
params.require(:product).permit(:id, :name :description, attribute_product_relations_attributes: [:id, :product_id, :attribute_product_id, :value])
end
And this is a form :
<%= nested_form_for #product, html: { class: "form-horizontal" } do |f| %>
<%#= order stuff here %>
<%= f.fields_for :attribute_product_relations do |attribute_product_relation_form| %>
<div class="row">
<div class="col-xs-6 col-md-6">
<%= attribute_product_relation_form.hidden_field :attribute_product_id, value: 1 %>
<label class="control-label">size</label>
</div>
<div class="col-xs-6 col-md-6">
<%= attribute_product_relation_form.text_field :value, {class: "form-control text select_short" %>
</div>
</div>
<% end %>
<% end %>
And this is a log when I submit a form :
Started POST "/products" for 127.0.0.1 at 2015-10-09 16:11:51 +0700
Processing by ProductsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"WzSKpXm4AFhETrPyv0Ez/WohxoAMMiiRRc6CNhgIPC8=", "product"=>{"id"=>"2132051", "name"=>"T-shirt", "attribute_product_relations_attributes"=>{"0"=>{"attribute_product_id"=>"1", "value"=>"Medium"}, "1"=>{"attribute_product_id"=>"1", "value"=>"SMALL"}} , "description"=>"cool t-shirt"}, "button"=>""}
You can see attribute_product_relations_attributes are exists on parameter, So I'm use pry-debugger gems to trace new_product_params method and I just get this :
[1] pry(#<ProductsController>)> new_product_params
=> {"id"=>"2132051",
"name"=>"T-shirt"
"description"=>"cool t-shirt"}
I can't see attribute_product_relations_attributes on new_product_params and didn't save to database.
As you are have has many relationship in new_product_params methods, it is required to make permit for array of attribute_product_relations_attributes. So, your should refactor new_product_params method to
def new_product_params
params.require(:product).permit(:id, :name :description, attribute_product_relations_attributes: [ [:id, :product_id, :attribute_product_id, :value] ])
end
I think this will help.

(How) Can I use a form object for the edit/update routines?

Couldn't resist to try out the relaunched RailsForum and crossposted this question here.
I have the following form object (in Rails 4)
class StationForm
include Virtus
include ActiveModel::Model
# I think this is unnecessary
# extend ActiveModel::Naming
# include ActiveModel::Conversion
# include ActiveModel::Validations
# Station
attr_reader :station
attribute :station_name, String
attribute :station_description, String
# Address
attr_reader :address
attribute :address_url, String
attribute :address_priority, Integer
attribute :address_is_active, Boolean
def persisted?
false
end
def save
if valid?
persist
true
else
false
end
end
private
def persist
#station = Station.create(name: station_name, description: station_description)
#address = #station.addresses.create(url: address_url, priority: address_priority, is_active: address_is_active)
end
end
I can use this form object in my new/create methods
class StationsController < ApplicationController
def new
#station_form = StationForm.new
end
def create
#station_form = StationForm.new(station_form_params)
#station_form.save
redirect_to station_path(#station)
end
private
def station_form_params
params.require(:station_form).permit(:station_name, :station_description, :address_url, :address_priority, :address_is_active)
end
end
However, I don't succeed to use it for the edit/update procedures...
Is it possible to use the form object for edit/update and if yes, how would this be done?
You will have to use "initialize" method in StationForm object to use it for editing. if you pass an id, it will assume, that the object already exist and from there we can treat it as an persisted object.
Also Add "update" method to update attributes of object.
class StationForm
include Virtus.model
include ActiveModel::Model
# I think this is unnecessary
# extend ActiveModel::Naming
# include ActiveModel::Conversion
# include ActiveModel::Validations
attr_reader :station
attribute :station_name, String
attribute :station_description, String
attr_reader :address
attribute :address_url, String
attribute :address_priority, Integer
attribute :address_is_active, Boolean
def initialize(attr = {})
if !attr["id"].nil?
#station = Station.find(attr["id"])
#address = #station.addresses.first
self[:station_name] = attr[:station_name].nil? ? #station.name : attr[:station_name]
self[:station_description] = attr[:station_description].nil? ? #station.description : attr[:station_description]
self[:address_url] = attr[:address_url].nil? ? #address.url : attr[:address_url]
self[:address_priority] = attr[:address_priority].nil? ? #address.priority : attr[:address_priority]
self[:address_is_active] = attr[:address_is_active].nil? ? #address.is_active : attr[:address_is_active]
else
super(attr)
end
end
def persisted?
#station.nil? ? false : #station.persisted?
end
def id
#station.nil? ? nil : #station.id
end
def save
if valid?
persist
true
else
false
end
end
def update
if valid?
update_form
true
else
false
end
end
private
def persist
#station = Station.create(name: station_name, description: station_description)
#address = #station.addresses.create(url: address_url, priority: address_priority, is_active: address_is_active)
end
def update_form
#station.update_attributes(
:name => self[:station_name],
:description => self[:station_description]
)
#address.update_attributes(
:url => self[:address_url],
:priority => self[:address_priority],
:is_active=> self[:address_is_active]
)
end
end
And Controller will be like
def new
#station = StationForm.new
end
def edit
#station = StationForm.new("id" => params[:id])
end
def create
#station = StationForm.new(station_params)
respond_to do |format|
if #station.save
format.html { redirect_to stations_path, notice: 'Station was successfully created.' }
format.json { render :show, status: :created, location: #station }
else
format.html { render :new }
format.json { render json: #station.errors, status: :unprocessable_entity }
end
end
end
def update
#station = StationForm.new(station_params.merge("id" => params[:id]))
respond_to do |format|
if #station.update
format.html { redirect_to stations_path, notice: 'Station was successfully updated.' }
format.json { render :show, status: :ok, location: #station }
else
format.html { render :edit }
format.json { render json: #station.errors, status: :unprocessable_entity }
end
end
end
use the "persisted" and "id" method from StationForm in the _form.html.erb
<%= form_for(#station,
:url => #station.persisted? ? station_path(#station.id) : stations_path,
:method => #station.persisted? ? "put": "post") do |f| %>
<% if #station.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#station.errors.count, "error") %> prohibited this station from being saved:</h2>
<ul>
<% #station.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :station_name %><br>
<%= f.text_field :station_name %>
</div>
<div class="field">
<%= f.label :station_description %><br>
<%= f.text_field :station_description %>
</div>
<div class="field">
<%= f.label :address_url%><br>
<%= f.text_field :address_url %>
</div>
<div class="field">
<%= f.label :address_priority%><br>
<%= f.text_field :address_priority%>
</div>
<div class="field">
<%= f.label :address_is_active %><br>
<%= f.text_field :address_is_active %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>

process collection_select on controller update actions

I have 2 models
The first model category.rb
class Category
include Mongoid::Document
# Relationships
has_many :boards, :dependent => :destroy , :autosave => true
accepts_nested_attributes_for :boards
#fields
field :name
#attr
attr_accessible :name, :boards_attributes
end
The second model its board.rb
class Board
include Mongoid::Document
#Relationships
belongs_to :category
#fields
field :name
field :description
#attr
attr_accessible :name, :description
end
I have in edit board view the next form:
<%= form_for [#board], :url => user_board_path do |f| %>
<%= f.text_field :name %>
<%= f.text_area :description, :cols =>72, :rows => 5, %>
<%= f.collection_select :category_id, Category.all, :id, :name%>
<% end %>
and I have in update action from boards_controller.rb the next:
def update
#board = Board.find(params[:id])
#category = Category.find(params[:category_id])
#board.category_id = #category
respond_to do |format|
if #board.update_attributes(params[:board])
format.html { redirect_to user_board_path(#board.user, #board), notice: 'Board was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #board.errors, status: :unprocessable_entity }
end
end
end
Why I get the #board.category_id nil? I want update the #board.category_id with the value that I choose in select
The problem is #board.category_id = #category (you set an Object to an id field). It should be
#board = Board.find(params[:id])
#category = Category.find(params[:category_id])
#board.category = #category
or if #category is not used in the controller nor in the view, you could write
#board = Board.find(params[:id])
#board.category_id = params[:category_id]
The second solution removes a "SELECT" request on the Categories

Why do I get a AssociationTypeMismatch when creating my model object?

I get the following error:
ActiveRecord::AssociationTypeMismatch in ContractsController#create
ExchangeRate(#2183081860) expected, got HashWithIndifferentAccess(#2159586480)
Params:
{"commit"=>"Create",
"authenticity_token"=>"g2/Vm2pTcDGk6uRas+aTgpiQiGDY8lsc3UoL8iE+7+E=",
"contract"=>{"side"=>"BUY",
"currency_id"=>"488525179",
"amount"=>"1000",
"user_id"=>"633107804",
"exchange_rate"=>{"rate"=>"1.7"}}}
My relevant model is :
class Contract < ActiveRecord::Base
belongs_to :currency
belongs_to :user
has_one :exchange_rate
has_many :trades
accepts_nested_attributes_for :exchange_rate
end
class ExchangeRate < ActiveRecord::Base
belongs_to :denccy, :class_name=>"Currency"
belongs_to :numccy, :class_name=>"Currency"
belongs_to :contract
end
My view is:
<% form_for #contract do |contractForm| %>
Username: <%= contractForm.collection_select(:user_id, User.all, :id, :username) %> <br>
B/S: <%= contractForm.select(:side,options_for_select([['BUY', 'BUY'], ['SELL', 'SELL']], 'BUY')) %> <br>
Currency: <%= contractForm.collection_select(:currency_id, Currency.all, :id, :ccy) %> <br> <br>
Amount: <%= contractForm.text_field :amount %> <br>
<% contractForm.fields_for #contract.exchange_rate do |rateForm|%>
Rate: <%= rateForm.text_field :rate %> <br>
<% end %>
<%= submit_tag :Create %>
<% end %>
My View Controller:
class ContractsController < ApplicationController
def new
#contract = Contract.new
#contract.build_exchange_rate
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #contract }
end
end
def create
#contract = Contract.new(params[:contract])
respond_to do |format|
if #contract.save
flash[:notice] = 'Contract was successfully created.'
format.html { redirect_to(#contract) }
format.xml { render :xml => #contract, :status => :created, :location => #contract }
else
format.html { render :action => "new" }
format.xml { render :xml => #contract.errors, :status => :unprocessable_entity }
end
end
end
I'm not sure why it's not recognizing the exchange rate attributes?
Thank you
The problem is that accepts_nested_attributes_for :exchange_rate looks for "exchange_rate_attributes" in the params, not "exchange_rate". The fields_for helper will do this for you, but you have to change it to:
<% contractForm.fields_for :exchange_rate do |rateForm|%>

Resources