Rails update attribute when dealing with through relationships - ruby-on-rails

I’m trying to update an attribute on a show page, keeping it in the show page, that is connected through a different table. For example: manager logs into admin site and sees several resources, clicks on one and can see all users that have access to the resource (either approved or pending). They can currently delete a user’s access. What I’m trying to do is allow the manager to change the status of the request say from pending to approved.
So for my models I have the following:
class TrainingResource
has_many :user_training_resources, dependent: :destroy
end
class UserTrainingResource
belongs_to :user
belongs_to :training_resource
enum status: [:pending, :confirmed, :rejected]
end
class Users
has_many :user_training_resources, dependent: :destroy
has_many :training_resources, through: :user_training_resources
end
TrainingResourcesController
class Admin::TrainingResourcesController < Admin::ApplicationController
belongs_to_app :training_resources
add_breadcrumb 'Training Resources', :admin_training_resources_path
before_action :load_training_resource, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def index
#training_resources = TrainingResource.paginate(page: params[:page])
#training_resources = #training_resources.search(params[:search]) if params[:search]
respond_with(#training_resources)
end
def show
respond_with #training_resource
end
def new
#training_resource = TrainingResource.new
respond_with(#training_resource)
end
def create
#training_resource = TrainingResource.new(training_resource_params)
flash[:notice] = 'TrainingResource created successfully' if #training_resource.save
respond_with #training_resource, location: admin_training_resources_path
end
def edit
respond_with(#training_resource)
end
def update
flash[:notice] = 'TrainingResource updated successfully' if #training_resource.update(training_resource_params)
respond_with #training_resource, location: admin_training_resources_path
end
def destroy
flash[:notice] = 'TrainingResource deleted successfully' if #training_resource.destroy
respond_with #training_resource, location: admin_training_resources_path
end
private
def load_training_resource
#training_resource = TrainingResource.find_by!(id: params[:id])
end
def training_resource_params
params.require(:training_resource).permit(:name, :description, :total_subscriptions, :url)
end
end
UserTrainingResourcesController, which is pointing to the TrainingResourcesController
class Admin::UserTrainingResourcesController < Admin::ApplicationController
belongs_to_app :training_resources
add_breadcrumb 'Training Resources', :admin_training_resources_path
before_action :load_training_resource
respond_to :html, :json
def edit
respond_with #user_training_resource
end
def update
flash[:notice] = 'UserTrainingResource updated successfully' if #user_training_resource.update(user_training_resource_params)
respond_with #user_training_resource, location: admin_training_resources_path
end
def destroy
flash[:notice] = 'UserTrainingResource deleted successfully' if #user_training_resource.destroy
respond_with #user_training_resource, location: admin_training_resources_path
end
private
def load_training_resource
#user_training_resource = UserTrainingResource.find_by!(id: params[:id])
end
def user_training_resource_params
params.require(:user_training_resources).permit(
:training_resources_id, :status).merge(user_id: current_user_id)
end
end
Training Resource Show
<tbody>
<% #training_resource.users.each do |training| %>
<tr>
<td><%= training.full_name %></td>
<% utr = training.user_training_resources.where(training_resource: #training_resource).first %>
<td><%= utr.status.capitalize %>
<%= form_tag '/user_training_resource/edit', :method => :get do %>
<%= select_tag( :user_training_resources_id, options_for_select(['pending', 'confirmed', 'rejected']))%>
<%= submit_tag 'Edit Status', class: 'btn btn-default btn-sm' %>
<% end %>
<%= tb_form_for [:admin, #training_resource], :remote => true, :data => {:errors => :inline, :success => admin_user_training_resources_path} do |f| %>
<%= tb_form_errors(f.object, :base) %>
<%= f.tb_select :name, options_for_select(holder, :status) %>
<%= f.tb_save_buttons('User Training Resource', admin_user_training_resources_path) %>
<% end %>
</td>
<td class="table-actions">
<%= link_to 'Edit', edit_admin_user_training_resource_path(training), :class => 'btn btn-default btn-sm' %>
<%= link_to 'Delete', admin_user_training_resource_path(training), :method => :delete, :data => {:confirm => 'Are you sure you want to delete this?'}, :class => 'btn btn-danger btn-sm' %>
</td>
</tr>
<% end %>
</tbody>
User Training Resource Helper, Holder Method
def holder
TrainingResource.all.each(&:id)
end
The link to Edit I’ll end up taking out because it pushes the user to the UserTrainingResource edit page and I want to keep it on the current page. The top form with the select_tag isn’t actually reading in the current status and then the submit_tag is redirecting the page.
My latest attempt is the bottom form_for. This is pulling in the TrainingResource and not the UserTrainingResource data. When I change the tb_select to :status, options_for_select(holder, :status) I end up with undefined method `status’. It looks like it’s only aiming at TrainingResource. I’ve also tried:
Thought here that it would pull in the default status and then allow the options to change. Still have an issue with undefined method of ‘status’ and even then there’s the issues with save.
I’ve also tried:
In this case I end up with undefined method ‘map’. Which I've tried to pinpoint down with attempting pluralization on #training_resource and #user_training_resource but that turns up First argument in form cannot contain nil or be empty.
Edit:
Tried in the UserTrainingResourcesController:
def set_to_confirmed
#user_training_resource = UserTrainingResource.find(params[:user_training_resource])
end
Then in the show
<%= link_to 'Confirmed', {:controller => 'user_training_resources', :action => 'set_to_confirmed', :status => #training_resource.user_training_resource }, :class => 'btn btn-default btn-sm'%>
Although no errors on the page load when clicking on the link I get:
Requested URL: https://localhost:3000/admin/set_to_confirmed?status=%23%3CUserTrainingResource%3A%3AActiveRecord_Associations_CollectionProxy%3A0x00007f93ee8b0f90%3E
Latest attempt using the following:
<% #training_resource.spud_users.each do |training| %>
<tr>
<td><%= training.full_name %></td>
<% utr = training.user_training_resources.where(training_resource: #training_resource).first %>
<td>
<%= tb_form_for [:admin, #training_resource], :remote => true, :data => {:errors => :inline, :success => admin_training_resources_path} do |f| %>
<%= f.select(:user_training_resource_id, options_for_select(['pending', 'confirmed', 'rejected']), :selected => utr.status)%>
<%= f.tb_save_buttons('', admin_training_resources_path) %>
<% end %>
</td>
This will show only the options, which is good, but I need it to default to what's current in the database and the save isn't sticking.

If I get the point, you need to update the joining table UserTrainingResource changing the status column.
I probably would create three actions in Admin::UserTrainingResourcesController:
def set_to_pending
end
def set_to_confirmed
end
def set_to_rejected
end
Then I would add three link to each row in the table in TrainingResource#Show passing the necessary parameters to complete the action, like explained in this post: pass parameter by link_to ruby on rails

Related

Nested routes - rails 5.2.4 - Undefined local variable or method

To register addresses for clients in my application I am using nested routes, however when clicking on the link to register a new address or to edit an existing address the system presents the error:
undefined local variable or method `address' for #<##Class:0x00007fd1c815fa58>:0x00007fd1d05ef598>
Did you mean? #address.
Can someone with a good heart inform me where the error is or what is missing that I haven't been able to see yet?
Here is the application code:
routes.rb
resources :clients do
resources :addresses
end
models/addresses.rb
class Address < ApplicationRecord
belongs_to :client
end
models/clients.rb
class Client < ApplicationRecord
has_many :addresses
end
controllers/addresses_controller.rb
class Backoffice::AddressesController < BackofficeController
before_action :set_client
before_action :set_address, only: [:edit, :update, :destroy]
def new
#address = #client.addresses.build
end
def create
#address = #client.addresses.build(params_address)
if #address.save
redirect_to backoffice_addresses_path, notice: "Endereço cadastrado com sucesso!"
else
render :new
end
end
def edit
end
def update
if #address.update(params_address)
redirect_to backoffice_client_addresses_path(#client), notice: "#{#client.name} alterado com sucesso!"
else
render :new
end
end
private
def set_client
#client = Client.find(params[:client_id])
end
def set_address
#address = #client.addresses.find(params[:id])
end
def params_address
params.require(:address).permit(:nickname,
:cep,
:street,
:number,
:district,
:city,
:state,
:client_id)
end
end
views/addresses/index.html.erb
<%= link_to new_backoffice_client_address_path(#client)" do %>
Novo
<% end %>
<% #client.addresses.each do |address| %>
<tr>
<td><%= address.nickname %></td>
<td> ... </td>
<%= link_to edit_backoffice_client_address_path(#client, address) do %>
Editar
<% end %>
<% end %>
views/addresses/edit.html.erb
<%= render partial: "addresses/shared/form" %>
views/addresses/shared/_form.html.erb
<%= form_with(model: [#client, address], local: true) do |f| %>
...
<% end %>
<%= form_with(model: #address, url: [#client, #address], local: true) do |f| %>
…
<% end %>
According to the docs, the values for url are "Akin to values passed to url_for or link_to", so using an array should work.
This also works with shallow nesting since the array is compacted.

Rails form drop down select data from database pulled from a different table

I have three models that are connected like the following
Users
has_many :training_resources, through: :user_training_resources
has_many :user_training_resources, dependent: :destroy
TrainingResources
has_many :user_training_resource, dependent: :destroy
has_many :users, through: :user_training_resource
UserTrainingResources
belongs_to :user
belongs_to :training_resource
I'm trying to build out a form on my TrainingResource show page to allow the user to change the status of a UserTrainingResource and it's failed pretty miserably.
This is the latest attempt:
<% #training_resource.spud_users.each do |training| %>
<tr>
<td><%= training.full_name %></td>
<% utr = training.user_training_resources.where(training_resource: #training_resource).first %>
<td>
<%= tb_form_for [:admin, #training_resource], :remote => true, :data => {:errors => :inline, :success => admin_training_resources_path} do |f| %>
<%= f.collection_select :user_training_resource_ids, #training_resource, :id, :name %>
<%= f.tb_save_buttons('', admin_training_resources_path) %>
<% end %>
</td>
<td class="table-actions">
<%= link_to 'Delete', admin_user_training_resource_path(training), :method => :delete, :data => {:confirm => 'Are you sure you want to delete this?'}, :class => 'btn btn-danger btn-sm' %>
</td>
</tr>
<% end %>
The delete works fine but the select at the moment errors out with: undefined method 'map'.
I've also tried:
<%= f.select(:user_training_resource_ids, UserTrainingResource.pluck(:status, :id))%>
No error on the page but the options displayed appear to display the current status for ALL UserTrainingResource. Plus it's not actually saving a change.
I've tried:
<%= f.select(:user_training_resource_ids, options_for_select(['pending', 'confirmed', 'rejected'], selected: utr.status))%>
This actually does present the options because I'm being explicit and it's actually showing the current status. However it's not saving when a selection is made.
Is this just a simple issue of a wrong route? Is there a better way to select data from a different table in a rails drop down?
Edit:
Here's the UserTrainingResources Controller. It's pointing to the TrainingResources Controller's Admin:
class Admin::UserTrainingResourcesController < Admin::ApplicationController
belongs_to_app :training_resources
add_breadcrumb 'Training Resources', :admin_training_resources_path
before_action :load_training_resource, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def new
#user_training_resource = UserTrainingResource.new
respond_with #user_training_resource
end
def edit
respond_with #user_training_resource
end
def show
respond_with #user_training_resource
end
def update
flash[:notice] = 'UserTrainingResource updated successfully' if #user_training_resource.update(user_training_resource_params)
respond_with #user_training_resource, location: admin_training_resources_path
end
def destroy
flash[:notice] = 'UserTrainingResource deleted successfully' if #user_training_resource.destroy
respond_with #user_training_resource, location: admin_training_resources_path
end
private
def load_training_resource
#user_training_resource = UserTrainingResource.find_by!(id: params[:id])
end
def user_training_resource_params
params.require(:user_training_resources).permit(
:training_resources_id, :status).merge(user_id: current_user_id)
end
end
Another edit: Routes
I feel like this is an issue with saving to the correct place so I've included below the routes for user_training_resources:
get '/user_training_resources' => 'user_training_resources#index'
delete '/user_training_resources/:id' => 'user_training_resources#destroy', as: :destroy_utr
put '/user_training_resources/:id' => 'user_training_resources#update', as: :update_utr
The issue is actually the form_for being passed to the wrong url. Changed it to reflect a new path url: admin_update_utr_path(utr)
It may well be the route - the route for the form using the POST or PATCH (not sure which) method while your route is expecting a PUT method.
To confirm, check your console to see if the route is resolving to the correct controller action. If not, you can either change the routes to be like:
post '/user_training_resources/:id' => 'user_training_resources#update', as: :update_utr
or
patch '/user_training_resources/:id' => 'user_training_resources#update', as: :update_utr
I think you need to set "multiple" because #training_resource can has many "user_training_resource_ids"
try this :
<%= f.collection_select :user_training_resource_ids, current_user.user_training_resources,
:id, :name,
{:prompt => "Please select training resources"},
{:multiple => true} %>
source : https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select

Rails 4 : Table Boolean Column Update Using "link_to "with a specific value "TRUE" always

In my customer controller the update method code is like bellow:
def update
#customer= Customer.find(params[:id])
if #customer.update_attributes(customer_params)
redirect_to customers_path
else
render 'edit'
end
end
In my view in customers index page I am planning to add a "link_to" link, if it is clicked, then that particular customers field "doc_delete" should be updated with value "TRUE".
<td><%= link_to "[Update", *************what is here ?******** method: :put %></td>
You can pass hidden params through button_to:
<%= button_to "Update", user, method: :put, params: { doc_delete: true } %>
This will create a micro-form, much like what Marwen alluded to. Whilst quite inefficient, it will be the best way to send data to your update action.
--
Another, more efficient, way would be to define a custom route/action:
#config/routes.rb
resources :customers do
patch :doc_delete, on: :member #-> url.com/users/:id/doc_delete
end
#app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def doc_delete
#customer = Customer.find params[:id]
redirect_to customers_path if #customer.update doc_delete: true
end
end
#app/views/customers/index.html.erb
<% #customers.each do |customer| %>
<%= link_to "Update", customer_doc_delete_path(customer) %>
<% end %>
You will need a form to do that for you
<% unless customer.doc_delete? %>
<%= form_for customer do |f| %>
<%= f.hidden_field_tag :doc_delete, true %>
<%= f.submit %>
<% end %>
<% end %>
Where to insert this form?
Well if you are rendering you costumers using:
<%=render #costumers %>
then you will add the form in the /customers/_customer.html.erb
If you are looping them manually:
<% #customers.each do |customer| %>
<%=customer.full_name %>
## Here you can add the form
<% end %>
An another way, you can use Ajax.
#app/views/customers/index.html.erb
<% #customers.each do |customer| %>
<% if !customer.doc_delete == true %>
<%= link_to "Update", customer_doc_delete_path(customer), remote: true %>
<% else %>
<%= Updated %>
<% end %>
<% end %>
#config/routes.rb
resources :customers do
patch :doc_delete, on: :member #-> url.com/customers/:id/doc_delete
end
#app/controllers/customers_controller.rb
class CustomersController < ApplicationController
def doc_delete
#customer = Customer.find params[:id]
if #customer.update doc_delete: true
respond_to do | format |
format.js {render :nothing => true}
end
end
end
end
In my index.html
<td>
<%= hidden_field_tag 'delete_present', :value => "present" %>
<%=link_to "[update]", customer_path(customer, :doc_delete => true), :method => :put, :confirm => "Are you sure?" %>
</td>
In my customer controller
def update
if params[:doc_delete].present?
#customer= Customer.find(params[:id])
#customer.doc_delete=true
#customer.save
redirect_to customers_path
else
#customer= Customer.find(params[:id])
if #customer.update_attributes(customer_params)
redirect_to customers_path
else
render 'edit'
end
end
end

Rails 4, how to update a model field from a different controller?

I am trying to update an invoice fields, when checking out in the carts controller. These must be present when checking out, or it should fail. However, I can't get it to update, much less validate them.
Here is my code:
cart show view:
<div class = "row">
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Customer: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Seller: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<%= submit_tag 'Complete', class: 'btn btn-success' %>
<% end %>
</div>
</div>
carts controller:
class CartsController < ApplicationController
def show
#invoice = current_invoice
#invoice_products = current_invoice.invoice_products
#customers = Customer.all
#employees = Employee.all
end
def checkout
current_invoice.customer_id = params[:customer_id]
current_invoice.employee_id = params[:employee_id]
current_invoice.save
redirect_to current_invoice
end
end
current_invoice is the current session's invoice, related to the cart. It redirects correctly, but doesn't update.
in the invoices controller:
def invoice_params
params.require(:invoice).permit(:invoice_number, :customer_id, :invoice_date, :invoice_status_id, :employee_id, invoice_products_attributes: [:id, :invoice_id, :product_id, :price, :tax, :discount, :value])
end
Can anyone please help me in identifying where I am going wrong? Could it be my approach is not even valid?
Thanks in advance
The type of functionality you're after is considered "business logic" and should be implemented in the model and called from the controller.
You can define a method in a model:
class Invoice < ActiveRecord::Base
def update_invoice(cust_id, emp_id)
if self.update_attributes(:customer_id => cust_id], :employee_id = emp_id])
puts "Success!
else
puts "Failed to update record. Handle the error."
end
end
You can call my_method from carts_controller.rb like this:
def update
# all your regular update logic here
# replace the bit of code that saves the cart with something like this:
respond_to do |format|
if(current_invoice.update_invoice(params[:customer_id], params[:employee_id])
if(#cart.update(cart_params))
format.html { redirect_to #activity, notice: 'Activity was successfully updated.' }
format.json { render :show, status: :ok, location: #activity }
else
format.html { render :edit }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
Also, note the use of update_attributes rather than save. Bear in mind that update_attributes will return false if you run into any problems updating (e.g. one or more validations failed). Don't confuse update_attributes with the singular update_attribute which updates a single field and will not run validations.
Finally got it.
current_invoice.update_attributes(customer_id: params[:invoice][:customer_id], employee_id: params[:invoice][:employee_id])
Also in view, changed location of form_tag:
<div class = "row">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Cliente: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Vendedor: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= submit_tag 'Completar', class: 'btn btn-success' %>
</div>
<% end %>
</div>
Could it be my approach is not even valid
Your approach is definitely valid, it's great that you're using sessions in this way.
I'd do it slightly differently:
#config/routes.rb
resource :cart, except: [:edit, :new, :create], path_names: { update: "checkout" }
This will give you the following paths:
#url.com/cart -> carts#show (here you can invoke a cart if one doesn't exist)
#url.com/cart/checkout #-> POST to "update" method in carts controller
#url.com/cart/ (method: :delete) -> DELETE to "destroy" cart (refresh)
--
#app/controllers/carts_controller.rb
class CartsController < ApplicationController
before_action :setup_cart
def show
#cart = current_cart #-> products called from this. I don't know how you're linking them
#customers = Customer.all
#employees = Employee.all
end
def update
#invoice = Invoice.find_or_create_by(id: current_card.id)
#invoice.update update_params
redirect_to cart_path
end
def destroy
current_cart = nil
redirect_to carts_path, notice: "Cart Cleared"
end
private
def setup_cart
current_cart ||= sessions[:cart]
end
def update_params
params.require(:cart).permit(:customer_id, :employee_id)
end
end
Now, to update the cart, you'll want to take note from MarsAtomic's answer. However it must be noted that naked params are not available in the model.
If you use update_attributes, or just plain update, you'll need to do the following:
#app/models/cart.rb
class Invoice < ActiveRecord::Base
has_many :products
belongs_to :employee
belongs_to :customer
#validations here
#callbacks here (what MarsAtomic refers to as business logic)
before_save :do_something, only: :update
private
def do_something
#something here
#params appended to current instance of object
#eg self.customer_id
end
end
I'd also go more succinct in your view:
#app/views/carts/show.html.erb
<div class = "row">
<%= form_tag cart_checkout_path, method: :patch do |f| %>
<% options = [["cliente", "customer"], ["vendedor", "employee"]] %>
<% options.each do |name, type| %>
<%= content_tag :strong, "#{name.titleize}:" %>
<%= collection_select :cart, eval(":#{type}_id"), instance_variable_get("##{type.pluralize}"), :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
<% end %>
<% content_tag :div, class: "col-lg-12 text-right" do %>
<%= submit_tag 'Completar', class: 'btn btn-success' %>
<% end %>
<% end %>
</div>

Storing Array of Checkbox Selections

I have an attribute called "features" in my application. In my form, "features" consists of a list of check boxes. The idea here is that users can check off which "features" apply to their post, and that list of features gets saved into the record.
I see the array being saved in my console ("features"=>{"Private bathroom"=>"1", "Elevator"=>"0", "Complimentary breakfast"=>"1", "Great view"=>"1", "Cable TV"=>"0", "Fireplace"=>"0", "Other (see description)"=>"0", "Sweet location"=>"0"}).
However... When I view the record, features returns nil. It doesn't seem to be saving the features array.
Code provided below. Any idea what I'm doing wrong here?
models/accommodation.rb
class Accommodation < ActiveRecord::Base
validates_presence_of :title, :description, :thing, :location
attr_accessible :photo_attributes, :title, :description, :thing, :borough, :location, :spaces, :price, :features
has_one :photo
has_many :requests
belongs_to :user
accepts_nested_attributes_for :photo, :allow_destroy => true
end
controllers/accommodation_controller.rb
class AccommodationsController < ApplicationController
before_filter :auth, :except => :show
uses_tiny_mce ( :options => {
:theme => 'advanced',
:theme_advanced_toolbar_location => 'top',
:theme_advanced_toolbar_align => 'left',
:theme_advanced_buttons1 => 'bold,italic,underline,image,bullist,numlist,separator,undo,redo',
:theme_advanced_buttons2 => '',
:theme_advanced_buttons3 => ''
})
def show
#accommodation = Accommodation.find(params[:id])
end
def new
#accommodation = current_user.accommodations.build
#accommodation.build_photo
end
def create
#accommodation = current_user.accommodations.build(params[:accommodation])
if #accommodation.save
flash[:notice] = "Successfully created your accommodation."
redirect_to #accommodation
else
render :new
end
end
def edit
#accommodation = Accommodation.find(params[:id])
end
def update
#accommodation = Accommodation.find(params[:id])
if #accommodation.update_attributes(params[:accommodation])
flash[:notice] = "Successfully updated accommodation."
redirect_to #accommodation
else
render :edit
end
end
def destroy
#accommodation = Accommodation.find(params[:id])
#accommodation.destroy
flash[:notice] = "Successfully destroyed accommodation."
redirect_to :inkeep
end
private
def auth
if current_user
if params[:action] != 'new' && params[:action] != 'create'
#accommodation = Accommodation.find(params[:id])
if #accommodation.user_id != current_user.id
flash[:notice] = "You don't own this accommodation!"
render :action => 'show'
end
end
return true
else
flash[:error] = "Please login first."
redirect_to :controller => 'sessions', :action => 'new'
end
end
end
views/accommodations/_form.html.erb
<%= form_for #accommodation, :html => {:multipart => true} do |f| %>
<%= f.error_messages %>
<p>
Title<br />
<%= f.text_field :title, :size => 60 %>
</p>
<p>
Description<br />
<%= f.text_area :description, :rows => 17, :cols => 75, :class => "mceEditor" %>
</p>
[...snip...]
<p>
<i>Featuring...</i>
<%= fields_for :features do |feature_fields| %>
<table>
<tr>
<td><%= feature_fields.check_box 'Private bathroom' %> Private bathroom</td>
<td><%= feature_fields.check_box 'Cable TV' %> Cable TV</td>
<td><%= feature_fields.check_box 'Complimentary breakfast' %> Complimentary breakfast</td>
</tr>
<tr>
<td><%= feature_fields.check_box 'Elevator' %> Elevator</td>
<td><%= feature_fields.check_box 'Fireplace' %> Fireplace</td>
<td><%= feature_fields.check_box 'Great view' %> Great view</td>
</tr>
<tr>
<td><%= feature_fields.check_box 'Sweet location' %> Sweet location</td>
<td><%= feature_fields.check_box 'Other (see description)' %> Other (see description)</td>
</tr>
</table>
<% end %>
</p>
[...snip...]
<% end %>
First, is the features array inside of your the accommodation hash in the params hash?
Second, there is no db column type which accepts an array, so you need to put
serialize :features
in the model. This will store the array as yaml in the db. You can also specify the data type as an argument to serialize() (probably Array in this case), but it's not always necessary.
I add the same problem today, it appears the form isn't properly built in the view.
Indeed, take a closer look at your params: params[:features] is outside params[:accomodation]
I simply added at the beginning of my create action:
params[:accomodation][:features] = params[:features]
And it works properly
What about your model? Do you have attr_accessible or attr_protected calls in there?

Resources