Rails 5: Is Nested forms set up correct - ruby

If I want a Nested Form for volunteer_shift
inside my assignment/_form
How should my model associations be set up?
My current associations look like this..but I am not sure they are correct if the volunteer_shift partial sits inside the assignment form....
When I try to open the "add new record" form...my assignment#new action errors out...
error message
Completed 500 Internal Server Error in 30ms (ActiveRecord: 2.7ms)
NoMethodError - undefined method `build' for nil:NilClass:
app/controllers/assignments_controller.rb:21:in `new'
Here is my new action...
# GET /assignments/new
def new
#assignment = Assignment.new
# binding.pry
#assignment.volunteer_event.build <----Line 21
#my_url = {:action => "create", :id => params[:id]}
end
Here are my current models associations...
class Assignment < ApplicationRecord
attr_accessor :volunteer_event
belongs_to :volunteer_shift
has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type
belongs_to :contact ,optional: true
validates_presence_of :volunteer_shift #belongs_to takes care of this now
validates_associated :volunteer_shift
accepts_nested_attributes_for :volunteer_shift, allow_destroy: true
...
class VolunteerShift < ApplicationRecord
has_many :assignments
belongs_to :volunteer_event
...
class VolunteerEvent < ApplicationRecord
belongs_to :volunteer_default_event
has_many :volunteer_shifts, :dependent => :destroy
has_many :resources_volunteer_events, :dependent => :destroy
validates_associated :volunteer_shifts
And inside my assignment/form
<%= form_for #assignment, :url => #my_url, remote: true do |f| %>
...
<!--VOLUNTEER SHIFT-->
<!--TODO: make this a partial under field_for-->
<%= f.fields_for :volunteer_shift do |builder| %>
<%= render 'volunteer_shift_fields', vs: builder %>
<% end %>
...
<% end %>
and if you want to see the volunteer_shift_fields partial
<div class="name large flex-row">
<%= vs.label :volunteer_shift %>
</div>
<div id="volunteer_shift" class="d-flex flex-row">
<div class="col-sm-12 p-2">
<div id="volunteer_shift" class="text-right">
<!-- old if: if class is assignment show volunteer shift else show default shift -->
<!-- we need default shift here...NO assignment is attached-->
<div class="field">
<%= vs.label :volunteer_task_type_id %>
<%= select_tag 'volunteer_task_type_id', options_from_collection_for_select([VolunteerTaskType.new(:description => ""), VolunteerTaskType.instantiables.effective_on(Date.today)].flatten, "id", "description") %>
</div>
<div class="field">
<%= vs.label :roster_id %>
<%= select_tag 'roster_id', options_from_collection_for_select([Roster.new(:name => ""), Roster.all].flatten, "id", "name") %>
</div>
<div class="field">
<%= vs.label :program_id %>
<%= select_tag 'program_id', options_from_collection_for_select([Program.new(:name => ""), Program.where(:volunteer => true)].flatten, "id", "name")%>
</div>
<div class="field">
<%= vs.label :set_description %>
<%= vs.text_field(:set_description, nil) %>
</div>
<div class="field">
<%= vs.label :set_date, "Date" %> <
<%= vs.text_field(:set_date, nil) %>
</div>
</div>
</div>
</div>
Are my model associations correct?
What is my "assignment#new" action missing?

Remove attr_accessor :volunteer_event, you need to set up the model association to volunteer_event through volunteer_shift
so
class Assignment < ApplicationRecord
attr_accessor :volunteer_event
needs to become
class Assignment < ApplicationRecord
has_one :volunteer_event, through: :volunteer_shift
Looking at your volunteer_shift associations I'm pretty sure that you only want to ever have a single volunteer_event record for a single assignment? if that's not what you want then you need to look at your associations again
Or...
You could change the attr_accessor to attr_reader and assign the relevant volunteer_event record in an initializer but I would not recommend that approach for this case

Related

rails 5.1: nested form validation

