Create and update with checkboxes and simple form - ruby-on-rails

I am trying to do something basic but for some reason that's not working and not returning me any errors.
I have a User model and a ManualBill model.
The Manual Bill belongs to a User (I can select who paid it when I create it and it belongs to this User).
But then, once the Manual Bill is created, I also want to be able to add "manual_bills_payers", who are the ones who did not pay the bill but have to refund it.
In the end, that makes my models look like this:
class ManualBill < ApplicationRecord
belongs_to :flat
belongs_to :user
has_many :manual_bill_payers
has_many :users, through: :manual_bill_payers
end
And
class ManualBillPayer < ApplicationRecord
belongs_to :manual_bill
belongs_to :user
end
I've created an update form for the ManualBill, in which I am going to select several users (who are going to be manual_bill_payers) with checkboxes (I am in a loop which is why I called the manual_bill "bill" here.
<%= simple_form_for [#flat, bill] do |f| %>
<%= f.error_notification %>
<%= f.association :manual_bill_payers, as: :check_boxes,
:label_method =>
lambda { |owner| "#{owner.email}" },collection: User.all,
input_html: {class: "input-
bar-settings"} %>
<%= f.submit 'Add payers', class: "btn-form" %>
<% end %>
I am really confused of what to do here.
What I want to do is basically CREATE several new manual_bill_payers when updating the manual_bill by selecting users who are in my flat (all the Users of the db in the exemple above). I also want to be able to remove them by unchecking the boxes.
I've basically tried everything... from changing the models to put "has_and_belongs_to_many" instead of the "through" association, to changing the :manual_bill_payers into :users in the simple_form.
Most of the time the update goes through (no errors and redirect me to the show page) but I still get an empty array when I do ManualBillPayer.all or when I check my manual_bill.manual_bill_payers.
My update method here just in case
def update
#manual_bill = ManualBill.find(params[:id])
authorize #manual_bill
if #manual_bill.update(manual_bill_params)
redirect_to flatshare_path(#manual_bill.flat)
else
redirect_to root_path
end
end
def manual_bill_params
params.require(:manual_bill).permit(:manual_bill_payer_id)
end
I don't have any create method in this controller because the manual_bill is created somewhere else, which is why the last method is pretty empty
Where am I wrong? Is there something I am completely minsunderstanding?
I've put a raise in the update to check the params
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"280izDKna4QWfr0OhZJW6u/
UWcqlxo56yR17fBDX4QFKhNG94mYqbBLPuq+ifo3mNIV10GFk1RK7Hr5AnmosOA==",
"manual_bill"=>{"manual_bill_payer_ids"=>["", "1", "35", "34"]},
"commit"=>"Add payers",
"flatshare_id"=>"15",
"id"=>"15"}
Thanks a lot

Related

Display several nested attributes in Rails form

I've been looking for a solution for a few days, in a Rails 4.1 app, so here is my question :
In a Rails app, I have my model User and Adress.
class User < ActiveRecord::Base
has_many :adresses
accepts_nested_attributes_for :adresses
class Adress < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
In my form, I make a form_tag for User, no problem.
But, how I can display to the final user, in a form, 2 adresses fields?
I use <%= f.fields_for :adress %> to display one, it's ok. But if I display two forms (so the user can enter 2 adresses) they have both the same name and the request post only keep one.
I read the doc at http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
but, still, I don't get it.
Is there a proper way to do it?
Thanks
I would suggest you to prepare two addresses in new action, add them to the use and then in the form reneder it with foreach.
I found this kind of solution here : http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Since you have multiple addresses I think foreach is way to go.
So, to help anyone who is noob in Rails and stuck the same way I was :
In your controller :
#user = User.new
#user.adresses = Adress.new, Adress.new
In your view, form :
<%= form_for #user do |f| %>
<%= f.fields_for :adresses do |a| %>
<%= wp.text_field :name %>
<% end %>
<% end %>
will print the name field for adress two times.
(thanks again to #NickCatib)

Trouble displaying data from multiple nested tables in rails 4

I'm new to rails. I've searched and been stuck on this problem for a couple days now.
I am building a site where there are users, bands (that users can join), tours (that belong to bands), and stops (stops on the tours). I have tables for each, as well as additional tables that link them together by id (bandmembership, bandtourmembership, tourstopmembership).
I followed a few tutorials and have used belongs_to, has_many => through to link these all together and I have used nested attributes to display data from one level deep successfully.
The final format I'm trying to display is
User Name
=> Band Name #1
====> Tour Name #1
========> Tour Stop #1
========> Tour Stop #2
====> Tour Name #2
========> Tour Stop #1
========> Tour Stop #2
=> Band Name #2
====> Tour Name #3
========> Tour Stop #1
========> Tour Stop #2
etc.
Currently I only can get the band name to display without an error, but it displays the same band name 3 times (there are 3 in the database). When I try to add in tours it just gives an error. I'd also like to try to use a partial and a collection to break out the rendering of each time of item.
My questions are
Why is the partial displaying the same name 3 times and how do I get it to display the correct name?
Why am I not able to access tours from bands and how do I get it to cooperate?
views/users/show.html.erb
<h1>Dashboard</h1>
<%= render partial: 'shared/user_item' %>
<% if #user.bands.any? %>
<h2>You are in <%= #user.bands.count %> bands:</h2>
<%= render partial: 'shared/band_item', collection: #band_items %>
<% else %>
shared/_band_item.html.erb
<%= #band.name %>
shared/_tour_item.html.erb
<%= #tour.name %>
shared/_stop_item.html.erb
<%= #stop.name %>
controllers/users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "You are now signed in"
redirect_to #user
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#band_items = Bandmembership.where(user_id: #user.id)
#band = Band.find(params[:id])
end
def new
#user = User.new
end
private
def user_params
params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation)
end
end
models/user.rb
class User < ActiveRecord::Base
has_many :bandmemberships
has_many :bands, :through => :bandmemberships
has_many :tours, :through => :bands
accepts_nested_attributes_for :bands, :tours
end
models/bandmembership.rb
class Bandmembership < ActiveRecord::Base
belongs_to :user
belongs_to :band
end
models/tour.rb
class Tour < ActiveRecord::Base
has_many :tourstopmemberships
has_many :stops, :through => :tourstopmemberships
has_many :bandtourmemberships
has_many :bands, :through => :bandtourmemberships
accepts_nested_attributes_for :stops
end
This smells like something solvable by a class-level delegate method (Rails Antipatterns, pp 6-7)
You've got the show method pulling the params for both user and band. Is that something like tld.com/user/1/band/3?
If you don't have params for both in the ID, then it's pulling the user's ID for band or vice versa.
From a code perspective, you should be able to refactor towards something like this:
<h1>Dashboard</h1>
<%# this should render app/views/users/_user.html.erb %>
<%= render #user %>
<% if #user.bands %>
<h2>
You are in <%= #user.bands.count %> bands:
</h2>
<ul>
<%= #user.bands.each do band %>
<%# this should render app/views/bands/_band.html.erb %>
<%= render #band,
locals: (band: band) %>
<% end %>
</ul>
<%- end -%>
Your _band.html.erb would be
<li class="band-name">
<%= band.name %>
</li>
The code may not be 100% right as I keep jumping between a 2.3 app and a 4.x app. But the principle is this:
1.) Use ActiveRecord's power to your benefit. You've got the model association, so you don't need to do the lookup yourself. #user.bands should return an array of bands the user belongs to since they belong to those bands THROUGH bandmemberships.
2.) If you need to get to something, don't walk the tree over 2 or 3 items. e.g. #user.band.first.tour.first is bad bad juju. Create a method that finds this on the User model and then go from there, e.g.
def next_tour
User.tour.first etc etc
end
and then call it in the view as #user.next_tour or whatever.
3.) Use the power of render #collection_name and use the defaults to clean your code up. It's easier to read and better than a lot of partials floating around in shared/.
This is something I harp on a lot when I give my Rails View talks, the partials should belong in the folder for the controller they exist under. /app/views/tours/_tour.html.erb and so forth would be better than the tour_item under shared. It's the rendering of a single tour entry for anywhere in the app.
I'm not sure about tourstopmemberships as a join model either. What are you joining it to? Why not just have a tour has many stops and stops belong to a tour? If you're looking at a venue model as well, then perhaps stops is the join model between tours and venues. That then allows you to add additional meta data onto the stop
stop.tour
stop.venue
stop.start_time
stop.support_act (which could be a different relationship)
etc.

