I want to create a submission model where a user can create submissions for tasks. Each submission should have a user_id and a task_id. When I try to create a submission, rails returns an error saying that the task must exist.
task model:
has_many :submissions
user model:
has_many :submissions
submission model:
belongs_to :user
belongs_to :task
routes:
resources :tasks do
resources :submissions
end
submissions controller:
def create
#task = Task.find(params[:task_id])
#submission = current_user.submissions.build(submission_params)
if #submission.save
flash[:success] = "Submitted!"
redirect_to task_submission_path(#task, #submission)
else
puts #submission.errors.full_messages
render 'new'
end
end
def new
#task = Task.find(params[:task_id])
#submission = Submission.new
end
def show
#submission = Submission.find(params[:id])
end
private
def submission_params
params.require(:submission).permit(:description)
end
tasks/show.html.erb:
<% if user_signed_in? %>
<%= link_to "Submit", new_task_submission_path(#task) %>
<% end %>
submissions/new.html.erb:
<h2>Submit</h2>
<%= form_for [:task, #submission] do |f| %>
<div><%= hidden_field_tag :task_id, #task.id %></div>
<div class="field">
<%= f.text_area :description, placeholder: "File description" %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
submission migration:
def change
create_table :submissions do |t|
t.string :description
t.integer :user_id
t.integer :task_id
t.timestamps
end
end
You are not assigning the task, you are only finding it in your create method. Please do this instead:
def create
#submission = current_user.submissions.build(submission_params)
#submission.task = Task.find(params[:task_id])
if #submission.save
flash[:success] = "Submitted!"
redirect_to task_submission_path(#task, #submission)
else
puts #submission.errors.full_messages
render 'new'
end
end
But Rails is able to perform this automatically, if you change the whitelisted params:
def create
#submission = current_user.submissions.build(submission_params)
if #submission.save
flash[:success] = "Submitted!"
redirect_to task_submission_path(#task, #submission)
else
puts #submission.errors.full_messages
render 'new'
end
end
private
def submission_params
params.require(:submission).permit(:description, :task_id)
end
Related
I'm doing a role permission edit function. It didn't have error but the logic of the code have some problem.
It didn't update the permission but update whole role_permission so it came out like image 1 & 2, it keep update many times.
I need to get the worker role_permission then check if that the permission already have?, if have then no need to add, if no then need to add(update). How can i check it at role controller update there?
Role controller
def edit
#role = Role.find(params[:id])
#role.company_id = params[:company_id]
#permissions = Permission.all
end
def update
#role = Role.find(params[:id])
#company_id = Company.find(params[:role][:company_id])
if #role.update!(role_params)
permission_ids = params[:permission_ids]
permission_ids.each do |permission_id|
RolePermission.update(role_id: #role.id, permission_id: permission_id)
end
flash[:success] = "Profile updated"
redirect_to #role
else
render 'edit'
end
end
Edit.html.erb
<% provide(:title, "Edit Roles") %>
<h1 class="dashboard">Update Role</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #role, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= #permissions.each do |permission|%>
<%= check_box_tag 'permission_ids[]', permission.id%>
<%= f.label :permission_name, permission.name %>
<% end %>
<%= f.hidden_field :company_id , value: 2%>
<%= f.submit "Save changes", class: "btn btn-secondary bottom" %>
<% end %>
</div>
</div>
RolePermission migration table
create_table "role_permissions", force: :cascade do |t|
t.integer "role_id"
t.integer "permission_id"
t.integer "company_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
parameter pass in console
parameter pass in console
Update
roles controller (role_params)
def role_params
params.require(:role).permit(:name, :company_id)
end
console logs when update role
console log
console log
Hi since you want Update/Add permissions related to roles.
can do it with the help of first_or_initialize:
def update
#role = Role.find(params[:id])
#company_id = Company.find(params[:role][:company_id])
if #role.update!(role_params)
permission_ids = params[:permission_ids]
permission_ids.each do |permission_id|
role_permissions = #role.role_permissions.where(permission_id: permission_id).first_or_initialize
role_permissions.save
end
flash[:success] = "Profile updated"
redirect_to #role
else
render 'edit'
end
end
Read more about first_or_initialize
OR
you should have the following associations with the below models:
# role.rb
has_many :role_permissions, dependent: :destroy
has_many :permissions, through: :role_permissions, source: :permission
# role_permission.rb
belongs_to :role
belongs_to :permission
Then in your roles_controller.rb should have below:
def update
#role = Role.find(params[:id])
#company_id = Company.find(params[:role][:company_id])
if #role.update!(role_params)
flash[:success] = "Profile updated"
redirect_to #role
else
render 'edit'
end
end
def role_params
params.require(:role).permit(:name, :company_id, permission_ids: [])
end
I am trying to work in my first implementation using fields_for to manage creating has_many relationship in one form partial. This form partial itself is part of a nested resource
So far, I am able to render, save and edit the form successfully without the fields_for nested form.
When I include the fields_for in the form_for, white-list the params, and build the objects in #new, I get this error in the console as it failed to save and re renders the #new view:
(0.1ms) rollback transaction
What can I do to successfully save the form along with the nested_attributes?
routes.rb
....
resources :projects do
resources :step_ones
resources :step_threes
resources :step_twos
resources :step_fours
resources :step_fives
resources :timelines
end
step_four.rb
class StepFour < ApplicationRecord
belongs_to :project
has_many :ios_devices
accepts_nested_attributes_for :ios_devices
end
ios_device.rb
class IosDevice < ApplicationRecord
belongs_to :step_four
end
_form.html.erb
<div>
<%= form_for([#project, #step_four]) do |f| %>
....
<%= f.fields_for :ios_devices do |d| %>
<div class='form-group'>
<%= d.label :full_name, "Name:"%>
<%= d.text_field :full_name %>
<%= d.label :email, "Email:"%>
<%= d.text_field :email %>
<%= d.label :udid, "UDID:"%>
<%= d.text_field :udid %>
<% end %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<div class='row'>
<span class='col-md-6'><%= button_to "Back", project_path(#project), method: :get, class:'btn btn-primary full-wide-button main-btn' %></span>
<span class='col-md-6'><%= f.submit 'Save Data', class: 'btn btn-primary full-wide-button'%></span>
</div>
<% end %>
</div>
step_fours_controller.rb
class StepFoursController < ApplicationController
def new
#project = Project.find(params[:project_id])
#step_four = StepFour.new
3.times { #step_four.ios_devices.build }
end
def create
#step_four = StepFour.new(step_four_params)
#project = Project.find(params[:project_id])
#step_four.ios_devices.each do |d|
puts d.full_name
puts d.email
puts d.udid
end
#step_four.project_id = params[:project_id]
if #step_four.save
flash[:success] = "Step Five Data Saved"
redirect_to #project
else
flash[:danger] = "Data Not Saved. Please Try Again"
render "new"
end
end
def show
#step_four = StepFour.where(project_id: (params[:project_id])).first
end
def update
#step_four = StepFour.where(project_id: (params[:project_id])).first
#project = Project.find(params[:project_id])
if #step_four.update_attributes(step_four_params)
flash[:success] = "Step Four Data Saved"
redirect_to #project
else
flash[:danger] = "Data Not Saved. Please Try Again"
render 'edit'
end
end
def edit
#step_four = StepFour.where(project_id: (params[:project_id])).first
#project = Project.find(params[:project_id])
end
def step_four_params
params.require(:step_four).permit(:iphone_name, :iphone_nickname, :android_name, ios_devices_attributes: [:id, :full_name, :email, :udid])
end
end
After realizing that the error was upon the save method, I tried to force the issue and raise an exception with a shebang ! . I received a Validation error that lead me to this question:
Error: Validation failed: Images imageable must exist , rails-5.0 , paperclip-5
I needed to add optional: true to the belongs_to: step_four in the ios_device model since, I believe that the object doesn't exist yet.
Now everything is working
I am trying to create a new teacher for a specific school in my project and I got this error:
No route matches [POST] "/schools/3/teachers/new"
Here is my teachers_controller.rb:
class TeachersController < ApplicationController
def new
#teacher = Teacher.new
end
def create
#teacher = Teacher.new(teacher_params)
#teacher.save
redirect_to school_path(school)
end
private
def teacher_params
params.require(:teacher).permit(:firstName, :lastName, :middleName)
end
end
schools_controller.rb:
class SchoolsController < ApplicationController
def show
#school = School.find(params[:id])
end
def new
#school = School.new
end
def edit
#school = School.find(params[:id])
end
def update
#school = School.find(params[:id])
if #school.update(school_params)
redirect_to #school
else
render 'edit'
end
end
def index
#schools = School.all
end
def create
#school = School.new(school_params)
if #school.save
redirect_to schools_path
else
render 'new'
end
end
def destroy
#school = School.find(params[:id])
#school.destroy
redirect_to schools_path
end
private
def school_params
params.require(:school).permit(:name)
end
end
routes.rb:
Rails.application.routes.draw do
resources :schools do
resources :teachers
end
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'welcome#index'
And teachers/new.html.erb:
<%= form_for :teacher, url: school_teachers_path(school) do |f| %>
<p>
<%= f.label :firstName %><br>
<%= f.text_field :firstName %>
</p>
<p>
<%= f.label :lastName %><br>
<%= f.text_field :lastName %>
</p>
<p>
<%= f.label :middleName %><br>
<%= f.text_field :middleName %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
As your teacher resource is nested under the school resource, so you need to pass the school when you try to create a teacher.
Try changing your new and create actions in teachers_controller.rb to something like this:
def new
#school = School.find(params[:school_id])
#teacher = #school.teachers.build
end
def create
#school = School.find(params[:school_id])
#teacher = #school.teachers.build(params[:teacher])
#teacher.save
redirect_to school_path(#school)
end
And, then change your form to this:
<%= form_for([#school, #teacher]) do %>
. . .
. . .
<% end %>
Try this in your form:
<%= form_for [school, Teacher.new] do |f| %>
The path you are posting to is for the index of teachers at a school:
school_teachers GET /schools/:school_id/teachers(.:format) teachers#index
I believe that it's a has_many belongs_to association. So you need to first change your teacher controller create action and new action.
class TeachersController < ApplicationController
def new
get_school
#teacher = #school.teachers.build
end
def create
get_school
#teacher = #school.teachers.build(teacher_params)
If #teacher.save
redirect_to school_path(school)
else
render 'new'
end
private
def teacher_params
params.require(:teacher).permit(:firstName, :lastName, :middleName)
end
def get_school
#school = School.find (params [:school_id])
end
end
Then in your form you 'll do :
<%= form_for([#school,#teacher]) do |f| %>
Hope this will help
Following a RoR turorial where I am building a basic bookmark app.
I have two models, topic and bookmark.
Bookmarks have a url attribute. The url attribute is rendering fine on my topics#show, but as plain text. When I tried to render it as a link it doesn't link to the url properly.
How do I render it as a hyperlink?
I have tried this
<%= #topic.bookmarks.each do |bookmark| %>
bookmark.url
<% end %>
But clearly that doesn't look right. Am I interpolating the correct way?
Alternatively is there a rails helper method that would do the trick?
Here are my files
Topics controller
class TopicsController < ApplicationController
def index
#topics = Topic.all
end
def new
#topic = Topic.new
end
def show
#topic = Topic.find(params[:id])
end
def create
#topic = Topic.new(params.require(:topic).permit(:name))
if #topic.save
redirect_to #topic
else
render :new
end
end
end
My bookmarks controller
class BookmarksController < ApplicationController
def create
#topic = Topic.find(params[:topic_id])
#bookmarks = #topic.bookmarks
#bookmark = #topic.bookmarks.build(params.require(:bookmark).permit(:url, :topic_id))
#bookmark.topic = #topic
#new_bookmark = Bookmark.new
if #bookmark.save
flash[:notice] = "Bookmark was saved"
redirect_to #topic
else
flash[:error] = "There was an error, please try again later"
redirect_to #topic
end
end
def destroy
#topic = Topic.find(params[:topic_id])
#bookmark = Bookmark.find(params[:id])
#bookmark.topic = #topic
if #bookmark.destroy
flash[:notice] = "Bookmark was destroyed successfully"
redirect_to [#topic]
else
flash[:error] = "There was an error, please try again later"
end
end
end
And these are my migration files
class CreateTopics < ActiveRecord::Migration
def change
create_table :topics do |t|
t.string :name
t.timestamps
end
end
end
class CreateBookmarks < ActiveRecord::Migration
def change
create_table :bookmarks do |t|
t.string :url
t.references :topic, index: true
t.timestamps
end
end
end
And here is my routes file
Rails.application.routes.draw do
resources :topics do
resources :bookmarks, only: [:destroy, :create]
end
get 'about' => 'welcome#about'
root to: 'welcome#index'
end
Bookmark form partial which is displayed in topics#show
<%= form_for [#topic, #topic.bookmarks.new] do |f| %>
<div class="col-md-5">
<div class="form-group">
<%= f.text_field :url, placeholder: "Enter bookmark url", class: 'form-control' %>
</div>
<%= f.submit "save", class: 'form-control' %>
</div>
<% end %>
in topics#show added this line to render partial
<%= render partial: 'bookmarks/form', locals: { topic: #topic, bookmark: #bookmark} %>
Have you tried to use the helper link_to?
<%= link_to 'name', bookmark.url, class: 'btn btn-default' %>
ERB does not interpolate strings unless they are inside ERB blocks (<% ... %>).
I.e., in your case the following would work:
bookmark.url
The cleaner solution is to use link_to as was mentioned in another answer. I just thought it's important to understand why the original solution did not work.
You can use the link_to helper method:
<%= #topic.bookmarks.each do |bookmark| %>
<%= link_to bookmark.url, bookmark.url %>
<% end %>
More info here
I have these models:
class List < ActiveRecord::Base
has_many :list_items, :dependent => :destroy
accepts_nested_attributes_for :list_items, :reject_if => lambda { |a| a(:task.blank?)}
end
class ListItem < ActiveRecord::Base
belongs_to :list
end
and this controller:
class ListsController < ApplicationController
def index
#lists = List.order("lists.created_at DESC")
end
def show
#list = List.find(params[:id])
end
def new
#list = List.new({:name => Time.now.strftime("%A %d %B")})
3.times { #list.list_items.build}
end
def create
#list = List.new(list_params)
if #list.save
flash[:notice] = "List successfully added."
redirect_to(:action => 'index')
else
render(:action => 'new')
end
end
def edit
#list = List.find(params[:id])
end
def update
#list = List.find(params[:id])
if #list.update_attributes
flash[:notice] = "List updated successfully."
redirect_to(:action => 'index')
else
render(:action => 'edit')
end
end
def delete
#list = List.find(params[:id])
end
def destroy
#list = List.find(params[:id]).destroy
flash[:notice] = "List successfully deleted"
redirect_to(:action => 'index')
end
private
def list_params
params.require(:list).permit(:name, :task)
end
end
and this form:
<%= form_for #list do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :list_items do |builder| %>
<p>
<%= builder.label :task %><br />
<%= builder.text_field :task %>
</p>
<% end %>
<p><%= f.submit "Create" %></p>
<% end %>
You can probably see what I am trying to do -> create a form where it creates a new list and then adds task to that list but for some reason whenever I hit submit on the form the name stays but tasks don't save?
I solved the problem turns out i needed to add the list_item attributes to the list controller. I was trying to do this the rails 3 way in teh model with attr_accessible which kept giving me an error. I needed to update the controller to have this at the bottom:
def list_params
params.require(:list).permit(:name, :list_items_attributes => :task)
end