Rails AJAX updating a nested model trouble - ruby-on-rails

I have an app with the models Game, Square, Ownedsq, and User, and I am trying to update a square's :user_id within a game. I have been trying this for hours now, I believe the model relationships are correct and I am just missing something small so I appreciate any help.
Currently clicking the update ownedsq submit button does not change the user_id. There are no errors, nothing gets updated, nothing happens on the server.
games/show.html.erb
(Rows of 10 Ownedsq)
<div class="squareBoard">
<% #ownedsqs.each_slice(10) do |slice| %>
<div class='row'>
<% slice.each do |s| %>
<%= div_for s, class: 'sq' do |buy| %>
<%= s.user_id %>
<%= render "buyownedsq", s: buy %>
<% end %>
<% end %>
</div>
<% end %>
</div>
_buyownedsq.html.erb (clicking should set the ownedsq user_id to current user)
<%= simple_fields_for s, remote: true do |z| %>
<%= z.input :user_id, as: :hidden, input_html: {value:current_user.id} %>
<%= z.button :submit %>
<% end %>
games_controller.rb
def show
require 'enumerator'
#user = current_user
#game = Game.find(params[:id])
#squares = Square.all
#square = Square.find(params[:id])
#ownedsqs = Ownedsq.all
end
ownedsqs_controller.rb
def update
#ownedsq.find(params[:id])
respond_to do |format|
format.js
if #ownedsq.update_attributes(params[:ownedsq])
format.html {redirect_to game_path}
format.json {head :no_content}
else
format.html {redirect_to :back}
end
end
end
Game.rb
has_many :ownedsqs
has_many :squares, through: :ownedsqs
has_and_belongs_to_many :users
accepts_nested_attributes_for :ownedsqs
accepts_nested_attributes_for :squares
Square.rb
has_many :ownedsqs
has_many :users, through: :ownedsqs
has_many :games, through: :ownedsqs
accepts_nested_attributes_for :ownedsqs
accepts_nested_attributes_for :users
accepts_nested_attributes_for :games
Ownedsq.rb
belongs_to :square
belongs_to :user
belongs_to :game
validates_uniqueness_of :square_id, scope: :game_id
User.rb
has_and_belongs_to_many :games
has_many :ownedsqs
has_many :squares, through: :ownedsqs
accepts_nested_attributes_for :ownedsqs
accepts_nested_attributes_for :squares

Several problems here for you to look at:
How do you receive the params[:id] of the ownedsq?
You can easily get rid of the view-level user_id reference
Your remote calls won't raise an error directly; they'll do it through the network tab of your browser
Formatting
I'd just use a simple link / button to send the request to the server:
_buyownedsq.html.erb
<%= button_to ownedsq_path(buy.id), method: :put, data: {confirm: "Are you sure?"}, remote: :true %>
This will send a request to the server, which you can process like this:
#app/controllers/ownedsqs_controller.rb
def update
#ownedsq = Ownedsq.find(params[:id]) #where do you get the params[:id] var?
respond_to do |format|
format.js
if #ownedsq.update_attributes(user_id: current_user.id)
format.html {redirect_to game_path}
format.json {head :no_content, status: :200}
else
format.html {redirect_to :back}
format.json {status: :500 }
end
end
end
Save
Your associations look right - I don't presume the update_attributes method would get confused / have issues, as you're working directly with the OwnedSq model (not using accepts_nested_attributes_for etc)

Related

Unpermitted params error while creating records of grand-child records in grand-parent forms