Rails 4 HABTM stops working when I add "multiple true" to collection_select

I've googling and trying everything I could think of for the past couple of days to solve a relatively simple (I presume) issue with has_and_belongs_to_many relation.
I managed to successful use the HABTM relation to submit a single relation value. Here's the sample code:
Model:
class Livre < ActiveRecord::Base
has_and_belongs_to_many : auteurs
end
class Auteur < ActiveRecord::Base
has_and_belongs_to_many :livres
end
Controller:
def new
#livre = Livre.new
#auteurs = Auteur.all
end
def create
#livre = Livre.new(livre_params)
if #livre.save
redirect_to [:admin, #livre]
else
render 'new'
end
end
private
def livre_params
params.require(:livre).permit(:name, :auteur_ids)
end
View:
<% f.label :auteur %><br>
<% f.collection_select(:auteur_ids, #auteurs, :id, :name) %>
Posted Params:
{"utf8"=>"✓",
"authenticity_token"=>"mAXUm7MRDgJgCH00VPb9bpgC+y/iOfxBjXSazcthWYs=",
"livre"=>{"name"=>"sdfsdfd",
"auteur_ids"=>"3"},
"commit"=>"Create Livre"}
But when I try to add "multiple true" to the view's collection_select helper, the (now multiple) relation doesn't get saved anymore. Sample code:
(both Model and Controller unchanged)
View:
<% f.label :auteur %><br>
<% f.collection_select(:auteur_ids, #auteurs, :id, :name, {}, {:multiple => true}) %>
Posted Params:
{"utf8"=>"✓",
"authenticity_token"=>"mAXUm7MRDgJgCH00VPb9bpgC+y/iOfxBjXSazcthWYs=",
"livre"=>{"name"=>"sdfsdf",
"auteur_ids"=>["1",
"5",
"3"]},
"commit"=>"Create Livre"}
As you can see, the params for "auteur_ids" is now an array. That's the only difference.
What am I doing wrong?
Just to clarify: both piece of code are able to add a new record to the livres db table, but only the 1st code is able to add the appropriate record to the auteurs_livres db table. The second one simply does not insert anything into auteurs_livres.
(I run on ruby 1.9.3p194 and rails 4.0.1)
Thanks!
Answer
For the fine folks stuck with the same problem, here's the answer:
Edit your controller and change the permitted parameter from :auteur_ids to {:auteur_ids => []}
params.require(:livre).permit(:name, {:auteur_ids => []})
And it now works :)
For the fine folks stuck with the same problem, here's the answer:
Edit your controller and change the permitted parameter from :auteur_ids to {:auteur_ids => []}
params.require(:livre).permit(:name, {:auteur_ids => []})
And it now works :)
You worked out the solution because Rails now expects auteur_ids to be an array, rather than a single item. This means that instead of just passing a single entity to the model, it will package the params as [0][1][2] etc, which is how you can submit your HABTM records now
There is a more Rails way to do this, which is to use accepts_nested_attributes_for. This is going to seem like a lot more work, but it will dry up your system, and also ensure convention over configuration:
Model
class Livre < ActiveRecord::Base
has_and_belongs_to_many : auteurs
accepts_nested_attributes_for :auteurs
end
class Auteur < ActiveRecord::Base
has_and_belongs_to_many :livres
end
Controller
def new
#livre = Livre.new
#livre.auteurs.build
#auteurs = Auteur.all
end
def create
#livre = Livre.new(livre_params)
if #livre.save
redirect_to [:admin, #livre]
else
render 'new'
end
end
private
def livre_params
params.require(:livre).permit(:name, auteur_attributes: [:auteur_id])
end
Form
<%= form_for #livre do |f| %>
<%= f.text_field :your_current_vars %>
<%= f.fields_for :auteurs do |a| %>
<%= a.collection_select(:auteur_id, #auteurs, :id, :name, {}) %>
<% end %>
<% end %>
This will submit the auteur_id for you (and automatically associate the livre_id foreign key in the HABTM model. Currently, this will only submit the number of objects which have been built in the new action -- so in order to add more items, you'll have to build more

nested_form not saving to model Rails 3

I think I am on the right path for the following, though i cannot save the form data to the model. I have 2 models
class Prediction < ActiveRecord::Base
attr_accessible :home_team, :away_team, :home_score, :away_score, :fixtures_attributes
has_many :fixtures
accepts_nested_attributes_for :fixtures
end
class Fixture < ActiveRecord::Base
attr_accessible :home_team, :away_team, :fixture_date, :kickoff_time
belongs_to :predictions
end
To create a new prediction record i have a form that takes all the fixtures and pre populates the form and the user will just add scores next to each team
<%= form_for #prediction do |f| %>
<!-- Gets all fixtures -->
<%= f.fields_for :fixtures, #fixtures<!-- grabs as a collection --> do |ff| %>
<%= ff.text_field :home_team %> VS <%= ff.text_field :away_team %><%= f.text_field :home_score %><%= f.text_field :away_score %><br>
<% end %>
<%= f.submit 'Submit Predictions' %>
<% end %>
Then i have my controller to take care of the new/create action, which i think is where i may be falling over
class PredictionsController < ApplicationController
def new
#prediction = Prediction.new
#prediction.fixtures.build
#fixtures = Fixture.all
end
def create
#prediction = Prediction.new(params[:prediction])
#prediction.save
if #prediction.save
redirect_to root_path, :notice => 'Predictions Submitted Successfully'
else
render 'new'
end
end
end
and finally my routes
resources :predictions
resources :fixtures
So when i submit the form i get the error
ActiveRecord::RecordNotFound in PredictionsController#create
Couldn't find Fixture with ID=84 for Prediction with ID=
Looking at the params being parsed (snapshot below), something does not look right, for one the home_score and away_score are not being passed through.
{"utf8"=>"✓",
"authenticity_token"=>"DfeEWlTde7deg48/2gji7zSHJ19MOGcMTxEsQEKdVsQ=",
"prediction"=>{"fixtures_attributes"=>{"0"=>{"home_team"=>"Arsenal",
"away_team"=>"Norwich",
"id"=>"84"},
"1"=>{"home_team"=>"Aston Villa",
"away_team"=>"Fulham",
"id"=>"85"},
"2"=>{"home_team"=>"Everton",
"away_team"=>"QPR",
"id"=>"86"}
Current Output of form
Any advice appreciated
Thanks
When you set #fixtures to Fixture.all in the new method in your prediction controller, you are including every fixture, not just the fixtures belonging to your prediction. When the results of the form are passed to the create controller there are fixtures associated with other predictions being passed in which is the source of the error you have reported. Perhaps you want something like #fixtures = #prediction.fixtures.
What you are doing in the fields_for block also looks fairly wrong to my eyes. You are using f.text_field for your home_score and away_score inputs. This will repeat the same form element for the prediction model in each fixture field. You won't get the result you want from this. To be honest, I'm struggling to understand how this association makes sense. Are you able to explain it a little better? My suspicion is that your models are not quite set up the way you need them to be.
edit:
OK, I think I have a better idea of what you're trying to achieve now. You're trying to create many predictions in one form and prefill the home_team and away_team from a list of existing fixtures, right?
Okay, assuming that is so, you're definitely approaching it the wrong way. You don't need a many-to-one relationship between your prediction and fixture models (as you have correctly surmised). What you need to do is generate your form by iterating over the collection of fixtures and populating the home_team and away_team fields from the current fixture instance. Do you actually need these to be editable text fields, or are you just putting them in a text field so they get passed through? If so, you could use hidden fields instead.
The problem now though, is that Rails doesn't easily allow creating multiple records in the one form. It's not something I've done before and it would take me quite a bit of trial-and-error to make it work, but here's a best guess for one way of making it so.
models
class Prediction < ActiveRecord::Base
attr_accessible :home_team, :away_team, :home_score, :away_score, :fixtures_attributes
end
class Fixture < ActiveRecord::Base
attr_accessible :home_team, :away_team, :fixture_date, :kickoff_time
end
controller
class PredictionsController < ApplicationController
def new
#prediction = Prediction.new
#fixtures = Fixture.all
end
def create
begin
params[:predictions].each do |prediction|
Prediction.new(prediction).save!
end
redirect_to root_path, :notice => 'Predictions Submitted Successfully'
rescue
render 'new'
end
end
end
view
<%= form_tag controller: 'predictions', action: 'create', method: 'post' do %>
<% #fixtures.each do |fixture| %>
<%= fixture.home_team %> vs <%= fixture.away_team %>
<%= hidden_field_tag "predictions[][home_team]", fixture.home_team %>
<%= hidden_field_tag "predictions[][away_team]", fixture.away_team %>
<%= text_field_tag "predictions[][home_score]" %>
<%= text_field_tag "predictions[][away_score]" %><br />
<% end %>
<%= submit_tag "Submit predictions" %>
<% end %>
So essentially I'm creating a form that returns an array of parameters. I can't use the model form helpers in this situation.
One way around this would be to create a dummy model that has_many predictions and create a nested form using this.
Anyway, that's a lot of untested code that may never get looked at so I'm going to leave it there for now.
I haven't spent a lot of time looking because my wife is making me leave soon. But if I understand things right each of your rows in your view is from fixtures and predictions model association. The params hash you are getting is a mess because default behavior is going to be a view that is updating a single record ID. So you are bringing multiple records into a single view and trying to update multiple records at once.
Your create method in your model
#prediction = Prediction.new(params[:prediction])
But your params hash being passed is this:
{"utf8"=>"✓",
"authenticity_token"=>"DfeEWlTde7deg48/2gji7zSHJ19MOGcMTxEsQEKdVsQ=",
"prediction"=>{"fixtures_attributes"=>{"0"=>{"home_team"=>"Arsenal",
"away_team"=>"Norwich",
"id"=>"84"},
"1"=>{"home_team"=>"Aston Villa",
"away_team"=>"Fulham",
"id"=>"85"},
"2"=>{"home_team"=>"Everton",
"away_team"=>"QPR",
"id"=>"86"}
So you have to get more logic in your create method in your predictions controller to iterate through the hash you are receiving and save each row one at a time.

Ruby on Rails -- Saving and updating an attribute in a join table with has many => through

To simplify things, I have 3 tables :
Person
has_many :abilities, through => :stats
Ability
has_many :people, through => :stats
Stats
belongs_to :people
belongs_to :abilities
Stats has an extra attribute called 'rating'.
What I'd like to do is make an edit person form that always lists all the abilities currently in the database, and lets me assign each one a rating.
For the life of me, I can't figure out how to do this. I managed to get it to work when creating a new user with something like this:
(from the people controller)
def new
#character = Character.new
#abilities = Ability.all
#abilities.each do |ability|
#person.stats.build(:ability_id => ability.id )
end
end
From the people form:
<% for #ability in #abilities do %>
<%= fields_for "person[stats_attributes]" do |t| %>
<div class="field">
<%= t.label #ability.name %>
<%= t.hidden_field :ability_id, :value => #ability.id, :index => nil %>
<%= t.text_field :rating, :index => nil %>
</div>
<% end %>
<% end %>
This successfully gives me a list of abilities with ratings boxes next to them, and lets me save them if i'm making a new user.
The problem is that if I then load up the edit form (using the same form partial), it doesn't bring back the ratings, and if I save, even with the exact same ratings, it creates duplicate entries in the stats table, instead of updating it.
I realize I'm a terrible programmer and I'm probably doing this the wrong way, but how do I get the edit form to recall the current ratings assigned to each ability for that user, and secondly how do i get it to update the rating instead of duplicating it if the combination of person and ability already exists?
Shouldn't that be
Character
has_many :stats
has_many :abilities, through => :stats
Ability
has_many :stats
has_many :characters, through => :stats
Stat
belongs_to :character
belongs_to :ability
?
Also, is it Person or Character? You refer variously to both. (I'm going to go with Character in my answer)
I think you've fallen foul of the "I'll try to make a simplified version of my schema in order to attempt to illustrate a problem but instead make things more complex and muddle the issue by screwing it up so it doesn't make sense" syndrome. Anyway, there's a couple of issues i can see:
1) first thing is that you're adding all the possible abilities to a character as soon as they're created. This is silly - they should start out with no abilities by default and then you create join table records (stats) for the ones they do have (by ticking checkboxes in the form).
2) A simple way to manipulate join records like this is to leverage the "ability_ids=" method that the has_many :abilities macro gives you - referred to as "collection_ids=" in the api http://railsbrain.com/api/rails-2.3.2/doc/index.html?a=M001885&name=has_many
In other words, if you say
#character.ability_ids = [1,12,30]
then that will make joins between that character and abilities 1, 12 and 30 and delete any other joins between that character and abilities not in the above list. Combine this with the fact that form field names ending in [] put their values into an array, and you can do the following:
#controller
def new
#character = Character.new
#abilities = Ability.all
end
#form
<% #abilities.each do |ability| %>
<div class="field">
<%= t.label #ability.name %>
<%= check_box_tag "character[ability_ids][]" %>
</div>
<% end %>
#subsequent controller (create action)
#character = Character.new(params[:character]) #totally standard code
Notice that there's no mention of stats here at all. We specify the associations we want between characters and abilities and let rails handle the joins.
Railscasts episodes 196 and 197 show how to edit several models in one form. Example shown there looks similar to what you're trying to do so it might help you out (same episodes on ascicasts: 196, 197).

Resources