Create multiple users in one form - ruby-on-rails

I have Event model and a show page which shows the #event page and the customer registration form.
Now, my client wants a form for a different kind of event, which would have customer fields as well but labeled not customer name but parent / carer's name (similar form for a customer) and he also wants a Add player button on the bottom which would allow the parent /carer to add max 3 players. So the buisness need is that a parent registers 'players'. It should be possible to add max 3 players. I am wondering how I could go about creating 4 customers in one form. To me it sounds a bit odd, to be honest. Is this even possible? Or should I introduce Parent model and a Player model and connect them with each other. So for specific kinds of events I would create a Parent/Carer and max 3 players.
<%= simple_form_for #customer do |f| %>
...
<% end %>

There is no complete details to give you an specific solution, but will guide you in the right direction to solve it with an example.
Let's say you have your Parent model and your players model, you want to add a parent with 3 players in the same form.
we define in your parent models that you can accept nested attributes for your players, so for example if you want to create a parent with some players, you can do something like Parent.create(params_with_players_too) and it will create the parent and create the players too, associated with that parent. of course, having in mind that the params comes in the correct way.
Class Parent < ActiveRecord::Base
has_many :books
accepts_nested_attributes_for :players
end
Class Player < ActiveRecord::Base
belongs_to :parent
end
after that, your form could be something like
<%= form_for #parent do |f| %>
<%= f.fields_for :players, [Player.new]*3 do |player| %>
<%= player.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
and in your controller, you have to permit the players params too, something like this
def create
#parent = Parent.new(parent_params)
if #parent.save
...
end
def parent_params
params.require(:parent).permit(:attribute1, players_attributes: [:id, :name])
end
of course, you will have to understand it and adapt it to your specific case, hope that this helps you.

Related

Edit/create nested resources in Formtastic (Rails)

