I am trying to create a person object in my application but within the controller when #person.save! is called the exception ActiveRecord::RecordInvalid: Validation failed: Contacts contactable can't be blank is thrown. This is due to the contactable_id not being set.
I know that I can save the person and then create and save a contact, but would preder to save all in one hit. What am I missing?
The classes involved are below:
person.rb
class Person < ActiveRecord::Base
has_many :contacts, as: :contactable
accepts_nested_attributes_for :contacts
end
contact.rb
class Contact < ActiveRecord::Base
belongs_to :contactable, polymorphic: true
end
The form posts the following:
{"commit"=>"Add Person",
"person"=>{"name"=>"Fred Bloggs"},
"website"=>"http://www.example.com",
"controller"=>"person",
"action"=>"create"}
and is processed in the controller create method.
person_controller.rb
class PersonController < ApplicationController
def create
load = person_params.merge({contacts_attributes: [{address:params[:website],contact_type:"web", contact_sub_type: "main"}]})
#person = Person.new(load)
#person.save!
end
private
def person_params
params.require(:person).permit( ..... contacts_attributes: [:address, :contact_type, :contact_sub_type])
end
end
Related
I'm getting this error when I try to mass-assign a HABTM relationship in Rails 5:
*** ActiveRecord::RecordNotFound Exception: Couldn't find Job with ID=6 for Profile with ID=
class Profile < ApplicationRecord
has_and_belongs_to_many :jobs
accepts_nested_attributes_for :jobs
end
class Job < ApplicationRecord
has_and_belongs_to_many :profiles
end
class ProfilesController < ApplicationController
def create
#profile = Profile.new(profile_params)
end
private
def profile_params
params.require(:profile).permit(
:name,
:email,
:jobs_attributes: [:id]
)
end
end
=form_for #profile do |f|
=f.fields_for :jobs do |j|
=j.select :id, options_for_select([[1, "Job 1", ...]])
The problem is that accepts_nested_attributes_for is a way to update attributes on an associated object (either already linked or one that you're creating). You can think of it doing something like:
params[:jobs_attributes].each do |job_attributes|
#profile.jobs.find(job_attributes[:id]).attributes = job_attributes
end
Except that the job is then saved with the new attributes in an after-save of the parent object.
This isn't what you want. In fact, you don't need accepts_nested_attributes at all.
Instead, change the attribute in your view to job_ids as if it's an attribute of the profile, something like the following:
=form_for #profile do |f|
=j.select :job_ids, options_for_select([[1, "Job 1", ...]])
This will effectively call profile.job_ids = [1,2,3] which will have the effect that you're looking for.
I've tried to save my model, but failed to save it.
#starship.rb
class Starship < ApplicationRecord
has_many :crew_members,inverse_of: :starship
accepts_nested_attributes_for :crew_members
has_many :holodeck_programs,inverse_of: :starship
accepts_nested_attributes_for :holodeck_programs
end
#crew_member.rb
class CrewMember < ApplicationRecord
belongs_to :starship
accepts_nested_attributes_for :starship
has_many :holodeck_programs,through: :starship
end
#holodeck_program.rb
class HolodeckProgram < ApplicationRecord
belongs_to :starship
belongs_to :crew_member
end
#controller
def create
#Starship,CrewMember and HolodeckProgram are new via CrewMember.new
#crew_member = CrewMember.new(crew_member_params)
#crew_member.save
.
.
end
.
.
private
def crew_member_params
params.require(:crew_member).permit(:name,:division,:starship_id,
starship_attributes: [:name,:id,
holodeck_programs_attributes: [:title,:starship_id,:crew_member_id]])
end
Because there is no crew_member_id in holodeck_programs_attributes, validation error happen.
I can not use inverse_of: :crew_member because of through in crew_member.rb
How can I handle it?
Actually you can create any number of crew_members, holodeck_programs while creating a starship.
But you are trying to create many starships, holodeck_programs for a single crew_members, look into
def crew_member_params
params.require(:crew_member).permit(:name,:division,:starship_id,
starship_attributes: [:name,:id,
holodeck_programs_attributes: [:title,:starship_id,:crew_member_id]])
end
So you need to change the create to,
def create
#starship = Starship.new(star_ship_params)
#starship.save
end
and crew_member_params to
def star_ship_params
params.require(:star_ship).permit(<attributes of star_ship>, holodeck_programs_attributes: [<attributes of holodeck_programs>], crew_members_attributes: [<attributes of crew_members>])
end
And also make sure that you have done all changes in the view accordingly
Please look into: NestedAttributes for more information regarding nested attributes and its usage.
newbie here...
I am trying to create an events registration page where anybody can register for an event without logging into the system.
My problem is trying to figure out how to tie the registration info to the specific event. I've created all the associations but can't figure how to tell the db that the person is registering for a specific event.
Here are my associations:
class Event < ActiveRecord::Base
belongs_to :user
has_many :event_regs
has_many :regs, through: :event_regs
class Reg < ActiveRecord::Base
has_many :event_regs
class Reg < ActiveRecord::Base
has_many :event_regs
Thanks in advance
Newbie here
Welcome!
Here's what you'll need:
#app/models/event.rb
class Event < ActiveRecord::Base
has_many :registrations
end
#app/models/registration.rb
class Registration < ActiveRecord::Base
belongs_to :event
end
This will allow you to use the following:
#config/routes.rb
resources :events do #-> url.com/events/:id
resources :registrations #-> url.com/events/:event_id/registrations/
end
#app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
def new
#event = Event.find params[:event_id]
#registration = #event.registration.new
end
def create
#event = Event.find params[:event_id]
#registration = #event.registration.new registration_params
end
private
def registration_params
params.require(:registration).permit(:all, :your, :params)
end
end
This will create a new registration record in your db, associating it with the Event record you've accessed through the route.
--
From this setup, you'll be able to use the following:
#app/controllers/events_controller.rb
class EventsController < ApplicationController
def show
#event = Event.find params[:id]
end
end
#app/views/events/show.html.erb
<% #event.registrations.each do |registration| %>
# -> output registration object here
<% end %>
Foreign Keys
In order to understand how this works, you'll be best looking at something called foreign keys...
This is a relational database principle which allows you to associate two or more records in different database tables.
Since Rails is designed to work with relational databases, each association you use will require the use of a "foreign key" in some respect.
In your case, I would recommend using a has_many/belongs_to relationship:
You'll need to make sure you add the event_id column to your registrations database.
I have a many to many relationship with DoctorProfile and Insurance. I'd like to create these associations off of a form from a client side app. I'm sending back an array of doctor_insurances_ids and trying to create the association in one line. Is it possible to send back an array of doctor_insurances ids? If so what's the proper way to name it for mass assignment in the params?
The error I'm getting with the following code is
ActiveRecord::UnknownAttributeError: unknown attribute 'doctor_insurances_ids' for DoctorProfile.
class DoctorProfile
has_many :doctor_insurances
accepts_nested_attributes_for :doctor_insurances # not sure if needed
class Insurance < ActiveRecord::Base
has_many :doctor_insurances
class DoctorInsurance < ActiveRecord::Base
# only fields are `doctor_profile_id` and `insurance_id`
belongs_to :doctor_profile
belongs_to :insurance
def create
params = {"first_name"=>"steve",
"last_name"=>"johanson",
"email"=>"steve#ymail.com",
"password_digest"=>"password",
"specialty_id"=>262,
"doctor_insurances_ids"=>["44", "47"]}
DoctorProfile.create(params)
end
You're not putting a doctor_insurance_id in your Doctor Profile so your DoctorProfile.create(params) line isn't going to work. You could do something like this:
def create
doctor = DoctorProfile.create(doctor_profile_params)
params["doctor_insurances_ids"].each do |x|
DoctorInsurance.create(doctor_profile_id: doctor.id, insurance_id: x)
end
end
def doctor_profile_params
params.require(:doctor_profile).permit(:first_name, :last_name, :email, :password_digest, :specialty_id)
end
I am currently making API with RoR, and I need to create an object with virtual attributes and associated object.
The problem is that serializer does not kick in when I return an object with virtual attribute.
Here is the returned object from foo_controller
{
:id=>280,
:virtual=>"y8st07ef7u"
:user_id=>280
}
:virtual is a virtual attribute and user_id is an id of associated table - User.
My goal is to make this
{
:id=>280,
:virtual=>"y8st07ef7u",
:user=>{
:id=>280,
:name=>'foo'
}
}
Foo_controller setting
class Api::V1::FoosController < ApplicationController
foos = Foo.all
foos.each do |foo|
foo.set_attribute('y8st07ef7u')
end
render json: foos.to_json(:methods => :virtual), status: 200
end
Foo_model setting
class Foo < ActiveRecord::Base
belongs_to :user
attr_accessor:virtual
def set_attribute(path)
self.virtual = path
end
end
Foo_serializer setting
class FooSerializer < ActiveModel::Serializer
attributes :id, :virtual
has_one :user
end
Foo migration setting
class CreateFoos < ActiveRecord::Migration
def change
create_table :foo do |t|
t.references :user
end
end
end
user model
class User < ActiveRecord::Base
has_many :foos
end
user serializer
class UserSerializer < ActiveModel::Serializer
attributes :id, :name
belongs_to :foo
end
When I replace "foo.to_json(:methods => :virtual)" in foo_controller with "foos", serializer kicks in and I get a user object inside the returned json instead of user_id, but :virtual is not in the json.
Are there any ways I can get an object with both virtual attributes and associated object using active model serializer.
Thank you in advance for your help!
I figured out. It was very simple.
I just had to add ":virtual" to attributes in the foo_serializer and replace "foo.to_json(:methods =>:virtual)" with just "foos"