rails How to save a serialized hash - ruby-on-rails

I have a serialized object :address in Hotel model and I don't know how to save it properly in the DB. I have the following:
#model hotel
class Hotel < ActiveRecord::Base
belongs_to :user
serialize :address, Hash
end
...and view 'new'
<%= form_for(#hotel) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :stars %>
<%= f.text_field :stars %>
<%= f.label :room, "Room description" %>
<%= f.text_area :room, size: "20x10" %>
<%= f.label :price %>
<%= f.number_field :price %>
<%= f.fields_for :address do |o| %>
<%= o.label :country %>
<%= o.text_field :country %>
<%= o.label :state %>
<%= o.text_field :state %>
<%= o.label :city %>
<%= o.text_field :city %>
<%= o.label :street %>
<%= o.text_field :street %>
<% end %>
<%= f.submit "Create hotel", class: "btn btn-large btn-primary" %>
<% end %>
With this code what I get is: hotel address nil...
Okay.. We will go another way. After googling much I came to this code:
# hotel.rb model
class Hotel < ActiveRecord::Base
class Address
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :country, :state, :city, :street
def persisted?; true end
def id; 1 end
def self.load json
obj = self.new
unless json.nil?
attrs = JSON.parse json
obj.country = attrs['country']
obj.state = attrs['state']
obj.city = attrs['city']
obj.street = attrs['street']
end
obj
end
def self.dump obj
obj.to_json if obj
end
end
belongs_to :user
serialize :address, Address
end
and the same view new.html.erb
The result is: Address:0xb0e530c
So, nothing saves in the database... I don't know what to try next, I'll appreciate any help. Didn't know that serialized object will cause so much problems to me.
THANKS!
PS Here's hotels_controller.
class HotelsController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
def new
#hotel = Hotel.new
end
def index
#hotels = Hotel.paginate(page: params[:page])
end
def show
#hotel = Hotel.find(params[:id])
end
def create
#hotel = current_user.hotels.build(hotel_params)
if #hotel.save
flash[:success] = "Hotel created!"
redirect_to #hotel
else
render 'new'
end
end
private
def hotel_params
params.require(:hotel).permit(:title, :stars, :room, :price, :address)
end
end

First thing on your migration file make sure that you are saving your fields as a test like
def self.up
add_column : hotels, : address, :text
end
Then Rails will convert it into YAML / Hash for you (and perform proper serialization).
Wish you the best of luck.
PS take a look at https://stackoverflow.com/a/6702790/1380867

Related

Nested one-to-one attribute does not save via form

I've got a form with a nested one-to-one attribute which I can't save into the dabase. I've been reading SO threads for four hours to try and solve it:
Contact.rb:
class Contact < ApplicationRecord
has_one :address
accepts_nested_attributes_for :address
end
Address.rb
class Address < ApplicationRecord
belongs_to :contact
end
Routes.rb
Rails.application.routes.draw do
resources :contacts do
resources :addresses
end
root :to => 'contacts#index'
end
The form
<%= form_for #contact do |f| %>
<div class="field">
<%= f.label :firstname %>
<%= f.text_field :firstname %>
</div>
<div class="field">
<%= f.label :lastname %>
<%= f.text_field :lastname %>
</div>
<%= f.fields_for :address do |address_fields| %>
<%= address_fields.label :streetname %>
<%= address_fields.text_field :streetname %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Controller:
class ContactsController < ApplicationController
before_action :find_contact, only: [:show, :edit, :update, :destroy]
def index
#contacts = Contact.all
end
def show
end
def new
#contact = Contact.new
#ontact.build_address
end
def create
#contact = Contact.new(contact_params)
if #contact.save
redirect_to #contact
else
render 'new'
end
end
(...)
private
def find_contact
#contact = Contact.find(params[:id])
end
def contact_params
params.require(:contact).permit(:firstname, :lastname,
address_attributes: [:contact_id, :streetname])
end
end
With the above code, nothing is saved and the transaction is rolled back. Earlier on, only the contact-details were saved whereas the address remained nil and the following line gave an error that "streetname" was unknown:
<p><%= #contact.address.streetname %></p>
Your help is much appreciated!
Thanks #fbelanger for leading me to the solution:
I noticed from the contents of "params" that permitted was set on false. I found via Google that Rails 5 requires the following addition in the address model, to the belongs_to line:
class Address < ApplicationRecord
belongs_to :contact, required: false
end
And now it's saved: hurray!

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 %>.

How to get multiple records for same model with Rails form_for

I am trying to build a basic recipes app but am having trouble allowing the user to input multiple ingredients for one recipe. The array of permitted params for the ingredients ends up empty. So I guess my question is - how do I permit the array of ingredients?
My controller:
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#ingredient = Ingredient.new
end
def create
safe_params = params.require(:recipe).permit(:title, :instruction, :category_id)
ingredient_params = params.require(:recipe).permit(:ingredient => [])
#recipe = Recipe.new(safe_params)
#recipe.save
ingredient_params.each do |i|
#recipe.ingredients << Ingredient.find_or_create_by(name: i[:ingredient][:name])
end
render body: YAML::dump(ingredient_params)
#redirect_to index_path(id: #recipe.id)
end
end
Form:
<%= form_for(#recipe, :url => create_path) do |f| %>
<%= f.label :category %>
<%= f.select :category_id, options_for_select(Category.all.map{|c|[c.title, c.id]}) %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for "ingredients[]", #ingredient do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
Models:
class Recipe < ActiveRecord::Base
has_and_belongs_to_many :ingredients
accepts_nested_attributes_for :ingredients
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :recipes
end
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
end
There are several issues here, I'll just provide what I'd do:
#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
#recipe.ingredients.new
end
def create
#recipe = Recipe.new safe_params
#recipe.save
end
private
def safe_params
params.require(:recipe).permit(:title, :instruction, :category_id, ingredients_attributes: [:name])
end
end
#app/views/recipes/new.html.erb
<%= form_for #recipe do |f| %>
<%= f.label :category %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
<%= f.label :title %>
<%= f.text_field :title%>
<%= f.label :instruction %>
<%= f.text_area(:instruction, size: "50x10") %>
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
<%= f.submit "Submit" %>
<% end %>
If you wanted to have multiple ingredients fields, you'll have to build multiple objects in the controller:
def new
#recipe = Recipe.new
3.times do
#recipe.ingedients.build
end
end
Everything else looks like it will work well.
--
As an extra, if you want to populate has_and_belongs_to_many relationships, you'll be able to just pass the [relationship]_ids parameter:
<%= form_for #recipe do |f| %>
<%= f.collection_select :ingredient_ids, Ingredient.all, :id, :name %>
<%= f.submit %>
<% end %>
This will only work for currently existing ingredients. If you want to create new ingredients, the above will work.
Need to make few changes to your code
class RecipesController < ApplicationController
def new
#recipe = Recipe.new
# here you can decide how many ingredients do you want. (Not in form looping through text fields)
3.times do
ingredient = #recipe.ingredients.build
end
end
so fields for ingredients were get generated for three times.
<%= f.fields_for :ingredients do |i| %>
<%= i.label :name %>
<%= i.text_field :name %>
<% end %>
Please go through following link, it will clear your idea about nested forms
http://railscasts.com/episodes/196-nested-model-form-part-1

nested forms : update child foreign key

I have a nested association:
class User < ActiveRecord::Base
has_many :hostels
accepts_nested_attributes_for :hostels
end
class Hostel < ActiveRecord::Base
belongs_to :user
end
The form :
<%= form_for #user do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email %>
<% f.object.hostels << #hostel -%>
<%= f.fields_for :hostels do |ff| %>
<%= ff.hidden_field :id %>
<% end -%>
<%= f.submit %>
<% end -%>
the controller
def create
#user = User.new(user_params)
raise #user.hostels.inspect
end
private
def user_params
params.require(:user).permit(:email, hostels_attributes: [:id])
end
I would like to relink existing records of hostels to new users by updating hostel foreign key. This way, it definitly don't work.
Tried update_only: true parameter to nested too.
Any ideas about the subject or am I totally wrong about trying to do the operation like that ?
you can use a multiple select for hotels in you form, then in the controller you must require hostel_ids.
Models:
class User < ActiveRecord::Base
has_many :hostels
end
class Hostel < ActiveRecord::Base
belongs_to :user
end
Form: where :name is what you see in your multiple select
<%= form_for #user do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email %>
<%= f.label "Hostels" %><br>
<%= select_tag :hotel_ids, options_for_select(Hostel.all.map{|h| [h.name, h.id]}), { :multiple => true } %>
<%= f.submit %>
<% end %>
Controller:
def create
#user = User.new(user_params)
end
private
def user_params
params.require(:user).permit(:email, hostel_ids)
end
I did not test the code but must work well.

Rails 4 trouble with accepts_nested_attributes_for

I am new to rails and I'm trying the accepts_nested_attributes_for function. I am creating an inventory system and the accepts_nested_attributes_for feature is being used to attach multiple order details to an order. An order must also be associated with store location.
The problem I'm having is the order is being created but no data is being passed to the order details table.
My views are below:
Orders View
<h1>Place An Order</h1>
<%= form_for ([#location, #order]) do |f| %>
<p>
<%= f.label :customer_id %><br />
<%= f.text_field :customer_id %>
</p>
<p>
<h3>Items</h3>
<%= f.fields_for :order_details do |builder| %>
<%= render 'order_detail_fields', :f => builder %>
<% end %>
</p>
<p><%= link_to_add_fields "Add Item", f, :order_details %></p>
<p>
<%= f.submit %>
</p>
<% end %>
Order_details_fields Partial
<p class="fields">
<%= f.label :item_id %><br />
<%= f.text_field :item_id %></br>
<%= f.label :quantity %></br>
<%= f.text_field :quantity %></br>
<%= f.label :cost %></br>
<%= f.text_field :cost %></br>
<%= f.label :discount %><br />
<%= f.text_field :discount %><br />
<%= f.hidden_field :_destroy %>
<%= link_to_function "remove", "remove_fields(this)" %>
</p>
Orders Controller
class OrdersController < ApplicationController
def index
#orders = Order.all
end
def show
#order = Order.find(params[:id])
end
def new
#order = Order.new
#location = Location.find(params[:location_id])
end
def create
#location = Location.find(params[:location_id])
#order = #location.orders.create(order_params)
##order = #order.order_details.create
if #order.save
redirect_to #order
else
render :action => 'new'
end
end
private
def order_params
params.require(:order).permit(:customer_id, order_detials_attributes: [:id, :item_id, :quantity, :cost, :discount])
end
end
Orders Model
class Order < ActiveRecord::Base
belongs_to :location
has_many :order_details, :dependent => :destroy
accepts_nested_attributes_for :order_details, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
Order Details Model
class OrderDetail < ActiveRecord::Base
belongs_to :order
end
Routes
resources :locations do
resources :orders
end
resources :orders do
resources :order_details
end
Any help with this would be greatly appreciated
Build
Looks like everything is right to me - the only problem being the issue #Pavan outlined, which is that when you use accepts_nested_attributes_for, you have to build the associative object, so it can be used in the form:
#app/controllers/orders_controller.rb
Class OrdersController < ApplicationController
def new
#location = Location.find parmas[:id]
#order = Order.find params[:id]
#order.order_details.build
end
end
Although this looks like the only issue you have, there may be other problems (validation on the OrderDetail model as an example (which you don't have)
The only issue with what both I and Pavan have recommended is if you don't build your associative data, the fields_for don't show on the form. If your fields are showing, it may be a different issue, which will be highlighted in the params hash

Resources