has_many Custom Create Action - ruby-on-rails

I think I need to user :through somewhere in my associations, but im very new to this so any help would be appreciated.
For simplicity, lets say I have 3 models.
Faults
Users
FaultComments
Where
Faults - belong_to :user and has_many :fault_comments
Users - has_many :faults and has_many :fault_comments
FaultComments - belongs_to :fault and belongs_to: user
What i would like to do is the ability to add fault comments from the fault show page, currently I have the below but i cant get it all to work as it should.
routes.rb
devise_for :users do
get '/users/sign_out' => 'devise/sessions#destroy'
end
resources :faults
resources :fault_comments
views/faults/show.html.erb
<h3>Add New</h3>
<%= form_for #faultcomment, :url => fault_comments_path(:fault_id => #fault.id, :user_id => current_user.id) do |f| %>
<%= f.text_field :comment %>
<%= f.submit %>
<% end %>
controllers/faults_comments_controller.rb
def create
#fault = Fault.find(params[:fault_id])
#faultcomment = #fault.fault_comments.new(params[:faultcomment])
#faultcomment.user_id = params[:user_id]
#faultcomment.comment = :comment
if #faultcomment.save
redirect_to faults_path
end
end

First of all, I think you should probably leave the FaultCommentsController like it came, which was probably something like this:
def create
#fault_comment = FaultComment.new(params[:fault_comment])
#fault_comment.user = current_user
if #fault_comment.save
redirect_to faults_path
end
end
(As a side note, it would probably be worth your while to learn about CamelCase and snake_case and how to properly translate between the two. The snake_case corollary to FaultComment is fault_comment, not faultcomment. You will certainly run into problems if you don't understand this.)
Your form on views/faults/show.html.erb looks more or less right to me. If you change your controller back to the original, does it work?
Also, change your form like this:
<h3>Add New</h3>
<%= form_for #fault_comment, :url => fault_comments_path do |f| %>
<%= f.text_field :comment %>
<%= f.hidden_field :fault_id, #fault.id %>
<%= f.submit %>
<% end %>

Related

Ruby on Rails Nested Models not Updating with form_for, but no error on update

So in my rails project, I have a Patient class, which has one Treatment class. This treatment class then has many DrNotes inside of it. I am still fairly new to rails, and I am aware that nesting this deeply is not recommended in Rails, but I am proceeding with this method.
My problem is with the editing of DrNotes. Since there are many doctor notes within treatment, I am trying to only edit one specific note. I am using Form_for to pass parameters to the doctor's note. When I submit the form, it redirects me to the page that should be shown only when the update function has succeeded. However, none of the notes are actually updated, and no errors are thrown when I try to perform the update.
Here are the models in question:
patient.rb
class Patient < ApplicationRecord
has_one :treatment, dependent: :destroy
accepts_nested_attributes_for :treatment, update_only: true
end
treatment.rb
class Treatment < ApplicationRecord
belongs_to :patient
has_many :dr_notes, class_name: "DrNote",
foreign_key: "treatment_id", dependent: :destroy
accepts_nested_attributes_for :dr_notes
end
dr_note.rb
class DrNote < ApplicationRecord
belongs_to :treatment
end
In my controller I have:
Doctor Note Edit Function
def edit_dr_note
#patient = Patient.find(params[:patient_id])
#dr_note = #patient.treatment.dr_notes.find(params[:dr_id])
#dr_note.update if #dr_note.nil?
end
Doctor Note Update Function
def update_dr_note
#patient = Patient.find(params[:patient_id])
#dr_note = #patient.treatment.dr_notes.find(params[:dr_id])
if #dr_note.update(dr_note_params)
redirect_to page_path(#patient)
else
flash.now[:error] = "Cannot update Doctor's notes"
render 'edit_dr_note'
end
end
Doctor Note Params
def dr_note_params
params.require(:dr_note).permit(:id, :name, :message)
end
I have :id in the params.permit because from researching, I heard that you need to include it when updating models, but i'm not sure if it is needed here.
I have the following code in the routes.rb
get '/pages/:patient_id/treatment/edit/edit_dr_note/:dr_id', to: 'pages#edit_dr_note', as: :edit_dr_note
match "pages/:patient_id/treatment/update/update_dr_note/:dr_id" => "pages#update_dr_note", as: :update_dr_note, via: [:patch, :post]
And in the edit_dr_note.html.erb
<%= form_for #patient.treatment.dr_notes.find(params[:dr_id]), url: update_dr_note_path do |patient_form| %>
<% #patient.treatment.dr_notes.each do |doctor| %>
<% if doctor.id == #dr_note.id %> #Only displays the fields for the desired note
<%= patient_form.fields_for :dr_note, doctor do |doctor_fields| %>
Name: <%= doctor_fields.text_field :name %>
Message: <%= doctor_fields.text_field :message %>
<% end %>
<p>
<%= patient_form.submit %>
</p>
<% end %>
<% end %>
<% end %>
Any help would be greatly appreciated. Thanks!
You are mixing two approaches(the nested resources and the nested attributes). Use one to serve your purpose.
With the nested resources:
<%= form_for [:pages, #patient, #treatment, #dr_note], url: update_dr_note_path do |dr_note| %>
Name: <%= dr_note.text_field :name %>
Message: <%= dr_note.text_field :message %>
<p>
<%= dr_note.submit %>
</p>
<% end %>
The routes would be
get '/pages/:patient_id/treatment/:treatment_id/edit_dr_note/:dr_id', to: 'pages#edit_dr_note', as: :edit_dr_note
match "pages/:patient_id/treatment/:treatment_id/update_dr_note/:dr_id" => "pages#update_dr_note", as: :update_dr_note, via: [:patch, :post]
Edit the edit_dr_note to define #treatment
def edit_dr_note
#patient = Patient.find(params[:patient_id])
#treatment = #patient.treatment
#dr_note = #patient.treatment.dr_notes.find(params[:dr_id])
#dr_note.update if #dr_note.nil?
end
And finally remove accepts_nested_attribute_for from the models, you don't need it in this approach.
With the nested attributes:
Keep the accepts_nested_attributes_for in the models. And change the routes and form like below
get '/edit_dr_note/:dr_id', to: 'pages#edit_dr_note', as: :edit_dr_note
match "/update_dr_note/:dr_id" => "pages#update_dr_note", as: :update_dr_note, via: [:patch, :post]
And the form_for
<%= form_for #patient, url: update_dr_note_path do |patient| %>
<%= patient.fields_for :treatment do |t| %>
<%= t.fields_for :dr_notes, #dr_note do |dr_note| %>
Name: <%= dr_note.text_field :name %>
Message: <%= dr_notetext_field :message %>
<% end %>
<% end %>
<p>
<%= patient.submit %>
</p>
<% end %>
And change the dr_note_params method as below
def dr_note_params
params.require(:patient).permit(:id, treatment_attributes: [:id, dr_notes_attributes: [:id, :name, :message])
end
When you write the following line, you're trying to find a DrNote using the dr_id:
#dr_note = #patient.treatment.dr_notes.find(params[:dr_id])
Whereas the dr_notes relation on Treatment does not seem to define any particular behavior, and this is your problem.
You'll need to find_by doctor's id (or dr_id in your code) and thus first define the relation on DrNote.

Has_many through association form creation

I have a has_many through association and I need to create a new record for UserGroup where the UserGroup is created within the Group controller.
In my groups controller I have this method
def add
#usergroup = UserGroup.new
end
Here is my UserGroup.rb model
class UserGroup < ApplicationRecord
belongs_to :user
belongs_to :group
end
In the add.html.erb view I have it render the form whos code is listed below.
<%= form_for(#usergroup) do |form| %>
<div class="field">
<%= form.label :user_id %>
<%= form.text_field :user_id %>
</div>
<% form.text_field :group_id, value: #usergroup.id %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
However I am getting this error.
undefined methoduser_groups_path' for #<#:0x007fba7c11aab0>`
Can you not create a record this way? I want to have the url presented like this http://localhost:3000/groups/4/add.
Im somewhat new to Rails and forms for different models is where I get confused. Can anybody help me out with this?
Routes
Rails.application.routes.draw do
resources :maps
devise_for :users
resources :groups do
get 'add', on: :member
end
resources :rows
root to: 'maps#index'
end
First of all you have to declare an appropriate route. You going to post but set get 'add'.
post '/groups/:id/add'
resources :groups
Because of this you have get add_group_path instead of post add_groups_path
It'll be better to specify explicity method and url:
<%= form_for #usergroup, method: 'post', url: add_groups_path do |form| %>
And don't forget to permit params in controller

Ruby on Rails Association Form

So i'm making a web app using ROR and I can't figure out what the right syntax for this form is. I'm currently making an association type of code for comments and posts.
<%= form_for #comment do |f| %>
<p>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.label :comment %><br />
<%= f.text_area :comment %>
</p>
<p>
<%= f.submit "Add Comment" %>
</p>
<% end %>
Your form is fine, except the first line (and you don't need a hidden field for the user_id, thats done through your relationship):
<%= form_for(#comment) do |f| %>
Should be:
<%= form_for([#post, #comment]) do |f| %>
Now you render a form for creating or updating a comment for a particular post.
However, you should change your model and controller.
class Post
has_many :comments
end
class Comment
belongs_to :post
end
This will give you access to #post.comments, showing all comments belonging to a particular post.
In your controller you can access comments for a specific post:
class CommentsController < ApplicationController
def index
#post = Post.find(params[:post_id])
#comment = #post.comments.all
end
end
This way you can access the index of comments for a particular post.
Update
One more thing, your routes should also look like this:
AppName::Application.routes.draw do
resources :posts do
resources :comments
end
end
This will give you access to post_comments_path (and many more routes)

Using has_one with condition to find related object, but how do you create the related object?

I have an Account model with the following :
has_one :primary_user, :class_name => "User", :conditions => "role = 'primary_user'"
So #account.primary_user looks for a user with a role of primary_user.
When creating a new account, I want to be able to create a new primary_user. What is the "Rails way" to do that ?
Do I need to create a primary_user= method?
Here is my create form ..
<%= semantic_form_for #account do |f| %>
<%= f.input :account_name %>
<%= f.semantic_fields_for :primary_user do |user| %>
<%= user.input :email %>
<% end %>
<% end %>
If I submit this form I get
ActiveRecord::AssociationTypeMismatch (User(#2159789500) expected, got ActiveSupport::HashWithIndifferentAccess(#2159703820)):
Thanks
Little cleaner:
has_one :primary_user, :class_name => "User", :conditions => { :role => "primary_user" }
Then straight solution:
<%= semantic_form_for #account do |f| %>
<%= f.input :account_name %>
<%= f.semantic_fields_for :primary_user, primary_user || build_primary_user do |user| %>
<%= user.input :email %>
<% end %>
<% end %>
But I suggest you to add this into new action in controller
#account.build_primary_user
This builds on top of what #fl00r says in his post... I agree the proper place to make build the objects is in the controller. In the new action on your AccountsController, you just need to add a line like this:
#account.primary_user ||= #account.build_primary_user
So your new action would look something like this (plus anything extra you added):
def new
#account = Account.new
#account.primary_user ||= #account.build_primary_user
end
Then your code for the create form should work as is.

How do I use nested attributes with the devise model

I have the same issue as Creating an additional related model with Devise (which has no answer).
I have overridden the devise view for creating a new user and added a company name, I have changed the model to use accepts_nested_attributes_for
There are no errors, but it is not adding the nested record and I don't have a controller where I can modify the request.
I have the following (shortened to make it readable):
routes.rb
map.devise_for :users
map.resources :users, :has_many => :companies
user.rb
has_many :companies
accepts_nested_attributes_for :companies
devise :registerable ... etc
company.rb
belongs_to :user
new.html.erb
...
<% form_for resource_name, resource, :url => registration_path(resource_name) do |f| %>
...
<% f.fields_for :company do |company_form| %>
<p><%= company_form.label :name %></p>
<p><%= company_form.text_field :name %></p>
<% end %>
...
UPDATE:
I didn't add :company to the attr_accessible list in the User model.
You can add the following method to the User model:
user.rb
def with_company
self.companies.build
self
end
And modify the view:
new.html.erb
...
<% form_for [resource_name, resource.with_company], :url => registration_path(resource_name) do |f| %>
...
<% f.fields_for :company do |company_form| %>
...
<% end %>
This way, you'll have the nested form to add one company to the user.
To dynamically add multiple companies to the user, check the Railcast #197 by Ryan Bates.
Make sure you are passing the pair of resources as an array, otherwide you will get an error like this: "wrong number of arguments (3 for 2)".
You may be trying to mass assign some protected variable, OR you might not be saving a valid record. Check to make sure that the record is actually saving to the db.
I realised this is a very old thread, but since I found a better solution, hence the reply.
Just change the new.html.erb as follows,
<% form_for(resource, :as => resource_name,:url => registration_path(resource_name) do |f| %>
...
<% prefix = "user[company_attributes]"%>
<% fields_for prefix, #user.company do |company_form| %>
...
<% end %>
<% end %>
This way when #user.save gets invoked, it would run company.save too with all the validations you may have in company model.
I don't have whole lot of RoR experience, but I think this is a better solution. What do you think?

Resources