I have a parent model called user and 2 nested models called award and certification. when I go to save a certification the model validation for award tells me that awards is blank, and when I got to save an award I get an error that says the validations for certification is blank. not sure what's going on.
here my user model
has_many :certifications, dependent: :destroy
has_many :awards, dependent: :destroy
# allows for nested attributes in the user views.
accepts_nested_attributes_for :work_histories, :qualifications, :certifications, :courses, :awards,
:languages, :patents, :publications, allow_destroy: true
here my award model
class Award < ApplicationRecord
belongs_to :user
validates :award_name, :award_granted_by, :award_date, :award_description, presence: true
end
and my certification model
class Certification < ApplicationRecord
belongs_to :user
validates :certification_name, :certification_authority, :certification_number, :certification_from,
:certification_to, presence: true
end
strong params in users_controller
def user_params
params.require(:user).permit( :first_name, :last_name, :email, :phone, :current_job_title, :password, :password_confirmation,
work_histories_attributes: [:user_id, :id, :_destroy, :job_title, :company, :description, :city, :state,:start_date, :end_date],
qualifications_attributes: [:user_id, :id, :_destroy, :education, :university, :university_major, :graduation_year, :currently_enrolled, :location],
awards_attributes: [:user_id, :id, :_destroy, :award_name, :award_granted_by, :award_date, :award_description],
certifications_attributes: [:user_id, :id, :_destroy, :certification_name, :certification_authority, :certification_number, :certification_from, :certification_to, :cert_never_expires],
courses_attributes: [:user_id, :id, :_destroy, :course_name, :course_number, :course_description],
languages_attributes: [:user_id, :id, :_destroy, :language, :language_proficiency],
patents_attributes: [:user_id, :id, :_destroy, :patent_title, :patent_office, :patent_number, :patent_status, :patent_date, :patent_description],
publications_attributes: [:user_id, :id, :_destroy, :publication_title, :publication_source, :publication_date, :publication_description] )
end
here's a part of the User form. I've got a button dropdown that has several options, when a user selects an option that particular div will be shown with a modal window.
<!-- one-to-many nested attributes -->
<%= form_for(#user) do |form| %>
<!-- a partial is rendered based on the user dropdown selection -->
<div id="award">
<%= render partial: "awards/new_award_modal", locals: { form: form } %>
</div>
<div id="cert">
<%= render partial: "certifications/new_certification_modal", locals: { form: form } %>
</div>
<%= form.submit 'Save', id: "submit-achievement", class: 'btn btn-primary form-control' %>
</div><!-- modal body -->
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
<% end %><!-- form_for -->
here is the partial for certifications
<div class="new_cert">
<%= form.fields_for :certifications, Certification.new do |cert_field| %>
<div class="field">
<%= cert_field.label :certification_name, 'Certification Title' %>
<%= cert_field.text_field :certification_name, class: "form-control" %>
</div>
<div class="field">
<%= cert_field.label :certification_authority %>
<%= cert_field.text_field :certification_authority, class: "form-control" %>
</div>
<div class="field">
<%= cert_field.label :certification_number %>
<%= cert_field.text_field :certification_number, class: "form-control" %>
</div>
<table>
<tr>
<td>
<div class="field">
<%= cert_field.label :certification_from, "From" %><br />
<%= cert_field.date_select :certification_from,{ :order => [:day, :month, :year],
:start_year => Date.current.year, :end_year => 1960,
:prompt => {day: 'Day', month: 'Month', year: 'Year'}, :discard_day => true }, class: 'form-control' %>
</div>
</td>
<td> </td>
<td>
<div class="field">
<%= cert_field.label :certification_to, "To" %><br />
<%= cert_field.date_select :certification_to, { :order => [:day, :month, :year],
:start_year => Date.current.year + (10), :end_year => 1960,
:prompt => {day: 'Day', month: 'Month', year: 'Year'}, :discard_day => true }, class: 'form-control' %>
</div>
</td>
</tr>
</table>
<div>
<%= cert_field.label :cert_never_expires, "This certification does not expire", class: "l" %>
<%= cert_field.check_box :cert_never_expires, class: 'cb' %>
</div>
<br />
<% end %>
</div><!-- ./new_cert -->
here's the awards partial
<div class="new_award">
<!-- nested attributes for award modal -->
<%= form.fields_for :awards, Award.new do |award_field| %>
<div class="field">
<%= award_field.label :award_name, 'Award or Honor Name' %>
<%= award_field.text_field :award_name, class: "form-control" %>
</div>
<div class="field">
<%= award_field.label :award_granted_by, "Granted by" %>
<%= award_field.text_field :award_granted_by, class: "form-control" %>
</div>
<div class="field form-inline">
<%= award_field.label :award_date, 'Date' %><br />
<%= award_field.date_select :award_date, { :order => [:day, :month, :year],
:start_year => Date.current.year, :end_year => 1960,
:prompt => {day: 'Day', month: 'Month', year: 'Year'},
:discard_day => true }, class: 'form-control ds' %>
</div>
<div class="field">
<%= award_field.label :award_description, 'Description' %>
<%= award_field.text_area :award_description, class: "form-control" %>
</div>
<% end %><!-- ./fields_for -->
</div>
so both awards and certifications can be saved to the database without any issue when there are no validations. however, when I just do a simple validation checking for presence of an attribute in the form that's when things get weird. The award model is saying that the certification form is empty, which it should be because I'm submitting the award form. And the opposite is also true, when I go to submit a certification the award validation is triggered saying the award form is blank.
It would be best to see the specific error you're getting, but I'd make sure that your form is setting the nested attributes appropriately, and that your user controller has nested attributes setup within the strong parameters.
There's documentation in the Rails guides: http://api.rubyonrails.org/classes/ActionController/StrongParameters.html

Adding Categories In Rails 3

I am building an uploader for videos and giving the videos a category through categorizations. Every time I try to upload a video I receive an error saying
NameError in VideosController#create
uninitialized constant Video::Categorization
I want to able to add one category to each video. But no regardless of how I write the association I get the same error.
model
class Video < ActiveRecord::Base
attr_accessible :source, :title, :url, :description,
:category, :category_id, :category_list
belongs_to :user
has_many :category, through: :categorizations
has_many :categorizations
validates :category, presence: true
has_attached_file :source
def source_remote_url=(url_value)
self.source = URI.parse(url_value) unless url_value.blank?
super
end
def self.categorized_with(name)
Category.find_by_name!(name).videos
end
def category_list
["Action", "Anime",
"Arts and Culture", "Beauty", "Business", "Comedy",
"Documentary", "Drama",
"Food", "Gaming", "Health and Fitness", "Horror"]
end
def category_list=(names)
self.category = names.split(",").map do |n|
Category.where(name: n.strip).first_or_create!
end
end
end
Video Controller
class VideosController < ApplicationController
before_filter :authenticate_user!
before_filter :set_video, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#videos = Video.all
end
def show
respond_with(#video)
end
def new
#video = Video.new
respond_with(#video)
end
def edit
end
def create
#video = Video.new(params[:video])
#video.save
respond_with(#video)
end
def update
#video.update_attributes(params[:video])
respond_with(#video)
end
def destroy
#video.destroy
respond_with(#video)
end
private
def set_video
#video = Video.find(params[:id])
end
end
Form
<%= form_for(#video) do |f| %>
<% if #video.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#video.errors.count, "error") %> prohibited this video from being saved:</h2>
<ul>
<% #video.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :source %><br />
<%= f.file_field :source %>
</div>
<div class="field">
<%= f.label "Category", class: 'control-label' %>
<%#= f.select :category, Category.all, :prompt => 'Select One' %>
<%= f.select :category_list, video_category, :prompt => "Select a category..." %>
</div>
<div class="field">
<%= f.label :description %><br />
<%= f.text_area :description, rows: 4, placeholder: "Description" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
If you declare
has_many :category, through: :categorizations
it should be
has_many :categories, through: :categorizations

Inserting multiple rows in Rails for single form

There are three fields in this form:
employee
project
project
The field project comes up twice and so I want two records created in this case. If I put in the values:
employee: John Doe
project: Project_1
project: Project_2
I would like two records in the model:
employee: John Doe; project: Project_1
employee: John Doe; project: Project_2
This is the view:
<%= simple_form_for(#source) do |f| %>
<div class="form-group">
<%= f.label :employee %>
<%= f.text_field :employee, class: "form-control" %>
</div>
<div class="form-group">
<%= f.input :project, class: "form-control" %>
<%= f.input :project, class: "form-control" %>
</div>
<% end %>
Here is my code for the application controller:
def create
#source = Source.new(source_params)
if #source.save
redirect_to #source, notice: 'Source was successfully created.'
else
render action: 'new'
end
end
Any help will be greatly appreciated.
In your controller, you should build the model along with two projects, then use the form helper fields_for, which will render both project fields
your_controller.rb
class YourController
def your_action_new
#object = YourModel.new
2.times{ #object.projects.build }
end
end
For the view, I don't really know how simple_form behaves, but basically
views/your_views/new.html.erb
<!-- blabla -->
<%= f.fields_for :project do |project_f| %>
<div class="project">
<%= project_f.text_field(:name) %>
<%= project_f.text_field(:description) %>
...
</div>
<% end %>
ALso don't forget in to accept nested attributes
class YourModel
has_many :projects, dependent: :destroy
accept_nested_attributes_for :projects
end
class YourController
def your_model_params
params.require(:your_model).permit(blabla, projects_attributes: [:id, :name, :blabla, ...])
end
end
end

how to do a many to many model for this project in rails

I'm having trouble creating a many to many model for my project.
Basically i have a Matches & Teams model.
Teams are created prior to the Matches.
Once the match is created then i would like to add teams to it.
Match can have many teams, Teams can have many matches.
I'm currently adding teams via nested_form and adding multiple teams at once.
When submitting the form, i get an error expecting the team to be in a relationship already with the match.
I can do this with a many to one relationship but it fails with many-to-many, was wondering if there was any way to do it without doing a custom route.
Below is the form, controllers are as per default values.
Form:
<%= nested_form_for(#match) do |f| %>
<% if #match.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#match.errors.count, "error") %> prohibited this match from being saved:</h2>
<ul>
<% #match.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 :date %><br />
<%= f.date_select :date %>
</div>
<%= f.fields_for :teams, :html => { :class => 'form-vertical' } do |builder| %>
<%= builder.label "Team Name:" %>
<%= builder.autocomplete_field :name, autocomplete_team_name_teams_path, :update_elements => {:id => "##{form_tag_id(builder.object_name, :id)}" },:class => "input-small",:placeholder => "Search" %>
<%= builder.hidden_field :id %>
<% end %>
<%= f.link_to_add raw('<i class="icon-plus-sign"></i>'), :teams, :class => 'btn btn-small btn-primary' %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Using a join model, the has_many :through macro and the accepts_nested_attributes_for macro you can do something like the following.
class Match
has_many :competitions
has_many :teams, :through => :competitions
accepts_nested_attributes_for :teams
end
class Competition
belongs_to :match
belongs_to :team
end
class Team
has_many :competitions
has_many :matches, :through => :competitions
end
Just make sure your form is set up to send the following data structure as params when the request reaches the create or update controller.
params => {
:match => {
# ...
:teams_attributes => [
{ :name => 'Foo', :color => 'blue' },
{ :name => 'Bar', :color => 'green' },
# ...
]
}
}

ruby - populating combobox

I have two objects ingredient & origin.
each ingredient has an origin so in the ingredient I have origin_id
the view displays
<p>
<b>Name:</b>
<%= #ingredient.name %>
</p>
<p>
<b>Origin:</b>
<%= #ingredient.origin_id %>
</p>
class ingredient is declared as follows
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
belongs_to :origin
attr_accessible :name, :origin_id
end
class origin
class Origin < ActiveRecord::Base
attr_accessible :name
end
in the edit form I write and it works
<% originsArray = Origin.all.map { |origin| [origin.name, origin.id] } %>
<div class="field">
<%= f.label :origin_id %><br/>
<%= f.select(:origin_id, originsArray) %><br/>
</div>
but if I write instead (as in all tutorials)
<%= collection_select(:origin, :id, #origins, :id, :name, options ={:prompt => "-Select a payment"}, :class =>"origin") %>
i get
undefined method `map' for nil:NilClass
what should I fix ?
Edit
added :
def edit
#ingredient = Ingredient.find(params[:id])
#origins = Origin.all
end
I don't see here where #origins is declarated. It seems like your #origins is just nil and not collection.

Resources