Changing from has_many to has_one relation rails - ruby-on-rails

Here personaldetails belongs_to user and the relation given is has_many which is wrong.I want to convert the has_many relation to has_one relation i.e. User has_one personaldetails. When I change the relation directly I am getting an error "uninitialized constant User::Personaldetails. Please guide me how to convert the relation .
Personaldetail.rb
class Personaldetail < ApplicationRecord
belongs_to :user
end
User.rb
class User < ApplicationRecord
has_many :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end
routes.rb
resources :users, except: [:new] do
resources :personaldetails
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps : :personaldetails
def show
#user = current_user
#personaldetails = #user.personaldetails.build
render_wizard
end
def update
#user = current_user
#user.update!(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end
personaldetails.html.erb
<%= form_with(model: #user, url: wizard_path, local: true) do |form| %>
<%= form.fields_for :personaldetail,Personaldetail.new do |info| %>
<%= render 'personaldetails_field', form: info %>
<% end %>
<%= form.submit %>
<% end %>
_personaldetails_field.html.erb
<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>
So the solution is:
Personaldetail.rb
class Personaldetail < ApplicationRecord
belongs_to :user
end
User.rb
class User < ApplicationRecord
has_one :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end
routes.rb
resources :users, except: [:new] do
resources :personaldetail
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps : :personaldetails
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.update!(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end
personaldetail.html.erb
<%= form_with(model: #user, url: wizard_path, local: true) do |form| %>
<%= form.fields_for :personaldetail,#user.personaldetail || #user.build_personaldetail do |info| %>
<%= render 'personaldetail_field', form: info %>
<% end %>
<%= form.submit %>
<% end %>
_personaldetail_field.html.erb
<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>

try with: has_one :personaldetail, dependent: :destroy
Rails are guessing class name from name AND type of association, so with has_many they will try to singularize association name (personaldetails => Personaldetail) but with has_one they will try to reach it as is (personaldetails => Personaldetails)

As in the comment by spickermann, has_many relationship wants plural form and has_one the singular form.
That is to say, you should already be able to infer the relationship from:
#user.personaldetails # user has many personal details
#user.personaldetail # user has one personal detail
Just a consideration: many weird cases arise when objects/models are not properly named. As a rule of thumb, you should use the most fitting and precise English noun for the object you need to name. That will help you hugely in cases like this. In normal English language, it is somehow strange to say "a user has a personal detail" but you would say of course "has personal details". Particularly when it comes to ActiveRecord associations, Rails syntax should be the nearest as possible to English language, to avoid later misunderstandings. I guess this confusion would not have arisen if instead of "PersonalDetail", the model was called "Account" or "Profile", for instance.

Few suggestions/comments
Keep model name as CamelCase like PersonalDetail rather than Personaldetail and association name has_one :personal_detail
Using has_one relation you can create the object using user.build_personal_detail.save
When you run the 2nd step again it will create another record in personal_details table and in that transaction it will return the new record. But, after that when you try to query it will return the 1st created personal_details record rather than new one. That's because ActiveRecord by default sorts by id and limit 1 for has_one relation

Related

Rails 4: How to update a collection_select based on another collection_select through AJAX?

The problem:
I need to filter a collection of Units based upon the selection of a collection of Organizations.
Upon selecting an Organization, the Unit dropdown-menu should refresh to show only the Units that belong to informed Organization.
I've checked these questions:
Rails forms: updating collection_select options based on other collection_select value using AJAX
UJS, AJAX, Rails 4, form_for collection_select to pass value into method and return value back to form
ajax in rails with collection_select
How to add an onchange event to select tag in rails
Rails forms: updating collection_select options based on other collection_select value using AJAX
Rails form_for collection_select ignoring remote ajax call that select_tag accepts
remote select, controller needs more form data
And these documentations:
http://guides.rubyonrails.org/form_helpers.html
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-options_from_collection_for_select
This is my code so far:
Models
class Organization < ActiveRecord::Base
has_many :units
has_many :projects, through: :units
end
class Unit < ActiveRecord::Base
belongs_to :organization
has_many :projects
end
class Project < ActiveRecord::Base
belongs_to :organizaton
has_one :organization, through: :unit
end
Views
app/views/projects/_form.html.erb
<%= form_for(#project) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :organization_id %><br>
<%= f.collection_select :organization_id, Organization.all, :id, :name %>
</div>
<div class="field">
<%= f.label :unit_id %><br>
<%= f.collection_select :unit_id, Unit.all.where(organization_id: :organization_id), :id, :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Controllers
Projects Controller
def new
#project = Project.new
end
def project_params
params.require(:project).permit(:name, :description, :organization_id, :unit_id)
end
How can I make this work?
I made it, the solution was pretty simple, but the lack of updated material regarding this simple issue made it a chore bigger than it should have:
config/routes.rb
get 'filter_units_by_organization' => 'projects#filter_units_by_organization'
controllers/projects_controller.rb
def filter_units_by_organization
#filtered_units = Unit.where(organization_id: params[:selected_organization])
end
views/projects/filter_units_by_organization.js.erb
$('select#project_unit_id').html('<%= j options_from_collection_for_select(#filtered_units, :id, :name) %>');
assets/javascripts/application.js
$(function() {
$("select#project_organization_id").on("change", function() {
$.ajax({
url: "/filter_units_by_organization",
type: "GET",
data: { selected_organization: $("select#project_organization_id").val() }
});
});
});
views/projects/_form.html.erb
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :organization_id %><br>
<%= f.collection_select :organization_id, Organization.all, :id, :name, { prompt: 'Please select' } %>
</div>
<div class="field">
<%= f.label :unit_id %><br>
<%= f.collection_select :unit_id, Unit.all.where(organization_id: :organization_id), :id, :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
Instead of placing an duplicate organization_id on the projects table you should set the relationship up to go through the Unit model.
class Organization < ActiveRecord::Base
has_many :units
has_many :projects, through: :units
end
class Unit < ActiveRecord::Base
belongs_to :organization
has_many :projects
end
class Project < ActiveRecord::Base
belongs_to :unit
has_one :organizaton, through: :unit
end
This avoids the awkward issue where you have to ensure that a unit and a project have the same organizaton_id.
This also means that you no longer need a input to select the organization when creating a Project - just the unit.

'Can't mass-assign protected attributes' when implementing Multiple Table Inheritance with nested forms

HI I am trying to implement the MTI in my application. I have a Person Model and 2 models inheriting from it: Client and TeamMember. When creating a Team Member I want to save to to database vallues for both person (first and last name, email etc) and team member(experience level, type of team, if lead or not). I am using the nested attributes form so in my team member form I am nesting the person fields. Unfortunatellly I am getting "Can't mass-assign protected attributes: person" error when trying to save. Can anyone tell me how this can be solved? Thanks!
Models:
UPDATED TeamMember class but still the same error
also tried people_attributes and persons_attributes and none of these worked
class TeamMember < ActiveRecord::Base
has_many :project_team_members
has_many :projects, through: :project_team_members
has_one :person, as: :profile, dependent: :destroy
accepts_nested_attributes_for :person
attr_accessible :person_attributes, :experience_level, :lead, :qualification, :team
end
class Person < ActiveRecord::Base
belongs_to :company
belongs_to :profile, polymorphic: true
attr_accessible :email, :first_name, :last_name, :phone_number, :profile_id, :profile_type
end
Controller as follows:
class TeamMembersController < ApplicationController
def create
person = Person.create! { |p| p.profile = TeamMember.create!(params[:team_member]) }
redirect_to root_url
end
and the view:
<%= form_for(#team_member) do |f| %>
<%= f.fields_for :person do |ff| %>
<div>
<%= ff.label :first_name %>
<%= ff.text_field :first_name %>
</div>
<div>
<%= ff.label :last_name %>
<%= ff.text_field :last_name %>
</div>
<div>
<%= ff.label :phone_number %>
<%= ff.text_field :phone_number %>
</div>
<div>
<%= ff.label :email %>
<%= ff.text_field :email %>
</div>
<div>
<%= ff.label :company_id %>
<%= ff.text_field :company_id %>
</div>
<% end %>
<div class="field">
<%= f.label :team %><br />
<%= f.text_field :team %>
</div>
<div class="field">
<%= f.label :experience_level %><br />
<%= f.text_field :experience_level %>
</div>
<div class="field">
<%= f.label :qualification %><br />
<%= f.text_field :qualification %>
</div>
<div class="field">
<%= f.label :lead %><br />
<%= f.check_box :lead %>
</div>
<div class="actions">
<%= f.submit %>
</div>
UPDATED TeamMembersController (Solution thanks to the courtesy of Tiago)
def new
#team_member = TeamMember.new
#team_member.build_person
respond_to do |format|
format.html # new.html.erb
format.json { render json: #team_member }
end
end
def create
#team_member = TeamMember.create!(params[:team_member])
redirect_to root_url
end
To mass assign attributes in a nested form, you'll need to specify:
class TeamMember < ActiveRecord::Base
has_many :project_team_members
has_many :projects, through: :project_team_members
has_one :person, as: :profile, dependent: :destroy
:experience_level, :lead, :qualification, :team #what is this line doing??
accepts_nested_attributes_for :person
attr_accessible :person_attributes
end
EDIT:
In the action called before the form you need to build person. Like:
#team_member = TeamMember.new
#team_member.build_person
Then you'll have one person (non-persisted) associated with #team_member.

Rails 4 oracle enhanced adapter many to many propagating data to the pivot table

First things first
Using:
rails4
oracle enhanced adapter rails4 branch
I have a many to many relationship mapped on an existing database.
My models look as such:
class EventMap < ActiveRecord::Base
self.table_name="TAKE_PART"
self.primary_key="id"
belongs_to :event, foreign_key: "lottery_event_id"
belongs_to :entrant, foreign_key: "address_id"
end
class Event < ActiveRecord::Base
self.table_name="THE_EVENT"
self.primary_key="id"
has_many :event_maps, :foreign_key => "lottery_event_id"
has_many :entrants, :through => :event_maps
accepts_nested_attributes_for :entrants, :reject_if => :all_blank
end
class Entrant < ActiveRecord::Base
self.table_name="ADDRESSES"
self.primary_key="id"
self.set_date_columns :date_of_birth
has_many :events, :through => :event_maps
has_many :event_maps, :foreign_key => "address_id"
end
static page controller for my voting page
...
def vote
#event=Event.find_by(id: 4227)
#entrants=#event.entrants
#entrant=#event.entrants.build
end
...
vote view:
<%= form_for(#event) do |f| %>
<%= f.fields_for :entrant do |builder| %>
<%= render "/entrants/fields", :f => builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
entrant/fields partial:
<% if #entrant.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#entrant.errors.count, "error") %> prohibited this entrant from being saved:</h2>
<ul>
<% #entrant.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.hidden_field :id %>
<div class="field">
<%= f.label :lastname %><br>
<%= f.text_field :lastname %>
</div>
<div class="field">
<%= f.label :firstname %><br>
<%= f.text_field :firstname %>
</div>
<div class="field">
<%= f.label :street %><br>
<%= f.text_field :street %>
</div>
<div class="field">
<%= f.label :country_id %><br>
<%= f.number_field :country_id %>
</div>
<div class="field">
<%= f.label :city %><br>
<%= f.text_field :city %>
</div>
<div class="field">
<%= f.label :telephone %><br>
<%= f.text_field :telephone %>
</div>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :date_of_birth %><br>
<%= f.date_select :date_of_birth %>
</div>
<div class="field">
<%= f.label :lang_id %><br>
<%= f.text_field :lang_id %>
</div>
<div class="field">
<%= f.label :added %><br>
<%= f.date_select :added %>
</div>
<div class="field">
<%= f.label :salut %><br>
<%= f.text_field :salut %>
</div>
<div class="field">
<%= f.label :zip %><br>
<%= f.text_field :zip %>
</div>
<div class="field">
<%= f.label :newsletter %><br>
<%= f.check_box :newsletter %>
</div>
<div class="field">
<%= f.label :company %><br>
<%= f.text_field :company %>
</div>
The form now submits to event controller PATCH
class EventsController < ApplicationController
before_action :set_event, only: [:show, :edit, :update, :destroy]
...
# PATCH/PUT /events/1
# --> HOW DO I SAVE THE SUBMITTED ENTRANT HERE??? <--
def update
respond_to do |format|
if #event.update(event_params)
format.html { redirect_to #event, notice: 'Event was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
...
private
# Use callbacks to share common setup or constraints between actions.
def set_event
#event = Event.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def event_params
params.require(:event).permit(:id, :lottery_id, :events_dates_id, :event_date, :event_location, :tickets_for_winner, :prize, :tickets_internally,
:entrants_attributes => [:id, :lastname, :firstname, :street, :country_id, :city, :telephone, :email, :date_of_birth, :lang_id, :added, :salut, :zip, :newsletter, :company])
end
end
How do I register an Entrant with an Event, adding data only to the intermediate model EventMap since the Event will always exist?
Do I need to include accepts_nested_attributes_for in my models to propagate changes accross tables (I could not quite figure out what this does from the documentation)? Do I need to send additional params via the Entrant form to update the EventMap?
Main GOAL: I want a form where Entrants can register to an existing event!
Hard to judge about Your forms, as we don't see any ;)
But You are right to be able to create nested attributes, from nested attributes from, You need to set accepts_nested_attributes_for :some_model, :some_other_model
If You find docs confusing, consult Railscasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
You are requiring custom logic and that custom logic must be defined, Rails will not automatically update everything, it must be defined in the update controller as you suggest.
I might do something along the lines of this:
def update
entrant = Entrant.find(params[:entrant_id])
event = Event.find(params[:event_id])
EventMap.create!(event: event, entrant: entrant)
#.... go on with usual stuff
# Alternatively you could use build
event_map = EventMap.new
event_map.build(event: event, entrant: entrant)
end

Nested form not rendering Rails 3.2

The nested form in the view just won't render, unless I remove the f attribute, in which case the submit button will not work. I have two models, job and employer. I've been following the railscast here
job.rb
attr_accessible :title, :location, :employers_attributes,
belongs_to :employers
accepts_nested_attributes_for :employers
employer.rb
attr_accessible :companyname, :url
has_many :jobs
jobs_controller.rb
def new
#job = Job.new
#employer = Employer.new
end
_form.html
<%= form_for(#job) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.fields_for :employers do |builder| %>
<%= builder.label :companyname, "Company Name" %>
<%= builder.text_field :companyname %>
<%= builder.label :url, "Web Address" %>
<%= builder.text_field :url %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any input would be brilliant - thanks
This happens because your job has no employers.
Change your code to this:
def new
#job = Job.new
#job.employer = #job.build_employer
end
In your job.rb change:
attr_accessible :title, :location, :employer_attributes,
belongs_to :employer
accepts_nested_attributes_for :employer
This line:
belongs_to :employers
Should be singulars:
belongs_to :employer
With this association you not need nested form you can use select for pick employer for each job.
But if you need many employers for each job and each job can have many employers see this screencast

How to display data from two tables into one form

I use Ruby v 3. I want to display data from 2 tables into one form.
My models:
class Address < ActiveRecord::Base
attr_accessible :city, :number, :street
validates :city, :presence => true
validates :number, :presence => true
validates :street, :presence => true
has_many :users
end
class User < ActiveRecord::Base
belongs_to :address
attr_accessible :name, :phone, :surname, :address_attributes
accepts_nested_attributes_for :address
end
My form looks alike:
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :surname %><br />
<%= f.text_field :surname %>
</div>
<div class="field">
<%= f.label :phone %><br />
<%= f.text_field :phone %>
</div>
<%= f.fields_for :address_attributes do |p| %>
<div class="field">
<%= p.label :city %><br />
<%= p.text_field :city %>
</div>
<div class="field">
<%= p.label :street %><br />
<%= p.text_field :street %>
</div>
<div class="field">
<%= p.label :number %><br />
<%= p.text_field :number %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
As you can see I use fields_for method. And my controller is here:
def edit
#user = User.find(params[:id])
#user.address_attributes = #user.address
end
It does not working and I totally don't know why. When I click edit on address list i've got an error:
undefined method `with_indifferent_access'
Anyone can help me figure it out?
Check out Episodes 196 and 197 on RailsCasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
There is a revised episode for 196, for which you will need to subscribe to RailsCasts.
I would highly recommend subscribing to learning sites like RailsCasts and CodeSchool, to lear RoR faster and in the right way.
Try to do:
def edit
#user = User.find(parmas[:id])
end
and on your view:
<%= f.fields_for :address do |p| %>
and see if it works, you don't have to add the attributes

Resources