I have three models Book_Room, Invoice, and Bill. Invoice belongs to BookRoom, and has many bills. the error is unpermitted params error for :invoice is thrown when i try to create Book Room after ive clearly asked to accept nested params
class Invoice < ApplicationRecord
belongs_to :book_room
has_many :bills
accepts_nested_attributes_for :bills
end
And BookRoom is meant to have only one invoice, but I used has_many because the fields_for form doesn't render when I use has_one.
class BookRoom < ApplicationRecord
belongs_to :customer
has_and_belongs_to_many :rooms
has_many :invoices
accepts_nested_attributes_for :invoices
end
Here is my create action for invoice controller:
def create
#booking = BookRoom.find(params[:book_room_id])
#invoice = #booking.invoice.new(invoice_params)
if #invoice.save
redirect_to #invoice, notice: 'Invoice was successfully created.'
else
render :new
end
end
And here is my form, which is meant to create invoices and bills.
<%= f.fields_for :invoice do |i| %>
<%= i.fields_for :bill do |b| %>
<%= b.label :price %>
<%= b.number_field :price %>
<%= b.hidden_field :type, value: :deposit %>
<% end %>
<% end %>
And finally, my book_room controller:
def create
#book_room = #customer.book_rooms.new(book_room_params)
if #book_room.save
redirect_to #book_room, notice: 'Book room was successfully created.'
else
render :new
end
end
def book_room_params
params.require(:book_room).permit(:customer_id, :start_date, :end_date, :room_ids=>[], :invoices_attributes => [ :invoice_id, :bills_attributes => [:price, :type] ])
end
When I try to create a book room with bill records, it throws an error of unpermitted params invoice. If there is a way to get the form to render on has_one relationship, I will appreciate it.
Try switching to has_one :invoice association - fields_for can work with it, but you need to build association first with #book_room.build_invoice in BookRoomController#new controller action.
Then you can fix book_room_params - change invoices_attributes key to singular invoice_attributes.

Updating nested fields_for and collection_select in Rails