This question is regarding Rails 4/postgresql and the app is hosted on Heroku.
I am making a Quiz-functionality on a website and I am wondering on how to implement the forms (using Formtastic) best to make this is easy as possible. I have three models:
Quiz (has_many :quiz_questions), e.g. "Test to see how awesome you are"
QuizQuestion(belongs_to :quiz, has_many :quiz_options). e.g. "1. Which is your favorite color")
QuizOption (belongs_to :quiz_question). e.g. "Blue"
I have set up the forms like this:
<%= semantic_form_for([:admin, #quiz], :url => admin_quiz_path(#quiz.id)) do |f| %>
<%= render 'form' , :f => f %>
<% end %>
where the form looks like this:
<%= f.inputs %>
<h3>Quiz questions</h3>
<%= f.semantic_fields_for :quiz_questions do |qq_f| %>
<%= qq_f.inputs %>
<h4>Quiz options</h4>
<%= qq_f.semantic_fields_for :quiz_options do |qqo_f| %>
<%= qqo_f.inputs %>
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit %>
or go <%= link_to 'back', admin_quizzes_path %>
<% end %>
It seems, however, not to be working the way I want. I expect to be able to see the fields of QuizQuestion and QuizOptions in this form (there are objects for those) but I don't.
More importantly is that I would like to be able to create a New QuizQuestion and subsequently QuizOption in this form. It doesn't necessarily have to be jQuery/ajax or anything but I would like to do it all from this form.
Basically, I would like my workflow to be like:
Create a Quiz and add values to it. Click Create.
Add QuizQuestion number one and add the values to it (like "name label"). Click Create.
Add QuizOption related to QuizQuestion number one, and its "name label". Click create.
Repeat for QuizQuestion/QuizOption until the Quiz is done.
How can I do this?
For your workflow you might have to add accept_nested_attributes_for for the nested resources, this way when creating an object object you can actually create nested children (as long as they fulfill all the validations). This way:
# A quiz :has_many :quiz_questions
#quiz = Quiz.create(...)
with a declaration like:
has_many :quiz_questions
accepts_nested_attributes_for :quiz_questions
in your Quiz model you'll actually be able to create QuizQuestion from the quiz model like:
# using the previously quiz model
quiz.quiz_questions.create(...)
Doing the same for the deeply nested associations will do have the same effect.
Perhaps the reason why you don't see any field on the form is because there is not nested object created. Let me explain. When you create a new Quiz object, in your quizs_controller (or whatever the inflection for quiz is...) you need a:
def new
quiz = Quiz.new()
end
and
def create
Quiz.new(quiz_params)
end
private
def quiz_params
# whitelisted parameters sent along with the form
params.require(:quiz).permit(...)
end
if you actually want to be able to see the fields in the form you'll have to use the build method and actually populate that new object with respective the nested resources.
Note that for this to work with the form you will have to whitelist in the quizzes_controller the right attributes. You can debug the params you receive once you send the new quiz formulary and check that everything is right.
TIP! if you don't want to worry about the JS when adding nested resources dynamically, I recommend you using the cocoon gem

simple_form rails 4, set automatic association

is a little project and I try to associate patient model with consultations. one patient has_many :consultations, in my form I have:
<%= f.association :patient %>
I pass the id parameter from the patient to the action 'new' in this way:
<%= link_to new_consultum_path(:id => #patient.id) %>
And in the view I have:
How can I make that the f.association field take the correspondent patient_id automatically?
How can I be sure that the patient_id is the current patient?
If I want to hide this field is that ok if I put
instead of
Is a better way to do this?
And why in the view shows me # patient:0x007f4e7c32cbd0 ?
thanks for your help.
And why in the view shows me # patient:0x007f4e7c32cbd0
This is a Patient object.
It means you need to call an attribute of this object - EG #patient.name.
--
f.association field take the correspondent patient_id automatically
This might help:
It looks like Organization model doesn't have any of these fields: [
:to_label, :name, :title, :to_s ] so SimpleForm can't detect a default
label and value methods for collection. I think you should pass it
manually.
#app/models/patient.rb
class Patient < ActiveRecord::Base
def to_label
"#{name}"
end
end
Apparently, you need to have either title, name or to_label methods in your model in order for f.association to populate the data.
-
How can I be sure that the patient_id is the current patient?
If you're having to verify this, it suggests inconsistencies with your code's structure. If you need the patient_id to be set as the current patient, surely you could set it in the controller:
#app/controllers/consultations_controller.rb
class ConultationsController < ApplicationController
def create
#consultation = Constultation.new
#consultation.patient = current_patient
#consultation.save
end
end
I can provide more context if required.
You want to associate consultations to patients using fields_for, which is similar to form_for, but does not build the form tags.
It you start with your patient object, you can iterate through the consultation associations binding it to form fields as you go.
it would look something like this
<%= form_for #patient do |patient_form| %>
<% patient_form.text_field :any_attribute_on_patient %>
<% #patient.consultations.each do |consultation| %>
<%= patient_form.fields_for consultation do |consultation_fields| %>
<% consultation_fields.text_field :any_attribute_on_consulatation %>
<% end %>
<% end %>
<% end %>
Sorry, the code may not be exactly right.
Check out the docs for field_for here
You will also have to set accepts_nested_attributes_for consultations on patient. When you set accepts_nested_forms_for, Rails will automatically update the associated consultations object to the patient and save any edits you have made. You DEFINITELY want to use accepts_nested_attributes_for most nested form handling of this type.

HABTM relationship with a text/hidden field for ids

I'm creating a has_and_belongs_to_many relationship with Rails. Each group has many participants and each participant can be part of many groups.
The relationship seems to be set up ok as I can use check boxes to add relationships using this in my form:
<%= collection_check_boxes(:group, :participant_ids, #participants, :id, :name) %>
However, I need to use a hidden field to submit these relationships (I use AJAX to fetch them in the view) with an array of ids (e.g. [1, 3]). I've tried using a text field like this but it doesn't save the data:
<%= f.text_field :participant_ids %>
When participant_ids saves using the checkboxes and I output it on the show view it's an array of ids but I can't seem to submit it in that format to start with.
Why can't I submit the participant_ids using a text/hidden field and is there a way around this?
For reference I've set up the join table and the models look like this:
class Group < ActiveRecord::Base
has_and_belongs_to_many :participants
end
class Participant < ActiveRecord::Base
has_and_belongs_to_many :groups
end
I also modified the group controller to work with strong parameters like this:
def group_params
params.require(:group).permit(:user_id, :purpose, :participant_ids => [])
end
I can post more code if necessary.
This answer worked for me. You will have to
<% #participants.each do |participant| %>
<% f.hidden_field 'participant_ids][', :value => participant.id %>
<% end %>

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.

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