Ruby on Rails : Updating multiple models in a single form - ruby-on-rails

I have 3 models User, House and Order.
Order Model
class Order < ActiveRecord::Base
belongs_to :from_house, :class_name => "House"
belongs_to :to_house, :class_name => "House"
belongs_to :user
accepts_nested_attributes_for :from_house, :to_house, :user
end
My House Model.
class House < ActiveRecord::Base
belongs_to :user
belongs_to :place
belongs_to :city
end
My user model.
class User < ActiveRecord::Base
has_many :orders
has_many :houses
end
In my order form I have something like this
<%= form_for #order do |f| %>
... # order fields
<%= f.fields_for :user do |i| %>
... # your from user forms
<% end %>
<%= f.fields_for :from_house do |i| %>
... # your from house forms
<% end %>
<%= f.fields_for :to_house do |i| %>
... # your to house forms
<% end %>
...
<% end %>
I haven't changed much in controller from the default. The controller code
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
def order_params
params.require(:order).permit( :shift_date, user_attributes: [:name, :email, :ph_no], from_house_attributes: [:place_id, :floor, :elevator, :size], to_house_attributes: [:place_id, :floor, :elevator])
end
When I submit the form, as expected a Order gets created with a new from_house and to_house along with a new user. But however my user_id in house table remains NULL. How can I make the houses(both from and to) reference the user created after submit.
The User is not logged in, So there is no current_user. We have to create a new user based on the details given. That user has to be associated with the houses (from and to).
I hope I'm clear. If not please let me know.
P.S: This question is an extension to this Ruby on rails: Adding 2 references of a single model to another model

I think this change in app/models/order.rb should do the trick:
class Order < ActiveRecord::Base
belongs_to :user
belongs_to :from_house, class_name: 'House'
belongs_to :to_house, class_name: 'House'
accepts_nested_attributes_for :user, :from_house, :to_house
validates :user, :from_house, :to_house, presence: true
def from_house_attributes=(attributes)
fh = build_from_house(attributes)
fh.user = self.user
end
def to_house_attributes=(attributes)
th = build_to_house(attributes)
th.user = self.user
end
end
Now, try this in your Rails console:
params = { user_attributes: { name: 'New name', email: 'name#example.com' }, from_house_attributes: { name: 'From house name' }, to_house_attributes: { name: 'to house name' } }
o = Order.new(params)
o.save
o.from_house
o.to_house
Cheers!

Related

3rd level nested model not receiving grandparent ID in form