Really stumped here. I'm trying to get my form to update the categories on the edit form. Problem is, everything in my form updates when submitted except the categories. It ends up inserting the new category chosen like it's going through the create method instead of the update method, so when the edit form is shown again after submission, it keeps doubling the fields of categories. 1, then 2, then 4, then 8, etc. after each submission. Please please help anyone. Appreciate it.
views/blog_posts/edit.html.erb
<div class="col-md-6 col-md-offset-3 blog-submit">
<%= form_for #blog_post do |b| %>
<%= b.label :title %>
<%= b.text_field :title %><br>
<%= b.fields_for :categorizations do |cat| %>
<%= cat.label :category_name, "Category 1" %>
<%= cat.collection_select(:category_id, Category.all, :id, :category_name, {blank: "Select Category"}) %>
<%= link_to "Add Categories", new_category_path %>
<br>
<% end %>
<%= b.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
Blog_post controller:
class BlogPostsController < ApplicationController
protect_from_forgery
before_action :authenticate_admin!, only: [:new, :edit]
def index
#blog_posts = BlogPost.order(id: :desc)
end
def new
#blog_post = BlogPost.new
#blog_post.categorizations.build.build_category
#blog_post.categories.build
end
def edit
#blog_post = BlogPost.find(params[:id])
end
def create
#blog_post = BlogPost.new(blog_post_params)
respond_to do |format|
if #blog_post.save
format.html { redirect_to #blog_post, notice: 'Your blog was submitted successfully' }
format.json { render :show, status: :created, location: #blog_post }
else
format.html { render :new }
format.json { render json: #blog_post.errors, status: :unprocessable_entity }
end
end
puts #blog_post.errors.inspect
end
def update
#blog_post = BlogPost.find(params[:id])
if #blog_post.update_attributes(blog_post_params)
render 'show'
else
render 'edit'
end
end
def show
#blog_post = BlogPost.find(params[:id])
end
private
def blog_post_params
params.require(:blog_post).permit(:title, :content, :posted_by, :comments, :blog_pic, {categorizations_attributes: [:category_id, :category_name]})
end
end
models:
class BlogPost < ApplicationRecord
has_many :categorizations
has_many :categories, :through => :categorizations
accepts_nested_attributes_for :categorizations
has_many :comments
mount_uploader :blog_pic, BlogPicUploader
end
class Categorization < ApplicationRecord
belongs_to :blog_post
belongs_to :category
end
class Category < ApplicationRecord
has_many :categorizations
has_many :blog_posts, :through => :categorizations
end
Add id in blog_post_params as shown below. This will work for you.
def blog_post_params
params.require(:blog_post).permit(:title, :content, :posted_by, :comments, :blog_pic, {categorizations_attributes: [:id,:category_id, :category_name]})
end

How do I structure my controller in Rails so that I can create multiple models on form submit?

I'm building a Rails app (Rails 4.1.0) and I'm running into some trouble organizing the form logic. I think it is because I'm fundamentally missing some knowledge about Rails routing and how to handle it, so I apologize if this question is basic.
This is what my routes.rb looks like:
Rails.application.routes.draw do
resources :users do
resources :addresses
resources :appointments
resources :boxes do
resources :statuses
end
resources :statuses
end
end
The User signs up with their name, email, and password on the landing page. Afterwards, they are immediately presented with a form where they can create an Appointment and add an Address (using fields_for in my form - see below). This form is the same form that will be used for all future Appointment requests. Also, every time the User submits an Appointment request, a number of Boxes will be added to the database as well tied to the that User.
My question is, where do I handle all of the logic for this form submit? On the landing page, I have the form to create a new user. However, every time I add a new Appointment, would the I submit the form to the create method of the Users controller as well? That doesn't seem like the correct way to approach it since I'm creating an Appointment (and possibly creating/updating the Address).
I'm familiar with fields_for and I'm using it in my form. This is the form the user sees right after they sign up and are sent to their dashboard (which is right now, /users/show/:id). I have it rendered in a modal right now.
<%= form_for(#user) do |f| %>
<%= f.fields_for :appointments do |appt| %>
<%= appt.date_select :appointment_date %>
<%= appt.time_select :appointment_start_time %>
<%= appt.time_select :appointment_end_time %>
<%= appt.text_area :comments %>
<% end %>
<%= f.fields_for :addresses do |addr| %>
<%= addr.text_field :street_address %>
<%= addr.text_field :street_address_optional %>
<%= addr.text_field :city %>
<%= addr.text_field :state %>
<%= addr.number_field :zip_code %>
<%= addr.check_box :primary %><%= builder.label :primary %>
<% end %>
<%= f.submit "Sign Up" %>
<% end %>
This is what my UsersController looks like (haven't done anything with it yet except change the show method):
class UsersController < ApplicationController
# GET /users/1
# GET /users/1.json
def show
#user = current_user
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
sign_in #user
format.html { redirect_to new_appointment_url, notice: 'User was successfully created.' }
format.json { render :new, status: :created, location: #appointment }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
And here are my Models:
class User < ActiveRecord::Base
has_many :addresses, dependent: :destroy
has_many :boxes, dependent: :destroy
has_many :statuses, dependent: :destroy
has_many :appointments, dependent: :destroy
accepts_nested_attributes_for :addresses, :boxes, :statuses, :appointments
end
class Address < ActiveRecord::Base
belongs_to :user
end
class Appointment < ActiveRecord::Base
belongs_to :user
end
class Box < ActiveRecord::Base
belongs_to :user
has_many :statuses, dependent: :destroy
end
class Status < ActiveRecord::Base
belongs_to :box
belongs_to :user
end
Can you show us your def user_params in your controller? My suspicion is that it's missing an :addresses_attributes component. It should probably look something like this (I'm just making up these attributes):
def user_params
params.require(:user).permit(:first_name, :last_name, addresses_attributes: [:street, :city, :state]
end

How to add has_many :through details to a form RoR

I'm doing what appears to be a common learning app for Ruby on Rails, the recipe app. Specifically, working on recipes and ingredients as a has_many :through relationship. Through looking at a million examples and questions, I've got my many-to-many relationship setup and my multi-model form working, but I'd like to add an additional field and can't get it working. Feels like I'm close to understanding how this stuff works. Here are the quick details:
Models:
class Ingredient < ActiveRecord::Base
has_many :recipe_ingredients
has_many :recipes, :through => :recipe_ingredients
end
class RecipeIngredient < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
class Recipe < ActiveRecord::Base
has_many :recipe_ingredients
has_many :ingredients, :through => :recipe_ingredients
accepts_nested_attributes_for :ingredients, :recipe_ingredients
def new_recipe_ingredient_attributes=(recipe_ingredient_attributes)
recipe_ingredient_attributes.each do |attributes|
recipe_ingredients.build(attributes)
end
end
def existing_recipe_ingredient_attributes=(recipe_ingredient_attributes)
recipe_ingredients.reject(&:new_record?).each do |recipe_ingredient|
attributes = recipe_ingredient_attributes[recipe_ingredient.id.to_s]
if attributes
recipe_ingredient.attributes = attributes
else
recipe_ingredient.delete(recipe_ingredient)
end
end
end
def save_recipe_ingredients
recipe_ingredients.each do |recipe_ingredient|
recipe_ingredient.save(false)
end
end
end
Controller:
def create
#recipe = Recipe.new(params[:recipe])
if #recipe.save
redirect_to :action => 'show', :id => #recipe
flash[:notice] = "Your record has been saved."
else
render :action => 'new'
end
end
def update
params[:recipe][:existing_recipe_ingredient_attributes] ||= {}
#recipe = Recipe.find(params[:id])
if #recipe.update_attributes(params[:recipe])
redirect_to :action => 'show', :id => #recipe
flash[:notice] = "Your changes have been saved."
else
render :action => 'edit'
end
end
View:
<% form_for(#recipe) do |f| %>
<%= f.label :name %><br />
<%= f.text_field :name %>
etc.....
Ingredients:
<div id="recipe_ingredients">
<div class="recipe_ingredient">
<% new_or_existing = recipe_ingredient.new_record? ? 'new' : 'existing' %>
<% prefix = "recipe[#{new_or_existing}_recipe_ingredient_attributes][]" %>
<% fields_for prefix, recipe_ingredient do |ri_form| %>
<p>
<%= ri_form.collection_select(:id, Ingredient.find(:all), :id, :name, :include_blank => true) %>
<%= ri_form.text_field :amount %>
</p>
<% end %>
</div>
</div>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
Sorry for the wall of code, hopefully it makes sense. The thing I can't understand is why the "amount" text field doesn't work. I've tried a million different ways, but can't get it working. In this case, the error I get is "undefined method `amount' for #"
What key connection am I missing here? Thanks.
At first glance it appears you should simply replace:
<% fields_for prefix, recipe_ingredient do |ri_form| %>
with:
<%= fields_for prefix, recipe_ingredient do |ri_form| %>

Rails: Updating Nested attributes - undefined method `to_sym' for nil:NilClass

Edit: Added the update action, and on what line the error occurs
Model:
class Match < ActiveRecord::Base
has_and_belongs_to_many :teams
has_many :match_teams
has_many :teams, :through => :match_teams
accepts_nested_attributes_for :match_teams, :allow_destroy => true
end
Controller:
def new
#match = Match.new
#match_teams = 2.times do
#match.match_teams.build
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #match }
end
end
def update
#match = Match.find(params[:id])
respond_to do |format|
if #match.update_attributes(params[:match])
format.html { redirect_to #match, notice: 'Match was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
Nested model:
class MatchTeam < ActiveRecord::Base
belongs_to :match
belongs_to :team
end
Association:
class Team < ActiveRecord::Base
has_and_belongs_to_many :matches
end
View:
<%= form_for(#match) do |f| %>
<%= f.fields_for :match_teams, #match_teams do |builder| %>
<%= builder.collection_select :team_id, Team.all, :id, :name, :include_blank => true %>
<% end %>
<% unless #match.new_record? %>
<div class="field">
<%= f.label :winning_team_id %><br />
<%= f.collection_select :winning_team_id, #match.teams, :id, :representation %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Params:
Processing by MatchesController#update as HTML
Parameters: {"utf8"=>"Ô£ô", "authenticity_token"=>"QIJChzkYOPZ1hxbzTZS8H3AXc7i
BzkKv3Z5daRmlOsQ=", "match"=>{"match_teams_attributes"=>{"0"=>{"team_id"=>"1", "
id"=>""}, "1"=>{"team_id"=>"3", "id"=>""}}, "winning_team_id"=>"3"}, "commit"=>"
Update Match", "id"=>"2"}
Creating a new match with 2 teams work fine, the edit view also shows the correct values, but the update action gives me this error.
undefined method `to_sym' for nil:NilClass
app/controllers/matches_controller.rb:65:in `block in update'
line 65: if #match.update_attributes(params[:match])
I've figured it out. I read that a join table like MatchTeams doesn't need an ID. I'm guessing this is true when not doing any nested forms. I redid my migration removing the exclusion of the id column, and now everything works fine. Don't we all love this stupid errors? :)
Without seeing the offending to_sym in your code, just know that the thing it's attached to has not been defined properly. If this is a variable such as #var.to_sym, you most likely:
Haven't set #var at all
Set it but it's returning nil because there are no matches (e.g. #var = #project.companies.first but #project has no companies tied to it).
You are missing a relevant bit of data in your params. If your to_sym is relying on data submitted through the form, it won't work if the user leaves out the bit of data you're assuming. In this case, you should test first to see if the data was entered before running .to_sym on it.

Resources