Pathing issues in rails form_with for associated model - ruby-on-rails

I am working on a project for which I have to analyse data inside a file, which the user should be able to upload. I have a model InputFile and chose to implement an analysis_facade for my Analysis model which bundles most of the functionality.
Here are the models
#app/models/analysis.rb
class Analysis < ApplicationRecord
has_one :input_file, dependent: :destroy
end
#app/models/input_file.rb
class InputFile < ApplicationRecord
belongs_to :analysis
has_many :access_data, dependent: :destroy
def uploaded_file=(file_field)
self.name = File.basename(file_field.original_filename).gsub(/[^\w._-]/,'')
self.content_type = file_field.content_type.chomp
self.data = file_field.read
end
end
Here comes my AnalysisController and the corresponding facade pattern
#app/controllers/analyses_controller.rb
class AnalysesController < ApplicationController
before_action :set_analysis, only: :show
def show
#analysis_facade = AnalysisFacade.new(#analysis)
end
private
def set_analysis
#analysis ||= Analysis.create
end
end
#app/facades/analysis_facade.rb
class AnalysisFacade
attr_reader :analysis
def initialize(analysis)
#analysis = analysis
end
def new_input_file
#input_file = analysis.build_input_file
end
def input_file
#input_file ||= new_input_file
end
def access_data
#access_data ||= analysis.input_file.access_data
end
end
My controller for InputFiles
#app/controllers/input_files_controller.rb
class InputFilesController < ApplicationController
include LogFileScanner
before_action :set_analysis, only: [:new, :create, :update]
def new
#input_file = Input_file.new
end
def create
#input_file = #analysis.build_input_file(input_file_params)
if #input_file.save
access_data = scan(#input_file.data)
#input_file.access_data.create(access_data)
end
end
def update
#input_file = #analysis.input_file
if #input_file.update
access_data = scan(#input_file.data)
#input_file.access_data.update(access_data)
end
end
private
def input_file_params
params.require(:input_file).permit(:uploaded_file, :analysis_id)
end
def set_analysis
#analysis = Analysis.find(params[:input_file][:analysis_id])
end
end
For the file upload I have these views
#app/views/analyses/show.html.erb
<%= render template: 'input_files/get' %>
#app/views/input_files/get.html.erb
<%= form_with model: [#analysis_facade.analysis, #analysis_facade.input_file], action: :update, html: {multipart: true, class: 'form-horizontal center'} do |form| %>
<div class='form-group.new'>
<% if false %>
<%= form.hidden_field :analysis_id, value: #analysis_facade.analysis[:id] %>
<% end %>
<%= form.file_field 'uploaded_file' %>
<%= form.submit "Upload", class: 'btn btn-default btn-primary' %>
</div>
<% end %>
Executing this results in error
undefined method `analysis_input_files_path' for #<#<Class:0x00007f1208040288>:0x00007f11f0d71d48>
Did you mean? analysis_path
for this loc
<%= form_with model: [#analysis_facade.analysis, #analysis_facade.input_file], action: :update, html: {multipart: true,
class: 'form-horizontal center'} do |form| %>
Why is that? I need to pass the analysis_id to params in order to create the new InputFile. But I want it to be cleaner than manually adding a hidden_field, since model_with should provide this feature. What do I miss here?
Also: Why does rails search for `analysis_input_files_path'? Its clearly a has_one relation, so shouldn't it look for analysis_input_file_path then?
EDIT:
#bin/rails routes
Prefix Verb URI Pattern Controller#Action
analyses GET /analyses(.:format) analyses#index
POST /analyses(.:format) analyses#create
new_analysis GET /analyses/new(.:format) analyses#new
edit_analysis GET /analyses/:id/edit(.:format) analyses#edit
analysis GET /analyses/:id(.:format) analyses#show
PATCH /analyses/:id(.:format) analyses#update
PUT /analyses/:id(.:format) analyses#update
DELETE /analyses/:id(.:format) analyses#destroy
input_file_access_data GET /input_files/:input_file_id/access_data(.:format) access_data#index
POST /input_files/:input_file_id/access_data(.:format) access_data#create
new_input_file_access_datum GET /input_files/:input_file_id/access_data/new(.:format) access_data#new
edit_access_datum GET /access_data/:id/edit(.:format) access_data#edit
access_datum GET /access_data/:id(.:format) access_data#show
PATCH /access_data/:id(.:format) access_data#update
PUT /access_data/:id(.:format) access_data#update
DELETE /access_data/:id(.:format) access_data#destroy
input_files GET /input_files(.:format) input_files#index
POST /input_files(.:format) input_files#create
new_input_file GET /input_files/new(.:format) input_files#new
edit_input_file GET /input_files/:id/edit(.:format) input_files#edit
input_file GET /input_files/:id(.:format) input_files#show
PATCH /input_files/:id(.:format) input_files#update
PUT /input_files/:id(.:format) input_files#update
DELETE /input_files/:id(.:format) input_files#destroy
analyses_show GET /

I was a conceptional mistake. I changed the facade model to
class AnalysisFacade
attr_reader :analysis
def initialize(analysis)
#analysis = analysis
end
def new_input_file
#analysis.build_input_file
end
def input_file
#analysis.input_file ||= new_input_file
end
def access_data
#analysis.input_file.access_data
end
end
And the view works now like this
<%= form_with model: #analysis_facade.input_file, html: {multipart: true, class: 'form-horizontal center'} do |form| %>
<div class='form-group.new'>
<%= form.file_field 'uploaded_file' %>
<%= form.submit "Upload", class: 'btn btn-default btn-primary' %>
</div>
<% end %>

Related

rails wicked gem has_one association and nested form

On my form on has_one association the fields do not appear for a singular form.
<%= f.fields_for :pack_social_media_sur_mesure, #commande.pack_social_media_sur_mesure do |ff| %>
remains empty
i think i missed something...
My Models :
Commande model :
class Commande < ApplicationRecord
belongs_to :user
validates_presence_of :user
has_one :pack_social_media_sur_mesure, dependent: :destroy
accepts_nested_attributes_for :pack_social_media_sur_mesure, allow_destroy: true
end
PackSocialMediaSurMesure model :
class PackSocialMediaSurMesure < ApplicationRecord
belongs_to :commande
...
end
My controller :
class CommandeStepsController < ApplicationController
...
def update
#user_id = current_user.id
#user = current_user
#commande = #user.commande
#commande.update(commande_params)
end
...
def commande_params
params.require(:commande).permit(:id,..., pack_social_media_sur_mesure_attributes: [:id, ...])
end
end
My form :
<%= form_for #commande, url: wizard_path, html: { class: "pack-slide" }, method: :put do |f| %>
<%= f.fields_for :pack_social_media_sur_mesure, #commande.pack_social_media_sur_mesure do |ff| %>
<%= ff.select :question1 %>
...
<% end %>
<%= f.submit %>
<% end %>
PS : I use wicked gem for this form, this why wizard_path.
Thx,
Théo
Ok I find.
The reason why the fields do not appear is that I had already created an 'commande' but not a 'pack_social_media_sur_mesure'. Or the "wicked gem" takes us to the edit path but we cannot edit it if it does not exist.
The solution was to create the pack_social_media_sur_mesure when creating the 'order' as bellow :
class CommandesController < ApplicationController
before_action :set_commande, only: [:show, :edit, :update, :destroy]
...
def create
#commande = current_user.create_commande(commande_params)
if #commande.save
if #commande.pack_social_media_sur_mesure == nil
#commande.create_pack_social_media_sur_mesure(...)
end
redirect_to ...
else
render :new
end
end
...
def set_commande
#commande = current_user.commande
end

param is missing or the value is empty:

When going on the view page, the error "param is missing or the value is empty: restaurant" is displayed.
- I solve the problem by deleting "require(:restaurant)" but don't really understand the trick...do you ?
- I am looking for a solution that allows me to keep the "require(:restaurant)... Any Idea ?
restaurants_controller.rb
class RestaurantsController < ApplicationController
before_action :set_restaurant, only: [:show, :edit, :update]
def index
#restaurants = Restaurant.all
end
def show
end
def new
#restaurant = Restaurant.new(params.permit(:name, :address, :category))
end
def create
#restaurant = Restaurant.new(restaurant_params)
#restaurant.save
redirect_to restaurant_path(#restaurant)
end
private
def set_restaurant
#restaurant = Restaurant.find(params[:id])
end
def restaurant_params
params.require(:restaurant).permit(:name, :address, :category)
end
end
new.html.erb
<%= simple_form_for(#restaurant) do |f| %>
<% if #restaurant.errors.any? %>
<div class="errors-container">
<ul>
<% #restaurant.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.input :name %>
<%= f.input :address %>
<%= f.input :category %>
<%= f.submit "add a resto", class: "btn btn-primary" %>
<% end %>
restaurant.rb
class Restaurant < ApplicationRecord
validates :name, :address, :category, presence: true
validates :category, inclusion: { in: ["chinese", "italian", "japanese", "french", "belgian"]}
has_many :reviews, dependent: :destroy
end
routes
Rails.application.routes.draw do
resources :restaurants, only: [:new, :create, :show, :index] do
resources :reviews, only: [ :new, :create ]
end
end
The inputs of your form should be named like this:
restaurant[name]
restaurant[address]
restaurant[category]
It could be that it is something like is:
name
address
category
You can debug your params by inspecting it.
You could do something like this in your restaurant_params in the controller:
raise params.inspect
Or open your debug console of the browser and see what is posted.
And as mentioned in the comments you can use the restaurant_params in your new method but I don't think that fixes you issue it is more likely that the form is the problem.
# instead of
Restaurant.new(params.permit(:name, :address, :category))
# do
Restaurant.new(restaurant_params)
I assume you're trying to auto-populate the form on your new view with some passed attributes.
Your restaurant#new is looking for a params[:restaurant] hash to create a new Restaurant object and can't find it. Look at how you're calling the new action and what parameters are being passed -- my guess is that you're not passing any or you're passing them in the root params hash like params[:name], params[:address] instead of params[:restaurant] => { :name => 'name'}.
Most new actions are a GET so if you're using inline URL parameters put them in like http://address.com/restaurant/new?restaurant[name]=Bistro&restaurant[category]=burgers
In restaurants_controller.rb, we can declare a method for show like:
def show
#restaurant = Restaurant.find(params[:id])
end
In the View Section where we want to link with a button, we can paste
<td><%= link_to "Show", restaurant %> </td>
I hope it will work

Ruby on Rails: how to make a form for associated models (nested)

First of all I have this:
https://polar-scrubland-30279.herokuapp.com/ - my project which is deployed on heroku (Captain Obvious)
I've got projects and todos inside them.
For this moment I show all projects using this way:
------index.html.erb------
<%= render #projects %>
------_project.html.erb-----
<div class="project">
<div class="project-header">
<h2><%= project.title %></h2>
</div>
<div class="project-todos">
<% project.todos.all.each do |todo| %>
<p><%= check_box('tag', todo.__id__, {class: 'icheckbox_square-blue', checked: todo.isCompleted}) %> <%= content_tag :todotext, todo.text %></p>
<% end %>
</div>
</div>
And as you understand it doesn't allow me to change my todo's status when checkbox is checked. So that's why I need a form that will allow me to track all the checkboxes. Also I wanna make text-decoration: line-through when checkbox is pressed, but don't get how to.
Is there a way to creat a form which will satisfy my needs? Please can you help me, Any information will be appreciated.
ADDITIONAL INFORAMTION:
GitHub - https://github.com/NanoBreaker/taskmanager
project.rb
class Project < ActiveRecord::Base
has_many :todos
end
todo.rb
class Todo < ActiveRecord::Base
belongs_to :project
end
Lets start with the models:
class Project < ApplicationRecord
has_many :todos
accepts_nested_attributes_for :todos
end
class Todo < ApplicationRecord
belongs_to :project
end
accepts_nested_attributes_for lets you create or modify several nested Todo records at once when creating or updating a Project.
# will update 2 todos at once
#project.update(
todos_attributes: [ { id: 1, isComplete: true }, { id: 2, isComplete: false }]
)
We can use fields_for to create nested inputs for todos:
<%= f.form_for(#project) do |f| %>
<%= f.fields_for(:todos) do |tf| %>
<%= tf.check_box :isCompleted %>
<% end %>
<% end %>
This generates fields for todos nested under the key todos_attributes. We can whitelist them by using a hash key containing a array of permitted attributes.
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
def new
#project = Project.new
# this seeds the project with 3 empty tasks
# otherwise we don't have any inputs.
3.times { #project.todos.new }
end
def create
#project = Project.new(project_params)
if #project.save
# ...
else
# ...
end
end
def update
if #project.update(project_params)
# ...
else
# ...
end
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project)
.permit(:foo, :bar,
todos_attributes: [:isCompleted, :text]
)
end
end
You can create a form for each project by creating a partial which uses a local instead of an instance variable:
# app/views/projects/_form.html.erb
<%= f.form_for(local_assigns[:project] || #project) do |f| %>
<%= f.fields_for(:todos) do |tf| %>
<%= tf.check_box :isCompleted %>
<% end %>
<% end %>
# app/views/projects/index.html.erb
<% #projects.each do |project| %>
<%= render partial: 'projects/form', project: project %>
<% end %>
You can reuse the same partial for the other views as well:
# app/views/projects/new.html.erb
<%= render partial: 'projects/form' %>
# app/views/projects/edit.html.erb
<%= render partial: 'projects/form' %>

Simple Form for - Nested Resources paths

I'm trying to make an app in Rails 5 for post answer to the task.
The Task content and the answer form in the same page.
When the user come into the task page, he can see the task content.
And he can post(create) an answer or edit the answer.
I get an error:
NoMethodError in Tasks#show
undefined method `to_key' for #
Did you mean? to_query
to_set
to_ary
What am I doing wrong?
What else information do you need that can help debug this issue?
I'd really apprieciate any help!
Models
```
# task.rb
class Task < ApplicationRecord
belongs_to :post
has_many :answers, dependent: :destroy
end
# answer.rb
class Answer < ApplicationRecord
belongs_to :task
belongs_to :user
end
```
routes.rb
```
resources :tasks, only: [:show] do
resources :answers #, only: [:new, :create, :edit, :update]
end
```
Controllers
```
# tasks_controller.rb
class TasksController < ApplicationController
before_action :authenticate_user!, only: [:show]
def show
#task = Task.find(params[:id])
#post = #task.post
if #task.answers.present?
#answer = Answer.where("task_id = ? and user_id = ?", #task.id, current_user.id)
else
#answer = Answer.new
end
end
end
# answers_controller.rb
def new
#task = Task.find(params[:task_id])
#answer = Answer.new
end
def create
#task = Task.find(params[:task_id])
#answer = Answer.new(answer_params)
#answer.task_id = #task.id
#answer.user_id = current_user.id
if #answer.save
redirect_to post_path(#task.post), notice: "Answer Added."
else
render :new
end
end
```
Views
```
<div class="answer-form">
<%= simple_form_for [#task, #answer], :url => task_answer_path(#task, #answer), method: :put do |f| %>
<% if #task.answers.present? %>
<%= f.input :content, id: "x", value: #task.answers.first.content,
input_html: {class: "hidden"}, name: "content", label: false %>
<trix-editor input="x" class="formatted_content trix-content"></trix-editor>
<div class="form-actions">
<%= f.submit "Submitting", class: "assignment-btn", data: {disable_with: "Submitting..."} %>
</div>
<% end %>
<% else %>
<%= simple_form_for [#task, #answer], :url => task_answers_path(#task), method: :post do |f| %>
<%= f.input :content, id: "x", value: "content",
input_html: {class: "hidden"}, name: "content", label: false %>
<trix-editor input="x" class="formatted_content trix-content"></trix-editor>
<div class="file-upload-block">
<input name="fileToUpload[]" id="fileToUpload" type="file">
</div>
<div class="form-actions">
<%= f.submit "提交", class: "assignment-btn", data: {disable_with: "Submitting..."} %>
</div>
<% end %>
<% end %>
</div>
```
I know what may be wrong, the line below was returning an ActiveRecord::Relation, not the instance itself, you need to append 'first' in the end of the answer query, like such:
#answer = Answer.where(task: #task, user: current_user).first

Nested resource parameters not being set properly in form_for

I'm getting a curious error after submitting my form. Been trying to solve this for several hours..
No route matches {:action=>"show", :controller=>"items", :item_id=>"141", :matter_id=>"3"} missing required keys: [:id]
The parameters are:
{"utf8"=>"✓",
"authenticity_token"=>"w0D7XmX2X2/ZMU19T6RlMvWCEClXnCFFOR+4EdIFvWg=",
"comment_item"=>{"item_id"=>"",
"name"=>"kaljdf",
"body"=>"yet another comment test"},
"commit"=>"Post Comment",
"matter_id"=>"3",
"item_id"=>"141"}
I have the following models:
class Matter < ActiveRecord::Base
has many :discoveries
delegate :items, to: :discoveries
end
class Discovery < ActiveRecord::Base
belongs_to :matter
scope :items, -> { where(type: 'Item') }
end
class Item < Discovery
has_many :comment_items
end
class CommentItem < ActiveRecord::Base
belongs_to :item
end
Controllers:
class ItemsController < DiscoveriesController
def show
#item = Item.find(params[:id])
#comment_item = CommentItem.new
end
def edit
#item = Item.find(params[:id])
end
def new
#item = Item.new
end
end
class CommentItemsController < ApplicationController
before_action :set_comment_item, only: [:show, :edit, :update, :destroy]
def new
#item = Item.find(params[:item_id])
#comment_item = #item.comment_item.new
end
def create
#item = Item.find(params[:item_id])
#comment_item = #item.comment_items.new(comment_item_params)
if #comment_item.save
flash[:notice] = 'Comment was successfully created'
redirect_to matter_item_url(matter_id: params[:matter_id])
else
flash[:notice] = "Error creating comment: #{#comment.errors}"
redirect_to matter_item_url(#matter, #item)
end
end
def destroy
#comment_item = CommentItem.find(params[:id])
#comment_item.destroy
redirect_to(#comment_item.item)
end
private
def set_comment_item
#comment_item = CommentItem.find(params[:id])
end
def comment_item_params
params.require(:comment_item).permit(:name, :body, :item_id, :matter_id)
end
end
The show action for the item resource:
<p>
<strong>Matter:</strong>
<%= #item.matter_id %>
</p>
<p>
<strong>Content:</strong>
<%= #item.content %>
</p>
<hr />
<%= form_for #comment_item, url: matter_item_comment_items_path(matter_id: #item.matter, item_id: #item.id) do |f| %>
<% if #comment_item.errors.any? %>
<ul>
<% #comment_item.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= f.hidden_field :item_id %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit "Post Comment" %>
</p>
<% end %>
<%= render :partial => 'comment_item', :collection => #item.comment_items %>
<%= link_to 'Edit', edit_matter_item_path(id: #item.id) %> |
<%= link_to 'Back', matter_items_path %>
Routes
resources :items do
resources :comment_items
end
resources :matters do
resources :items do
resources :comment_items
end
end
When looking at CommentItems in the console, I see that the comments are in fact being added to the model with their correct ID's, but they don't seem to be passed as parameters.. What am I missing?
I've reviewed Rails 4 form_for double nested comments and Rails 3.2 - Nested Resource Passing ID but I didn't have much luck..
I'd really appreciate your help!
No route matches {:action=>"show", :controller=>"items", :item_id=>"141", :matter_id=>"3"} missing required keys: [:id]
your request is going to ItemsController instead of CommentItemsController
see :controller => "items"

Resources