Nested Forms for Embedded Documents - ruby-on-rails

I have the following Models
cities(id, name, geo {lng,lat})
geo(lng,lat)
Cities Model
class City
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
field :timezone, type: String
field :slug, type: String
belongs_to :region
belongs_to :country
embeds_one :geo_location
accepts_nested_attributes_for :geo_location
end
Geo Locations Model
class GeoLocation
include Mongoid::Document
include Mongoid::Timestamps
field :lng, type: String
field :lat, type: String
embedded_in :city
end
Cities Controller
class CitiesController < ApplicationController
before_action :set_city, only: [:show, :edit, :update, :destroy]
# GET /cities
def index
#cities = City.all
end
# GET /cities/1
def show
end
# GET /cities/new
def new
#city = City.new
#regions = Region.all.asc(:name)
#countries = Country.all.asc(:name)
end
# GET /cities/1/edit
def edit
#regions = Region.all.asc(:name)
#countries = Country.all.asc(:name)
end
# POST /cities
def create
#city = City.new(city_params)
if #city.save
redirect_to #city, notice: 'City was successfully created.'
else
render action: 'new'
end
end
# PATCH/PUT /cities/1
def update
if #city.update(city_params)
redirect_to #city, notice: 'City was successfully updated.'
else
render action: 'edit'
end
end
# DELETE /cities/1
def destroy
#city.destroy
redirect_to cities_url, notice: 'City was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_city
#city = City.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def city_params
params.require(:city).permit(:name, :timezone, :region_id, :country_id, :slug, :geo_locations_attributes => [:id, :lag, :lat])
end
end
Form:
<%= form_for(#city) do |f| %>
<% if #city.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#city.errors.count, "error") %> prohibited this city from being saved:</h2>
<ul>
<% #city.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 :country %><br>
<%= f.collection_select :country_id, #countries, :id, :name, :prompt => "Please Select" %>
</div>
<div class="field">
<%= f.label :region %><br>
<%= f.collection_select :region_id, #regions, :id, :name, :prompt => "Please Select" %>
</div>
<div class="field">
<%= f.label :timezone %><br>
<%= f.text_field :timezone %>
</div>
<div class="field">
<%= f.label :slug %><br>
<%= f.text_field :slug %>
</div>
<%= f.fields_for :geo_locations do |geo_location| %>
<div class="field">
<%= geo_location.label :lag %><br>
<%= geo_location.text_field :lag %>
</div>
<div class="field">
<%= geo_location.label :lat %><br>
<%= geo_location.text_field :lat %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
New View
<h1>New city</h1>
<%= render 'form' %>
<%= link_to 'Back', cities_path %>
The ERROR I am getting
Unpermitted parameters: geo_location

On controller, Replace your city_params method with this,
def city_params
params.require(:city).permit(:name, :timezone, :region_id, :country_id, :slug, :geo_location_attributes => [:id, :lag, :lat])
end
On view, Replace this "f.fields_for :geo_locations" with "f.fields_for :geo_location"
Problem in geo_locations_attributes. It should be geo_location_attributes as this is one-to-one relationship.

Related

Could not save values in Nested forms using rails 5

I am stuck up with building nested forms using Ruby on Rails.
I am trying to build a form which has fields from three tables(User, Contact, Address). User table has address_id and contact_id. When user fills the details, contact details should be saved in contact table and address should be saved in address table. Both the ids should get saved in user table along with the user details. How should I proceed?
My model,
class Address < ApplicationRecord
has_one :user
end
class Contact < ApplicationRecord
has_one :user
end
class User < ApplicationRecord
belongs_to :address
belongs_to :contact
end
My controller,
class UsersController < ApplicationController
def new
#user = User.new
#user.build_contact
#user.build_address
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:name, :email, contact_attributes: [:phone], address_attributes: [:street, :city])
end
end
And my view is,
<%= form_for(user) do |f| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<%= f.fields_for :contact do |c| %>
<div class="field">
<%= c.label :phone %>
<%= c.text_field :phone %>
</div>
<% end %>
<%= f.fields_for :address do |a| %>
<div class="field">
<%= a.label :street %>
<%= a.text_field :street %>
</div>
<div class="field">
<%= a.label :city %>
<%= a.text_field :city %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Is my approach right? Kindly please suggest. Thanks in advance.
You missing a couple of lines...
class User < ApplicationRecord
belongs_to :address
belongs_to :contact
accepts_nested_attributes_for :address
accepts_nested_attributes_for :contact
end
Also ensure you accept :id and :_delete
params.require(:user).permit(:name, :email, contact_attributes: [:id, :phone, :_delete], address_attributes: [:id, :street, :city, :_delete]

Rails passing params to different model and then saving it not working

I have a 'post' controller in that I have two variable title and body which I am passing through strong parameters.But I need to use two other variable which are path and name which are in different model name 'Document'..And also I am saving the content in database ..but unable to do so..getting this error view [posts/_form.html.erb]
undefined method `name' for #
[posts_controller]
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post = find_params
end
def create
#post = Post.create(post_params)
#post.user = current_user
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
Document.new(params,:files=>[])
end
def find_params
Post.find(params[:id])
end
end
[post/_form.html.erb]
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<%= f.label :name %> <br>
<%= f.text_field :name %><br>
<br>
<br>
<%= f.label :path %><br>
<%= f.file_field :path %><br>
<%= f.submit %>
<% end %>
[document.rb]
class Document < ActiveRecord::Base
validates :name, presence: true
validates :path, presence: true
validates :resource_type, presence: true
validates :resource_id, presence: true
mount_uploader :path, PathUploader
validates :name, presence: true
# def self.abc
# params.permit(:name,:path)
# end
def initialize(params,file)
params=file[:name]
#params.permit(name =>:name,path =>:path)
end
end
undefined method `name' for #
You're referencing a non-existent attributes for your Post form:
<%= form_for #post,html: { multipart: true } do |f| %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<%= f.submit %>
<% end %>
Remove :name & :path references.
--
If you want to pass "extra" attributes to another model, you need to use accepts_nested_attributes_for or set the params separately to your "primary" model:
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :documents
accepts_nested_attributes_for :documents
end
#app/models/document.rb
class Document < ActiveRecord::Base
belongs_to :post
end
This will allow you to pass the documents as "nested" attributes of your Post model:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = Post.new
#post.documents.build
end
def create
#post = Post.new post_params
#post.save
end
private
def post_params
params.require(:post).permit(:title, :body, documents_attributes: [:name, :path])
end
end
#app/views/posts/_form.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.fields_for :documents do |d| %>
<%= d.text_field :name %>
<%= d.text_field :path %>
<% end %>
<%= f.submit %>
<% end %>
So undefined method on a model will indicate that, well, the method doesn't exist on the model. Want to see a model's methods? Post.methods. However, in this example, the column name is not defined on the model., and you're trying to tell Post that it has a name. What you need to do is nest your parameters.
While there is a ton of cleaning up that might want to focus on first, your answer is found in the accepts_nestable_attributes_for class methods, as shown here, http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html, and strong_params documentation as shown here, http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
In your case, you want to create a new document from a post. Your permitted params hash will look like this,
params.require(:post).permit(:title, :body, :document_attributes => [:name])
Ensure that document_attributes is singular; if a person has_many pets (for example), then you'd have pets_attributes.
In your form, something that often trips people up is the builder.
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.fields_for #post.document do |document_field| %>
<%= document_field.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
Make sure that you're telling ERB that <%= f.fields_for %>, not just <% f.fields_for %>.

i use many to many relationship but it not save data in through table

i have 2 model book and wishlist.
and between this 2 model i use many to many relationship.
Below is my model.
class Book < ActiveRecord::Base
has_many :book_wishlist_customizations, dependent: :destroy
has_many :wish_lists ,through: :book_wishlist_customizations
end
class BookWishlistCustomization < ActiveRecord::Base # through table
belongs_to :wish_list
belongs_to :book
end
class WishList < ActiveRecord::Base
has_many :book_wishlist_customizations
has_many :books,through: :book_wishlist_customizations
end
when i delete book from admin side it raise following error.
PG::ForeignKeyViolation: ERROR: update or delete on table "books" violates foreign key constraint "fk_rails_7a6b92667b" on table "wish_lists" DETAIL: Key (id)=(1) is still referenced from table "wish_lists". : DELETE FROM "books" WHERE "books"."id" = $1.
below is my book form.
<%= form_for #book, url: books_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :stream_id %>
<%= f.collection_select :stream_id, Stream.all, :id, :stream_name, prompt: true %>
</div>
</br>
<div class="field">
<%= f.label :university_board_id, "University" %>
<%= f.collection_select :university_board_id, UniversityBoard.where(category_id: $college_id).all, :id, :name, prompt: true %>
</div>
</br>
<div class="field">
<%= f.label :course_standard_id, "Course & Year" %>
<%=f.select(:course_standard_id, :"Please select" => true)%>
<%=f.select(:year_semester, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :college_school_id, "College" %>
<%=f.select(:college_school_id, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :subject_id, "Subject" %>
<%=f.select(:subject_id, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :book_name, "Book name" %>
<%= f.text_field :book_name %>
</div>
</br>
<div class="actions">
<%= f.submit "Continue", class: "btn btn-primary" %>
</div>
<% end %>
below is my controller:-
class BooksController < ApplicationController
def new
#book = Book.new
$college_id = Category.where(category_name: ['college']).select(:id)
$school_id = Category.where(category_name: ['school']).select(:id)
end
def create
if user_signed_in?
#book = Book.new(book_params)
#last_commision = Commision.last
#book_status = BookStatus.find_by(status_name: "pending")
#book.update_attributes( commision_id: #last_commision.id, book_status_id: #book_status.id, user_id: current_user.id, quantity: "1")
if #book.save
# exit
session[:book_id] = #book.id
# session[:photo_id] = #book_photo.id
redirect_to multi_steps_path
else
respond_to do |format|
format.html { render :new }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
else
session[:book_details] = book_params
redirect_to unauthenticated_root_path, notice: "For post your book first login in our system."
end
end
def stream_change_course
#stream_details=CourseStandard.where(stream_id: params[:stream], category_id: $college_id)
render :json => #stream_details
end
def course_change_college_subject
#year_semester=CourseStandard.where(id: params[:course])
#subject_details=Subject.where(course_standard_id: CourseStandard.where(category_id: $college_id))
#college_details=CollegeSchool.where(course_standard_id: CourseStandard.where(category_id: $college_id))
render :json => {:subject => #subject_details,:college => #college_details,:year =>#year_semester}
end
def price_calculate_commission
#final_amount=params[:price].to_i-(params[:price].to_i*Commision.last.percentage)/100
render :json => #final_amount
end
private
def book_params
params.require(:book).permit(:book_id, :user_id, :book_name, :book_condition_id,:price_for_sale, :mrp, :book_auther, :isbn, :description, :publish_year, :edition, :publication, :book_status_id, :quantity, :category_id, :university_board_id, :college_school_id, :course_standard_id, :subject_id, :commision_id, :stream_id, :medium_id,:year_semester) rescue {}
# params.require(:book).permit(:stream_id, :university_board_id, :course_standard_id, :subject_id, :id)
end
def book_photo_params
params.require(:book_photo).permit(:photo) rescue {}
end
end
I'm guessing from your error that your WishList model has a field for book ids. It shouldn't. Your BookWishListCustomization model's book id field and wishlist id field along with the belongs_to and has_many associations your models have now are enough.

Rails nested attributes are not saving

I am new to rails and am learning to complete an Inventory System database project for school.
Here is my Item model:
class Item < ActiveRecord::Base
self.primary_key ='item_id'
validates :item_id, :presence => true
has_one :vendor_item, :dependent => :destroy
has_one :vendor, :through => :vendor_item
accepts_nested_attributes_for :vendor_item
end
Here is my item controller:
class ItemsController < ApplicationController
def new
#item = Item.new
#all_vendors = Vendor.all
#item_vendor = #item.build_vendor_item
end
def create
#item = Item.new(item_params)
vendor = params[:vendors][:id]
#item_vendor = #item.build_vendor_item(:vendor_id => vendor)
#item_vendor.save
#raise params.inspect
if #item.save
redirect_to #item
else
render 'new'
end
end
def show
#item = Item.find(params[:id])
#item_vendor = #item.vendor_item
end
def index
#items = Item.all
end
def priceUnder500
#priceUnder500 = Item.where("price < ?", 500)
respond_to do |format|
format.html
format.js
end
end
def priceOver500
#priceOver500 = Item.where("price > ?", 500)
respond_to do |format|
format.html
format.js
end
end
def edit
#item = Item.find(params[:id])
#all_vendors = Vendor.all
#vendor_item = #item.vendor_item
end
def update
#item = Item.find(params[:id])
vendor = params[:vendors][:id]
if #item.vendor_item.blank?
#item.build_vendor_item(:vendor_id => vendor)
end
if #item.update(params[:item].permit(:item_id, :name, :category, :description, :reorder_level, :quantity, :price, :vendor_item_attributes => [:vendor_item_id]))
redirect_to items_path
else
render 'edit'
end
end
def destroy
#item = Item.find(params[:id])
#item.destroy
redirect_to items_path
end
private
def item_params
params.require(:item).permit(:item_id, :name, :category, :description, :reorder_level, :quantity, :price, :vendor_item_attributes => [:vendor_item_id])
end
end
And my _form partial for items:
<%= form_for #item do |f| %>
<% if #item.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#item.errors.count, "error") %> prohibited
this post from being saved:</h2>
<ul>
<% #item.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :item_id, 'Item Id' %><br>
<%= f.text_field :item_id %>
</p>
<%= fields_for #item_vendor do |vii| %>
<div class= "vendorItemId">
<%= vii.label :vendor_item_id%>
<%= vii.text_field :vendor_item_id%><br>
</div>
<% end %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :category %><br>
<%= f.text_field :category %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.label :reorder_level %><br>
<%= f.text_field :reorder_level %>
</p>
<p>
<%= f.label :quantity %><br>
<%= f.text_field :quantity %>
</p>
<p>
<%= f.label :price %><br>
<%= f.text_field :price %>
</p>
<h3>Vendors:</h3>
<%= fields_for(#item_vendor) do |ab| %>
<div class= "field">
<%= ab.label "All Vendors:" %><br>
<%= collection_select(:vendors, :id, #all_vendors, :id, :name, {},{:multiple => false})%>
</div>
<% end %>
<p>
<%= f.submit %>
</p>
<% end %>
vendor_item contains a reference to item_id, vendor_id, and has another attribute of vendor_item_id. When the program saves, item_id and vendor_id save but vendor_item_id does not save. I have tried every online source but cannot seem to get that one attribute to save. I am using rails 4. Thanks in advance for any help provided.
You haven't saving the result in an instance variable in create action.
Try giving like this in your create action.
def create
#item = Item.new(item_params)
vendor = params[:vendors][:id]
#item_vendor = #item.build_vendor_item(:vendor_id => vendor)
if #item.save
redirect_to #item
else
render 'new'
end
end
def create
#item = Item.new(item_params)
#vendor = params["item"]["vendors"]
if #item.save
#item.create_vendor_item(#vendor.id => vendor).save
redirect_to #item
else
render 'new'
end
end

rails 3.2 NoMethodError undefined method `slice' for nil:NilClass

i am getting this error end have no clue how to fix it. the weird thing is, it was working before. i think after i run annotate, it is broken, but not sure.
the error comes from confs.controller index and own methods.
it also rejects something like this: conf.machine_brand[0,1].upcase as NoMethodError [ ] bla bla
this is my conf model:
# == Schema Information
#
# Table name: confs
#
# id :integer not null, primary key
# machine_brand :string(255)
# machine_model :string(255)
# control_unit_brand :string(255)
# control_unit_model :string(255)
# tool_axis_x :decimal(, )
# tool_axis_y :decimal(, )
# tool_axis_z :decimal(, )
# rotary_axis_number :integer
# linear_axis_number :integer
# turning_mode :boolean
# milling_mode :boolean
# description :text
# xml :text
# user_id :integer
# developer_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Conf < ActiveRecord::Base
attr_accessible :linear_axis_number, :control_unit_brand, :control_unit_model, :description, :developer_id, :machine_brand, :machine_model, :milling_mode, :rotary_axis_number, :tool_axis_x, :tool_axis_y, :tool_axis_z, :turning_mode, :user_id, :xml
belongs_to :developer, :class_name => 'User', :foreign_key => 'developer_id'
belongs_to :receiver, :class_name => 'User', :foreign_key => 'user_id'
validates :user_id, presence: true
validates :developer_id, presence: true
end
this is confs.controller:
class ConfsController < ApplicationController
before_filter :signed_in_user, only:[:index, :edit, :update, :destroy]
before_filter :developer_user, only: :destroy
def new
#conf = Conf.new
end
def index
#grouped = {}
Conf.all.each do |conf|
letter = conf.machine_brand.slice(0,1).upcase
#grouped[letter] ||= []
#grouped[letter] << conf
end
end
def show
#conf = Conf.find(params[:id])
respond_to do |format|
format.html #index.html.erb
format.json { render json: #conf }
format.xml { render xml: #conf }
end
end
def own
#grouped = {}
Conf.where(:developer_id => current_user.id).each do |conf|
letter = conf.machine_brand.slice(0,1).upcase
#grouped[letter] ||= []
#grouped[letter] << conf
end
end
def create
#conf = Conf.new(conf_params)
if #conf.save
flash[:success] = "New Configuration uploaded!"
redirect_to conf_show_path
else
flash[:error] = "There is a problem!"
render 'new'
end
end
def destroy
#conf = Conf.find(params[:id]).destroy
redirect_to conf_show_own_path
end
def update
#conf.update_attributes(params[:conf])
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in"
end
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def developer_user
redirect_to(root_path) unless current_user.developer?
end
def conf_params
params.require(:conf).permit(:xml, :user_id, :developer_id) if params[:conf]
end
end
and this is conf.new if you wish:
<% provide(:title, 'New Configuration')%>
<h1> Upload new configuration </h1>
<div class="row">
<div class="span6 offset3">
<%= form_for #conf, :html => {:multipart => true} do |f| %>
<%= f.label :machine_brand %>
<%= f.text_field :machine_brand %>
<%= f.label :machine_model %>
<%= f.text_field :machine_model %>
<%= f.label :control_unit_brand %>
<%= f.text_field :control_unit_brand %>
<%= f.label :control_unit_model %>
<%= f.text_field :control_unit_model %>
<%= f.label :tool_axis_x %>
<%= f.text_field :tool_axis_x %>
<%= f.label :tool_axis_y %>
<%= f.text_field :tool_axis_y %>
<%= f.label :tool_axis_z %>
<%= f.text_field :tool_axis_z %>
<%= f.label :rotary_axis_number %>
<%= f.text_field :rotary_axis_number %>
<%= f.label :linear_axis_number %>
<%= f.text_field :linear_axis_number %>
<%= f.label :turning_mode %>
<%= f.text_field :turning_mode %>
<%= f.label :milling_mode %>
<%= f.text_field :milling_mode %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.label :xml %>
<%= f.text_field :xml %>
<%= f.label :client %>
<%= f.collection_select :user_id, User.where(:admin => false, :developer => false), :id, :name, options ={:prompt => "Select a client"}, :class =>"user" %>
<%= f.label :me %>
<%= f.collection_select :developer_id, User.where(:id => current_user.id), :id, :name, options ={:prompt => "Select me"}, :class =>"user" %>
<br />
<%= f.submit "Upload", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
conf.machine_brand.slice(0,1)
I think you got the error here machine_brand so simply do in your controller
letter = params[:machine_brand].to_s.slice(0,1).upcase unless params[:machine_brand].blank?
or
letter = params[:conf][:machine_brand].to_s.slice(0,1).upcase unless params[:machine_brand].blank?
As #Rajarshi mentioned, the error is in the following code
conf.machine_brand.slice(0,1).upcase
The error says that you're calling slice on a nil object, which means that one of your conf records has a nil machine_brand. I'm not sure how you'd want to approach this problem but this issue will be lessened if you add a validation that requires a machine_brand
class Conf < ActiveRecord::Base
...
validates :machine_brand, presence: true
or you can only fetch for records where machine_brand is present
Conf.where('machine_brand IS NOT NULL').each do |conf|
conf.machine_branch.slice(...)

Resources