I have a 3 models. User, CV, and Language. A User has one CV. A CV has many Languages. The User has many Languages through its CV. When I try to save the form I get an error that the Language does not have a User ID. How can I get the User ID to pass through the CV and to the Language in my form?
The CV is receiving the User ID properly. Languages is not.
I am using the Simple-Form and Cocoon gems.
Simplified version of form
= simple_form_for(#cv, url: user_cvs_path) do |f|
= f.simple_fields_for :languages do |language|
From User Model
has_one :cv, dependent: :destroy
has_many :languages, through: :cv, inverse_of: :user
From Cv Model
belongs_to :user
has_many :languages, dependent: :destroy
accepts_nested_attributes_for :languages, allow_destroy: true
From Language Model
belongs_to :user
belongs_to :cv
From the CV Controller
before_action :set_user
def new
#cv = #user.build_cv
#cv.languages.build
end
def create
#cv = #user.create_cv(cv_params)
respond_to do |format|
if #cv.save
format.html { redirect_to user_cv_url(#user, #cv), notice: 'Cv was successfully created.' }
format.json { render :show, status: :created, location: #cv }
else
format.html { render :new }
format.json { render json: #cv.errors, status: :unprocessable_entity }
end
end
end
def cv_params
params.require(:cv).permit(
:user_id,
:first_name,
:middle_name,
:last_name,
... # lots of params left out for brevity
languages_attributes: [
:id,
:cv_id,
:user_id,
:name,
:read,
:write,
:speak,
:listen,
:_destroy])
end
def set_user
#user = current_user if user_signed_in?
end
Your Language model does not need the belongs_to :user. Language belongs to CV and CV belongs to User, so the relation between Language and User is already in place. If you need to access the user for a specific language you can write #language.cv.user
To solve your problem just remove the belongs_to :user from the Language model, remove the user_id from languages_attributes, and remove the user_id from languages table.

simple_fields_for with check_boxes error

Good night friends!
In a form with many through, I need to display all the objects of a given class (tool), with a checkbox field and text field next to it. My form is as follows:
= simple_form_for #service, html: { class: 'form-horizontal' } do |f|
- #tools.each do |tool|
= f.simple_fields_for :instrumentalisations, tool do |i|
= i.input :tool_id, tool.id, as: :check_boxes
= i.input :amount
But I'm getting the following error:
Undefined method `tool_id 'for # <Tool: 0x007faef0327c28>
Did you mean To_gid
Models
class Service < ApplicationRecord
has_many :partitions, class_name: "Partition", foreign_key: "service_id"
has_many :steps, :through => :partitions
has_many :instrumentalisations
has_many :tools, :through => :instrumentalisations
accepts_nested_attributes_for :instrumentalisations
end
class Tool < ApplicationRecord
has_many :instrumentalisations
has_many :services, :through => :instrumentalisations
accepts_nested_attributes_for :services
end
class Instrumentalisation < ApplicationRecord
belongs_to :service
belongs_to :tool
end
Controller
def new
#service = Service.new
#service.instrumentalisations.build
end
def edit
#tools = Tool.all
end
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render :show, status: :created, location: #service }
else
format.html { render :new }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
def service_params
params.require(:service).permit(:name, :description, :price, :runtime, :status, step_ids: [], instrumentalisations_attributes: [ :id, :service_id, :tool_id, :amount ])
end
Thank you!
the error is quite simple: tool doesn't has a tool_id method. BUT, why are you asking this to a tool object instead of a instrumentalisation object?
So, you're trying to create some instrumentalisations but you're passing a tool as object:
f.simple_fields_for :instrumentalisations, **tool** do |i|
fields_for requires a record_name, whith in this case is :instrumentalisations and the 2nd arg is the record_object, which should be an instrumentalisations object and not a tool object.
So to fix it would have to pass an instrumentalisation object. You can accomplish that by:
f.simple_fields_for :instrumentalisations, Instrumentalisation.new(tool: tool) do |i|
Of course this isn't the best solution since if you edit this object would be building a lot of new instrumentalisations.
I'd recommend the cocoon gem, which makes it easier to handle nested forms!

'Build' error when nesting form attributes

I'm encountering an error when trying to create a new nested form.
I have 3 models:
class Child < ActiveRecord::Base
has_many :hires
has_many :books, through: :hires
end
class Hire < ActiveRecord::Base
belongs_to :books
belongs_to :children
accepts_nested_attributes_for :books
accepts_nested_attributes_for :children
end
class Book < ActiveRecord::Base
has_many :hires
has_many :children, through: :hires
belongs_to :genres
end
I'm trying to set up a view which allows children to 'hire' 2 books.
The view looks like this:
<%= form_for(#hire) do |f| %>
<%= hires_form.label :child %><br>
<%= hires_form.select(:child, Child.all.collect {|a| [a.nickname, a.id]}) -%>
<%= f.fields_for :books do |books_form| %>
<%= books_form.label :book %><br>
<%= books_form.select(:book, Book.all.collect {|a| [a.Title, a.id]}) -%>
<%= books_form.label :book %><br>
<%= books_form.select(:book, Book.all.collect {|a| [a.Title, a.id]}) -%>
<% end %>
<%= f.submit %>
<% end %>
The controller looks like this:
class HiresController < ApplicationController
...
def new
#hire = Hire.new
2.times { #hire.books.build }
end
def create
#hire = Hire.new(hire_params)
respond_to do |format|
if #hire.save
format.html { redirect_to #hire, notice: 'Hire was successfully created.' }
format.json { render :show, status: :created, location: #hire }
else
format.html { render :new }
format.json { render json: #hire.errors, status: :unprocessable_entity }
end
end
end
...
private
# Use callbacks to share common setup or constraints between actions.
def set_hire
#hire = Hire.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def hire_params
params.require(:hire).permit(:book, :child, books_attributes: [:id, :book, :child, :_destroy])
end
end
I'm getting the error:
undefined method `build' for nil:NilClass
I feel this is something obvious that i'm missing but any help would be great!
belongs_to :books
belongs_to :children
You need to singularize these.
belongs_to :book
belongs_to :child
Also see section 2.4 of the rails guides as a reference "The has_many :through Association":
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Keep in mind that you are using a belongs_to on the Hire object.
Notice the methods that are added when using the belongs_to association:
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})
So you should be able to use the method build_book on the Hire object (eg. #hire.build_book of #hire.build_child).
The methods added by the has_many association are:
collection(force_reload = false)
collection<<(object, ...)
collection.delete(object, ...)
collection.destroy(object, ...)
collection=(objects)
collection_singular_ids
collection_singular_ids=(ids)
collection.clear
collection.empty?
collection.size
collection.find(...)
collection.where(...)
collection.exists?(...)
collection.build(attributes = {}, ...)
collection.create(attributes = {})
collection.create!(attributes = {})
Which can be used on your Book and Child objects. For example: #book.childeren.build or #child.books.build.
For reference see the rails guides section 4.2 and 4.3 'Methods Added by ..':
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

Ruby On Rails [Carrierwave]: Cannot delete image

I'm a newbie in rails and trying to implement image uploading to ftp with 'carrierwave-ftp' gem. For image uploading, I have two controllers. First one is 'events_controller' while the second one is 'events_pictures_controller'.
Pictures are getting uploading to ftp. But the problem is that when I'm deleting a single picture, it is destroying the entire event. Please help!
Here is my Events Model:
class Event < ActiveRecord::Base
has_many :event_pictures, dependent: :destroy
accepts_nested_attributes_for :event_pictures, allow_destroy: true
validates_presence_of :name, :date
end
Here is my EventPictures Model:
class EventPicture < ActiveRecord::Base
mount_uploader :picture_title, EventPicturesUploader
validates_presence_of :picture_title
belongs_to :event, dependent: :destroy
end
Events Controller:
def index
#events = Event.all.order('date DESC')
end
def show
#event_pictures = #event.event_pictures.all
end
def new
#event = Event.new
#event_picture = #event.event_pictures.build
end
def edit
end
def create
#event = Event.new(event_params)
respond_to do |format|
if #event.save
params[:event_pictures]['picture_title'].each do |a|
#event_picture = #event.event_pictures.create!(:picture_title => a, :event_id => #event.id)
end
format.html { redirect_to #event, notice: 'Event was successfully created.' }
else
format.html { render :new }
end
end
end
def destroy
#event = Event.find params[:id]
#event.destroy
redirect_to events_url
end
private
def set_event
#event = Event.find(params[:id])
end
def event_params
params.require(:event).permit(:name, :date, event_pictures_attributes: [:id, :event_id, :picture_title])
end
This is the Destroy method in EventPictures Controller
def destroy
#event_picture = EventPicture.find params[:id]
#event_picture.destroy
redirect_to "events_url"
end
Meanwhile in the events.show.html.erb, I have this:
<% #event_pictures.each do |p| %>
<%= link_to image_tag(p.picture_title_url, :class => 'event-img'), image_path(p.picture_title_url) %>
<%= link_to 'Delete', p, method: :delete, data: { confirm: "Are you sure?" } %>
<% end %>
In your EventPicture model you have dependent: :destroy on the association which means that when the picture will deleted the corresponding events too. So just edit the association and make it:
belongs_to :event
And you have dependent destroy on the Event model so when a event will be deleted the corresponding pictures too will get deleted which is correct.
Hope this helps.
I believe your error lies with this line
belongs_to :event, dependent: :destroy
This is telling the EventPicture model to delete its parent model Event when it is deleted.
Replace with
belongs_to :event

False name? Patient::Diagnosi

I have to models:
class Patient < ActiveRecord::Base
attr_accessible :bis_gultigkeit, :geburtsdatum, :krankenkassennummer, :kvbereich, :landercode, :name, :namenszusatz, :plz, :statuserganzung, :strasse, :titel, :versichertennumer, :versichertenstatus, :vorname, :wohnort, :geschlecht, :telefon, :email, :gewicht
has_many :diagnosis
end
class Diagnose < ActiveRecord::Base
attr_accessible :beschreibung, :code, :seite, :sicherheit, :typ, :patient_id
belongs_to :patient
end
How you can see the two models have an association.
So that i want to display on the patient show page all of his diagnosis.
def show
#patient = Patient.find(params[:id])
#diagnosis = #patient.diagnosis
respond_to do |format|
format.html # show.html.erb
format.json { render json: #patient }
end
end
And in my view i call:
<%= #diagnosis.inspect %>
But somehow i get the error:
uninitialized constant Patient::Diagnosi
I cannot explain me why i get this error? And why does it say Diagnosi? I mean my model name is Diagnose! Thanks
You can call Diagnose.class_name.pluralize to see how rails pluralizes it.
I guess it is "Diagnoses", so you shoudl call:
#diagnoses = #patient.diagnoses
and
<%= #diagnoses.inspect %>

Resources