Relationship models in rails - ruby-on-rails

Im associating models and im stuck i have my Restaurant controller
before_action :set_restaurant, only: [:show, :edit, :update, :destroy]
def new
#restaurant = Restaurant.new
#restaurant.build_chef
end
def create
#restaurant = Restaurant.new(restaurant_params)
respond_to do |format|
if #restaurant.save
format.html { redirect_to #restaurant, notice: 'Restaurant was successfully created.' }
format.json { render action: 'show', status: :created, location: #restaurant }
else
format.html { render action: 'new' }
format.json { render json: #restaurant.errors, status: :unprocessable_entity }
end
end
end
def set_restaurant
#restaurant = Restaurant.find(params[:id])
end
def restaurant_params
params.require(:restaurant).permit(:avatar, :name, :description,
chef_attributes: [ :avatar, :name ]
)
end
And this is my _form that im rendering in create.html.erb
<%= form_for(#restaurant, multipart: true) do |f| %>
<div class="field">
<%= f.label :restaurant_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :image %>
<%= f.file_field :avatar %>
</div>
<%= f.fields_for :chef do |f| %>
<div class="field">
<%= f.label :chef_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :image %>
<%= f.file_field :avatar %>
</div>
<% end %>
<div class="actions">
<%= f.submit 'Add new restaurant' %>
<%= link_to 'Nevermind', restaurants_path, class: 'button' %>
</div>
<% end %>
then this is my Chef model
class Chef < ApplicationRecord
belongs_to :restaurant
validates :name, presence: true
validates :avatar,
attachment_content_type: { content_type: /\Aimage\/.*\Z/ },
attachment_size: { less_than: 5.megabytes }
has_attached_file :avatar, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}
end
and this is my Restaurant model
class Restaurant < ApplicationRecord
has_one :chef
accepts_nested_attributes_for :chef
validates :name, presence: true
validates :avatar,
attachment_content_type: { content_type: /\Aimage\/.*\Z/ },
attachment_size: { less_than: 5.megabytes }
has_attached_file :avatar, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}
end
My problem is that when i save a new restauran, nothing happens just reloads and stay in the form, i dont know whats happening im stuck here, probably its just a dumb thing to do but im learning, thanks for the support.

It looks like your problem is related to this question. In Rails 5, there is an implicit validation on belongs_to associations, so you need to add required: false in order to build it from nested attributes.
class Chef < ApplicationRecord
belongs_to :restaurant, required: false
...
end

Related

carrierwave multiple uploader for single model

I have created a gallery module with the help of this tutorial https://kolosek.com/carrierwave-upload-multiple-images/ and now I want to add one more image attribute has a master image to the same model. So I thot of creating one more uploader but I'm bit confused about how to call that the controller can anybody help me out?
My code looks like this:
MOdel Code:
class Image < ApplicationRecord
belongs_to :gallery
mount_uploader :image, ImageUploader
mount_uploader :avatar, AvatarUploader
end
class Gallery < ApplicationRecord
has_many :images
accepts_nested_attributes_for :images
end
gallery_controller.rb
class GalleriesController < AdminController
def index
#galleries = Gallery.all
end
def show
#images = #gallery.images.all
end
def new
#gallery = Gallery.new
#image = #gallery.images.build
end
def create
#gallery = Gallery.new(gallery_params)
respond_to do |format|
if #gallery.save
params[:images]['image'].each do |a|
#images = #gallery.images.create!(:image => a, :gallery_id => #gallery.id)
end
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render :show, status: :created, location: #gallery }
else
format.html { render :new }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def gallery_params
params.require(:gallery).permit(:title, :details, :status, images_attributes:[:id, :gallery_id, :image, :avatar])
end
Image Controller code:
class ImagesController < AdminController
def image_params
params.require(:image).permit(:gallery_id, :slideshow_id,:image, :avatar)
end
end
form.html.erb
<%= form.fields_for :images do |p| %>
<div class="field">
<%= form.label :master_image, class: "col-2 col-form-label" %>
<%= form.file_field :avatar, :multiple => true, name: "images[avatar][]" %>
</div>
<% end %>
<%= form.fields_for :images do |p| %>
<div class="field">
<%= form.label :image, class: "col-2 col-form-label" %>
<%= form.file_field :image, :multiple => true, name: "images[image][]" %>
</div>
<% end %>
I am confused in modifing the gallery controller create part.
You can add the images creation 2 times in the create method if you have 2 uploaders
params[:images]['image'].each do |a|
#gallery.images.create!(:image => a, :gallery_id => #gallery.id)
end
params[:images]['avatar'].each do |a|
#gallery.images.create!(:avatar => a, :gallery_id => #gallery.id)
end
but i think it's a little bit verbose and it not really prevents if you don't want to upload any image in your form (return params[:images]['...'] nil)
Btw your form is not correct for the form.fields_for. It's :
<%= form.fields_for :images_attributes do |p| %>
<div class="field">
<%= p.label :master_image, class: "col-2 col-form-label" %>
<%= p.file_field :avatar, :multiple => true, name: "images[avatar][]" %>
</div>
<% end %>
<%= form.fields_for :images_attributes do |p| %>
<div class="field">
<%= p.label :image, class: "col-2 col-form-label" %>
<%= p.file_field :image, :multiple => true, name: "images[image][]" %>
</div>
<% end %>

collection_select not inserting value from other model

I have two models, Roaster and Roast
I want to have the user select the value of :roaster in the new roast form, from the Roaster model. I am using a collection_select which displays the list of roasters in the dropdown ok, but it doesn't insert the value into the table. From the console, it actually looks like it's trying to pass the roaster_id
"roast"=>{"roaster_id"=>"1", "name"=>"Rugby", "beans"=>"", "countries_attributes"=>{"0"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "1"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "2"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}}, "bestfor"=>"", "roast"=>"", "tastingnotes"=>""}, "commit"=>"Create Roast"}
My select:
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
I've tried
<%= form.collection_select(:roaster_name, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
but this gives and undefined method error.
My roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
Adding in :roaster_name doesn't solve either.
As requested full form:
<%= form_with(model: roast, local: true, multipart: true) do |form| %>
<% if roast.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger" role="alert">
<h2><%= pluralize(roast.errors.count, "error") %> prohibited this roast from being saved:</h2>
<ul>
<% roast.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<form>
<div class="row">
<div class="col-6">
<div class="field form-group">
<%= form.label :roaster, class: 'control-label' %>
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
</div>
</div>
<div class="col-6">
<div class="form-group">
<%= form.label :name, class: 'control-label' %>
<%= form.text_field :name, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= form.label :beans, "Blend", class: 'control-label' %><br />
<%= form.select :beans, [ 'Single Origin','Two Country Blend', 'Three Country Blend' ], :prompt => 'Select One', id: :roast_beans, class: "form-control" %>
</div>
<div class="row">
<%= form.fields_for :countries do |countries_form| %>
<div class="col-6">
<div class="form-group">
<%= countries_form.label :country %>
<%= countries_form.text_field :country_name, class: "form-control" %>
</div>
</div>
<div class="col-6">
<!-- note the appending of `countries_` to form.fields to allow for deeper nested to work-->
<%= countries_form.fields_for :regions do |regions_form| %>
<%= regions_form.label :region %>
<%= regions_form.text_field :region_name, class: "form-control" %>
<% end %>
<br />
</div>
<% end %>
</div>
<div class="form-group">
<%= form.label :bestfor, "Style", class: 'control-label' %><br />
<%= form.select :bestfor, [ 'Espresso','Filter' ], :prompt => 'Select One', id: :roast_bestfor, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :roast, "Strength", class: 'control-label' %><br />
<%= form.select :roast, [ 'Light','Medium','Dark' ], :prompt => 'Select One', id: :roast_roast, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :tastingnotes, "Tasting Notes (separate with commas, e.g chocolate, citrus)", class: 'control-label' %><br />
<%= form.text_area :tastingnotes, id: :roast_tastingnotes, class: "form-control" %>
</div>
<br />
<div class="form-group">
<%= form.label :avatar, "Upload image...", class: 'control-label' %>
<%= form.file_field :avatar %>
</div>
<div class="actions">
<%= form.submit class: "btn btn-success" %> <%= link_to "Cancel", "/roasts", class: "btn btn-secondary"%>
</div>
<% end %>
</form>
roast_controller.rb
class RoastsController < ApplicationController
before_action :set_roast, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:create, :edit, :update, :destroy]
before_action :set_search
# GET /roasts
# GET /roasts.json
def index
#q = Roast.ransack(params[:q])
#roastsalpha = #q.result.order(:name)
#roastcount = Roast.count(:country)
#roasts = Roast.order(:name).count
#countroastschart = Roast.order("roaster DESC").all
end
# GET /roasts/1
# GET /roasts/1.json
def show
#roast = Roast.friendly.find(params[:id])
#commentable = #roast
#comments = #commentable.comments
#comment = Comment.new
#sameroaster = Roast.where(roaster: #roast.roaster)
#samecountry = Roast.where(country: #roast.country)
#roastcount = Roast.where(roaster: #roast.roaster)
end
# GET /roasts/new
def new
#roast = Roast.new
3.times {#roast.countries.build.regions.build}
end
# GET /roasts/1/edit
def edit
3.times {#roast.countries.build.regions.build}
end
# POST /roasts
# POST /roasts.json
def create
#roast = Roast.new(roast_params)
respond_to do |format|
if #roast.save
format.html { redirect_to #roast, notice: 'Roast was successfully created.' }
format.json { render :show, status: :created, location: #roast }
else
format.html { render :new }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /roasts/1
# PATCH/PUT /roasts/1.json
def update
respond_to do |format|
if #roast.update(roast_params)
format.html { redirect_to #roast, notice: 'Roast was successfully updated.' }
format.json { render :show, status: :ok, location: #roast }
else
format.html { render :edit }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# DELETE /roasts/1
# DELETE /roasts/1.json
def destroy
#roast.destroy
respond_to do |format|
format.html { redirect_to roasts_url, notice: 'Roast was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_roast
#roast = Roast.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
end
end
I think you are doing many wrong things. By looking your other questions I saw your models. I put some important things:
class Roast < ApplicationRecord
has_many :countries
accepts_nested_attributes_for :countries
end
class Country < ApplicationRecord
has_many :regions, inverse_of: :country
accepts_nested_attributes_for :regions
belongs_to :roast
end
class Region < ApplicationRecord
belongs_to :country, inverse_of: :regions
end
In these models I didn't see the Roaster. I assume a Roast belongs_to :roaster.
So: your Roast has many countries and each country has many regions. But you are passing country names and region names in your view to the create controller. You need to pass the ids, so that you save references to these models.
You have many unnecessary field in params, and some missing ones. This is how it should be:
def roaster_params
params.require(:roast).permit(:roaster_id, :name, :bestfor, :beans, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:id, regions_attributes: [:id]])
end
You don't need roast, roaster, country_name, region_name. You need the id of the country (and not the country_id), and the id of the region (and not the region_id)
In your form you should ask for country and region ids:
<%= countries_form.collection_select(:id, Country.all, :id, :name, :prompt => 'Select Country') %>
<%= regions_form.collection_select(:id, Region.all, :id, :name, :prompt => 'Select Region') %>
In fact this is more difficult, because a region belongs to a country, but here you are showing all regions. You should only show regions for the selected country (which is dynamic).

Nested forms not storing data into database in rails 4

I have a vendor model, a product model, and a vendor_product model. In my vendors form, I have used nested form to create vendor_products with attributes as vendor_id, product_id and copies.On creating a new vendor, it also creates a vendor_product. But for some reason, it does not stores the vendor_id and product_id in vendor_products table but only stores the copies
My associations are as follows
A vendor ->
has_many :vendor_products
has_many :products, through: :vendor_products
A product ->
has_many :vendor_products
has_many :vendors, through: :vendor_products
A vendor_product
belongs_to :vendor
belongs_to :product
Vendor.rb
class Vendor < ActiveRecord::Base
has_many :vendor_products
has_many :products, through: :vendor_products
accepts_nested_attributes_for :vendor_products, :products,
:allow_destroy => true
end
My vendors/_form.html.erb
<%= form_for(#vendor) do |f| %>
<% if #vendor.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#vendor.errors.count, "error") %> prohibited this
vendor from being saved:</h2>
<ul>
<% #vendor.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
:
:
:
<%= f.fields_for :vendor_products do |vproducts| %>
<div class="field">
<%= vproducts.label :product %><br>
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>
<div class="field">
<%= vproducts.label :copies %><br>
<%= vproducts.number_field :copies %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My vendors_controller.rb
class VendorsController < ApplicationController
before_action :set_vendor, only: [:show, :edit, :update, :destroy]
respond_to :json
def index
#vendors = Vendor.all.limit(20)
end
def show
end
def new
#vendor = Vendor.new
#vendor.products.build
#vendor.vendor_products.build
end
def edit
end
def create
#vendor = Vendor.new(vendor_params)
respond_to do |format|
if #vendor.save
format.html { redirect_to #vendor, notice: 'Vendor was successfully
created.' }
format.json { render :show, status: :created, location: #vendor }
else
format.html { render :new }
format.json { render json: #vendor.errors, status:
:unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #vendor.update(vendor_params)
format.html { redirect_to #vendor, notice: 'Vendor was successfully
updated.' }
format.json { render :show, status: :ok, location: #vendor }
else
format.html { render :edit }
format.json { render json: #vendor.errors, status:
:unprocessable_entity }
end
end
end
private
def set_vendor
#vendor = Vendor.find(params[:id])
end
def vendor_params
params.require(:vendor).permit(:name, :email, :phone_no, :addressline1,
:addressline2, :landmark,
:city, :state, :country, :pincode, :latitude, :longitude, :status,
product_attributes: [:product_id, :product_name, :price ],
vendor_products: [:vendor_product_id, :vendor_id, :product_id,
:copies])
end
end
Now a vendor and VendorProduct is created but my vendor_product looks lik this
{"id":3,
"vendor_id":null,
"product_id":null,
"copies":4,
}
Can any one point out how to fix this. What am I doing wrong. Please bear in mind that I am a rails newbie.
change once vendor_products to vendor_products_attributes and look
In your view
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
replace with
<%= vproducts.select :product_id, options_from_collection_for_select(Product.all, "id", "name"), prompt: "Select something" %>
Change this line:
<%= collection_select(:product, :product_ids, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>
to
<%= collection_select(:product, :product_id, Product.all, :id,
:product_name,
{:prompt => 'Please select', :multiple => true }) %>
</div>

Save successful on nested attributes on has many through but doesn't add to database

I and order and item system with Rails 4 with a has many through association. When I select create order the webpage says that the order was created successfully however the link is not made in the OrderItems linking tables meaning that the items relating to that order do not appear on the show page or edit page for an order.
The order is also linked to an employee. The current employee ID is linked to that order. I have just not been able to figure out how to add each item to the database.
p.s. I am using a gem called nested_form to handle all of the jQuery on the front end of dynamically adding and removing new items on the _form.html.erb for Orders.
orders_controller.rb
class OrdersController < ApplicationController
before_action :logged_in_employee, only:[:new, :show, :create, :edit, :update, :index]
before_action :admin_employee, only:[:destroy]
before_action :set_order, only: [:show, :edit, :update, :destroy]
def new
#order = Order.new
#items = Item.all
end
def edit
#items = Item.all
end
def create
#order = current_employee.orders.build(order_params)
if #order.save
flash[:success] = "Order successfully created"
redirect_to #order
else
render 'new'
end
end
def update
if #order.update_attributes(order_params)
flash[:success] = "Order updated!"
redirect_to current_employee
else
render 'edit'
end
end
....
private
def set_order
#order = Order.find(params[:id])
end
def order_params
params.require(:order).permit(:table_number, :number_of_customers, :status, :comment, order_items_attributes: [:id, :order_id, :item_id, :_destroy])
end
end
order.rb
class Order < ActiveRecord::Base
belongs_to :employee
has_many :order_items
has_many :items, :through => :order_items
default_scope { order('status DESC') }
validates :employee_id, presence: true
validates :table_number, numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 20 }, presence: true
validates :number_of_customers, numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 50 }, presence: true
validates :status, inclusion: { in: %w(Waiting Ready Closed), message: "%{value} is not a status" }
accepts_nested_attributes_for :order_items, :reject_if => lambda { |a| a[:item_id].blank? }
end
item.rb
class Item < ActiveRecord::Base
belongs_to :menu
has_many :order_items
has_many :orders, :through => :order_items
before_save { name.capitalize! }
VALID_PRICE_REGEX = /\A\d+(?:\.\d{0,2})?\z/
validates :name, presence: true, length: { maximum: 100 }
validates :price, format: { with: VALID_PRICE_REGEX }, numericality: { greater_than: 0, less_than_or_equal_to: 100}, presence: true
validates :course, inclusion: { in: %w(Starter Main Dessert Drink), message: "%{value} is not a course" }
validates :menu_id, presence: true
end
order_item.rb
class OrderItem < ActiveRecord::Base
belongs_to :item
belongs_to :order
validates :order_id, presence: true
validates :item_id, presence: true
end
orders/_form.html.erb
<% provide(:title, "#{header(#order)} #{#order.new_record? ? "order" : #order.id}") %>
<%= link_to "<< Back", :back, data: { confirm: back_message } %>
<h1><%= header(#order) %> <%= #order.new_record? ? "order" : #order.id %></h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= nested_form_for #order do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="row">
<div class="col-xs-6">
<%= f.label :table_number %>
<%= f.number_field :table_number, class: 'form-control' %>
</div>
<div class="col-xs-6">
<%= f.label :number_of_customers %>
<%= f.number_field :number_of_customers, class: 'form-control' %>
</div>
</div>
<%= f.fields_for :order_items do |oi| %>
<%= oi.grouped_collection_select :item_id, Menu.where(active: true).order(:name), :items, :name, :id, :name, { include_blank: 'Select Item' }, class: 'items_dropdown' %>
<%= oi.hidden_field :item_id %>
<%= oi.hidden_field :order_id %>
<%= oi.link_to_remove "Remove item" %>
<% end %>
<p><%= f.link_to_add "Add an item", :order_items %></p>
<br>
<% if !#order.new_record? %>
<%= f.label "Status" %>
<%= f.select(:status, options_for_select([['Waiting', 'Waiting'], ['Ready', 'Ready'], ['Closed', 'Closed']], #order.status), class: 'form-control') %>
<% end %>
<%= f.label "Comments - how would you like your steak cooked? Or feedback" %>
<%= f.text_area :comment, size: "20x5", class: 'form-control' %>
<%= f.submit submit_label(#order), class: "btn btn-success col-md-6" %>
<% end %>
<% if !#order.new_record? && current_employee.try(:admin?) %>
<%= link_to "Cancel", :back, data: { confirm: back_message }, class: "btn btn-warning col-md-offset-1 col-md-2" %>
<%= link_to "Delete", #order, method: :delete, data: { confirm: "Are you sure? The employee will be deleted! "}, class: "btn btn-danger col-md-offset-1 col-md-2" %>
<% else %>
<%= link_to "Cancel", :back, data: { confirm: back_message }, class: "btn btn-warning col-md-offset-1 col-md-5" %>
<% end %>
</div>
</div>
Issue: Let's say your order has got many items and you want the items to be saved just when the order is created.
order = Order.new(order_params)
item = Item.new(name: 'item-name', product_info: 'etc etc')
if order.save
item.create
order.items << item
end
You need to follow similar approach in your case. just get the item_params properly and apply above rule.
Try below approach, hope that helps. :)
def create
#order = current_employee.orders.build(order_params)
if #order.save
item = params["order"]["order_items_attributes"]
#please debug above one and try to get your items from params.
order_item = Item.create(item)
#makes sure above item hold all item attributes then create them first
#order.items << item
redirect_to #order
end
end

Ruby on Rails dropdown values not saving

I'm new to RoR and having an issue when trying to save from multiple dropdowns. I have three objects - books, genres, and authors. A book object has a genre and author associated to it, but the issue is I can only manage to save either a genre or author to my book object, and not both. Here's where I'm at:
class Author < ActiveRecord::Base
validates :name, :presence => true
validates :biography, :presence => true
has_many :books
end
class Genre < ActiveRecord::Base
validates :description, :presence => true
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :genre
belongs_to :author
has_many :cartitems
validates :name, :presence => true
validates :price, :presence => true
validates :description, :presence => true
end
Controller:
def create
##book = Book.new(params[:book])
#author = Author.find(params[:author].values[0])
#genre = Genre.find(params[:genre].values[0])
#book = #author.books.create(params[:book])
#one or the other saves, but not both
##book = #genre.books.create(params[:book])
respond_to do |format|
if #book.save
format.html { redirect_to(#book, :notice => 'Book was successfully created.') }
format.xml { render :xml => #book, :status => :created, :location => #book }
else
format.html { render :action => "new" }
format.xml { render :xml => #book.errors, :status => :unprocessable_entity }
end
end
end
Not sure if this will help out or not, but here's what the dropdowns look like in the View:
<div class="field">
<%= f.label :genre %><br />
<%= #items = Genre.find(:all)
select("genre", "description", #items.map {|u| [u.description,u.id]}, {:include_blank => true})%>
Appreciate any help with this.
EDIT - Here's my full form.
<%= form_for(#book) do |f| %>
<% if #book.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#book.errors.count, "error") %> prohibited this book from being saved:</h2>
<ul>
<% #book.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :price %><br />
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :description %><br />
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :genre %><br />
<%= #items = Genre.find(:all)
select("genre", "description", #items.map {|u| [u.description,u.id]}, {:include_blank => true})%>
</div>
<div class="field">
<%= f.label :author %><br />
<%=#items = Author.find(:all)
select("author", "name", #items.map {|u| [u.name,u.id]}, {:include_blank => true}) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Update your select fields to be defined like this:
<div class="field">
<%= f.label :genre %><br />
<%= f.select( :genre_id, Genre.all.map {|u| [u.description,u.id]}, {:include_blank => true}) %>
</div>
<div class="field">
<%= f.label :author %><br />
<%= f.select( :author_id, Author.all.map {|u| [u.name,u.id]}, {:include_blank => true}) %>
</div>
And your controller action should be like this:
def create
#book = Book.new(params[:book])
respond_to do |format|
if #book.save
format.html { redirect_to(#book, :notice => 'Book was successfully created.') }
format.xml { render :xml => #book, :status => :created, :location => #book }
else
format.html { render :action => "new" }
format.xml { render :xml => #book.errors, :status => :unprocessable_entity }
end
end
Also, remove the format.xml calls if you don't need them, they're just cluttering your controller action.
There are lots of different ways to fix your problem, it would help to see exactly what's in your params hash and what your full form looks like, but no matter. Here is one way:
#book = #author.books.create(:genre => #genre)
Here is another (essentially the same thing):
#book = #author.books.create {|book| book.genre = #genre}
You could also instantiate the book separately:
#author = Author.find(params[:author].values[0])
#genre = Genre.find(params[:genre].values[0])
#book = Book.create(:author => #author, :genre => #genre)
My guess is you didn't quite build your form correctly, otherwise your params hash would look similar to this {:book => {:author => 1, :genre => 5}} and you would be able to do this:
#book = Book.create(params[:book])
And you would not look up the author using params[:author], but would instead do params[:book][:author] if you needed to do it at all.

Resources