There is an association with course and course instance. When I visit the url /courses/1/course_instances/new, I get the error below.
This is the error I got:
No route matches {:controller=>"course_instances", :course_id=>nil}
Models
Course:
class Course < ActiveRecord::Base
attr_accessible :code, :credits, :description, :hours, :id, :name, :pass_mark
has_many :course_instances, :dependent => :destroy
accepts_nested_attributes_for :course_instances
end
Course Instance
class CourseInstance < ActiveRecord::Base
attr_accessible :end_date, :id, :start_date
belongs_to :course
end
Routes
Sis::Application.routes.draw do
resources :courses do
resources :course_instances
end
root :to => 'home#index'
end
Course Instance Controller
class CourseInstancesController < ApplicationController
before_filter :find_course
def new
#course_instance = #course.course_instances.build
respond_to do |format|
format.html
end
end
def find_course
#course = Course.find(params[:course_id])
end
end
new.html.erb
<%= form_for ([#course, #course_instance]) do |f| %>
---- excluded for brevity ----
Rake Routes
new_course_course_instance GET /courses/:course_id/course_instances/new(.:format) course_instances#new
Am not sure what I did but it started to work. I tried backtracking to recreate the error however I have been unsuccessful so far. Weird! Thanks for the help from everyone!
Related
I can't get rails to update my nested attributes, though regular attributes work fine. This is my structure:
unit.rb:
class Unit < ApplicationRecord
has_many :unit_skill_lists
has_many :skill_lists, through: :unit_skill_lists, inverse_of: :units, autosave: true
accepts_nested_attributes_for :skill_lists, reject_if: :all_blank, allow_destroy: true
end
unit_skill_list.rb:
class UnitSkillList < ApplicationRecord
belongs_to :unit
belongs_to :skill_list
end
skill_list.rb:
class SkillList < ApplicationRecord
has_many :unit_skill_lists
has_many :units, through: :unit_skill_lists, inverse_of: :skill_lists
end
And this is (part of) the controller:
class UnitsController < ApplicationController
def update
#unit = Unit.find(params[:id])
if #unit.update(unit_params)
redirect_to edit_unit_path(#unit), notice: "Unit updated"
else
redirect_to edit_unit_path(#unit), alert: "Unit update failed"
end
end
private
def unit_params
unit_params = params.require(:unit).permit(
...
skill_list_attributes: [:id, :name, :_destroy]
)
unit_params
end
end
The relevant rows in the form (using formtastic and cocoon):
<%= label_tag :skill_lists %>
<%= f.input :skill_lists, :as => :check_boxes, collection: SkillList.where(skill_list_type: :base), class: "inline" %>
Any idea where I'm going wrong? I have tried following all guides I could find but updating does nothing for the nested attributes.
Edit after help from Vasilisa:
This is the error when I try to update a Unit:
ActiveRecord::RecordInvalid (Validation failed: Database must exist):
This is the full unit_skill_list.rb:
class UnitSkillList < ApplicationRecord
belongs_to :unit
belongs_to :skill_list
belongs_to :database
end
There is no input field for "database". It is supposed to be set from a session variable when the unit is updated.
If you look at the server log you'll see something like skill_list_ids: [] in params hash. You don't need accepts_nested_attributes_for :skill_lists, since you don't create new SkillList on Unit create/update. Change permitted params to:
def unit_params
params.require(:unit).permit(
...
skill_list_ids: []
)
end
UPDATE
I think the best options here is to set optional parameter - belongs_to :database, optional: true. And update it in the controller manually.
def update
#unit = Unit.find(params[:id])
if #unit.update(unit_params)
#unit.skill_lists.update_all(database: session[:database])
redirect_to edit_unit_path(#unit), notice: "Unit updated"
else
redirect_to edit_unit_path(#unit), alert: "Unit update failed"
end
end
I am following Ryan Bates railscasts video of friendly url. I am trying to implement that on my Category model by overriding the to_parammethod.
Seems like it's not working, or I am missing something.
Below is my url before overriding:
localhost:3000/search?category_id=1
After overriding the to_param the url remained same.
Following is my code:
Category model
class Category < ActiveRecord::Base
enum status: { inactive: 0, active: 1}
acts_as_nested_set
has_many :equipments, dependent: :destroy
has_many :subs_equipments, :foreign_key => "sub_category_id", :class_name => "Equipment"
has_many :wanted_equipments, dependent: :destroy
has_many :services, dependent: :destroy
validates :name, presence: true
validates_uniqueness_of :name,message: "Category with this name already exists", scope: :parent_id
scope :active, -> { where(status: 1) }
def sub_categories
Category.where(:parent_id=>self.id)
end
def to_param
"#{id} #{name}".parameterize
end
end
Controller
def search_equipments
begin
if (params.keys & ['category_id', 'sub_category', 'manufacturer', 'country', 'state', 'keyword']).present?
if params[:category_id].present?
#category = Category.active.find params[:category_id]
else
#category = Category.active.find params[:sub_category] if params[:sub_category].present?
end
#root_categories = Category.active.roots
#sub_categories = #category.children.active if params[:category_id].present?
#sub_categories ||= {}
Equipment.active.filter(params.slice(:manufacturer, :country, :state, :category_id, :sub_category, :keyword)).order("#{sort_column} #{sort_direction}, created_at desc").page(params[:page]).per(per_page_items)
else
redirect_to root_path
end
rescue Exception => e
redirect_to root_path, :notice => "Something went wrong!"
end
end
route.rb
get "/search" => 'welcome#search_equipments', as: :search_equipments
index.html.erb
The line which is generating the url
<%= search_equipments_path(:category_id => category.id ) %>
You are generating URLs in such a way as to ignore your to_param method. You're explicitly passing a value of only the ID to be used as the :category_id segment of your URLs. If you want to use your to_param-generated ID, then you need to just pass the model to the path helper:
<%= search_equipments_path(category) %>
I' ve got a controller
class Api::V1::QuestionsController < Api::V1::BaseController
authorize_resource
before_action :set_question, only:[:show]
api :GET, '/questions/id', 'This renders question by id'
def show
render json:#question
end
and a serializer
class QuestionSerializer < ActiveModel::Serializer
attributes :id, :title, :body, :created_at, :updated_at, :short_title
has_many :answers
has_many :comments
has_many :attachments
def short_title
object.title.truncate(10)
end
end
Here is a part of test
context 'answers' do
it 'included in question object' do
expect(response.body).to have_json_size(2).at_path("answers")
end
%w(id body created_at updated_at).each do |attr|
it "contains #{attr}" do
#NO UNDERSTANDING AT ALL!!!!!
expect(response.body).to be_json_eql(answers[0].send(attr.to_sym).to_json).at_path("answers/1/#{attr}")
end
end
end
The thing is that this test passes, so obviously (for unknown reasons for me) the scope differs. I am new to rails, could anybody tell me how could I set a default scope to normalize my responds, also I have to admit that In my app I have a similar not api controller and there are no problems with that scope.
Yep this is the way. I should only set order in serializer
class QuestionSerializer < ActiveModel::Serializer
attributes :id, :title, :body, :created_at, :updated_at, :short_title
has_many :answers
has_many :comments
has_many :attachments
def comments
object.comments.order(updated_at: :asc)
end
def answers
object.answers.order(updated_at: :asc)
end
def attachments
object.attachments.order(updated_at: :asc)
end
def short_title
object.title.truncate(10)
end
end
I will have an option were comment model can be used for a user to post in a person profile page and community page. Currently I'm working on community and would like some direction as I'm confused.
Current error I get is ActiveModel::ForbiddenAttributesError for my CommentsController#Create. Would like help if possible or help me point in the direction of fixing my mistake.
questions in regards to what view people are seeing comment is /communities/show
Models
User
has_one :profile
has_many :communities
has_many :comments, dependent: :destroy
Community
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
has_many :comments, dependent: :destroy
belongs_to :user
Comment
belongs_to :user
belongs_to :community
Routes
resources :communities do
resources :comments
end
Controllers
Communities
def show
#community = Community.friendly.find(params[:id])
#current_user = User.find(session[:user_id])
#comment = Comment.new
end
Comments
before_filter :load_community
def create
#comment = #community.comments.build(params[:comment])
#comment.user_id = current_user.id
if #comment.save
redirect_to :back
else
redirect_to "/"
end
# #comment = Comment.new(comment_params)
# #comment.user_id = session[:user_id]
# if #comment.save && #comment.community_id
# flash[:notice] = "Comment has been posted"
# else
# flash[:alert] = #comment.errors.full_messages
# end
end
private
def load_community
#community = Community.friendly.find(params[:community_id])
end
def comment_params
params.require(:comment).permit(:text, :user_id, :community_id, :profile_id)
end
Views
/communities/show
<%= render "profiles/index" %>
<h4><%= #community.title.capitalize! %></h4>
<%= #community.bio %>
<%= render "comments/new" %>
/comments/_new
<%= form_for ([#community, #comment]) do |f| %>
<%= f.text_area :text, placeholder: "Enter New Comment Here ...", :cols => 50, :rows => 3, :class => 'text_field_message', :id => 'new_comment' %>
<%= f.submit :class => 'new_comment_button' %>
<% end %>
Thank you everyone who helps explain where I'm making my mistake and also sorry in advance if I may need to ask what you might be requesting from me. For further questions please ask.
UPDATE
What I see in my console is
Started POST "/communities/dang/comments" for 127.0.0.1 at 2015-10-23 18:38:47 -0400
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"us8KNTLUZUdao13GK4OQId0YoUqf+CeLFIGjydnyWtI=", "comment"=> {"text"=>"www"}, "commit"=>"Create Comment", "community_id"=>"dang"}
Community Load (0.1ms) SELECT "communities".* FROM "communities" WHERE "communities"."slug" = 'dang' ORDER BY "communities"."id" ASC LIMIT 1
Completed 500 Internal Server Error in 11ms
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/comments_controller.rb:17:in `create'
Okay.
ActiveModel::ForbiddenAttributesError for my CommentsController#Create
This basically means you're not permitting the required attributes in your create method.
This is what you need:
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
#comment = #community.comments.new comment_params
#comment.save
end
private
def comment_params
params.require(:comment).permit(:text).merge(user_id: current_user.id)
end
end
You should read up on strong params to better understand how this works.
Polymorphic
You also have another issue which can be solved with a polymorphic association:
Simply, this allows you to associate a model with any number of others.
In your instance, where you can comment on users and communities, this functionality will serve well:
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, polymorphic: true
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :sent_comments, class_name: "Comment", foreign_key: :user_id
has_many :comments, as: :commentable
end
#app/models/community.rb
class Community < ActiveRecord::Base
has_many :comments, as: :commentable
end
This will allow you to perform the following:
#user = User.find params[:id]
#user.comments.new comment_params
def comment_params
params.require(:comment).permit(:text).merge(user_id: current_user.id) #-> current_user from Devise
end
This allows you to use #user.comments and #community.comments with a single association.
You'll have to migrate the commentable_id & commentable_type columns into your comments table, but after that the above code should work.
I have a nested form where users can book appointments. However, I've noticed an issue with the form where a user can fill out the required Client model fields and not the required Appointment model fields and the form still submits since for some reason the validation on the Appointment model isn't being triggered. The only time the Appointment validation is triggered is when the associated form fields are populated. How do I get the nested form to verify that the Appointment fields are being filled out? Since clients can have multi
Customer model:
class Customer < ActiveRecord::Base
has_many :appointments
accepts_nested_attributes_for :appointments
attr_accessible :name, :email, :appointments_attributes
validates_presence_of :name, :email
validates :email, :format => {:with => /^[^#][\w.-]+#[\w.-]+[.][a-z]{2,4}$/i}
validates :email, :uniqueness => true
end
Appointment model:
class Appointment < ActiveRecord::Base
belongs_to :customer
attr_accessible :date
validates_presence_of :date
end
Customers controller:
class CustomersController < ApplicationController
def new
#customer = Customer.new
#appointment = #customer.appointments.build
end
def create
#customer = Customer.find_or_initialize_by_email(params[:customer])
if #customer.save
redirect_to success_customers_path
else
# Throw error
#appointment = #customer.appointments.select{ |appointment| appointment.new_record? }.first
render :new
end
end
def success
end
end
Customers form view:
= simple_form_for #customer, :url => customers_path, :method => :post, :html => { :class => "form-horizontal" } do |customer_form|
= customer_form.input :name
= customer_form.input :email
= customer_form.simple_fields_for :appointments, #appointment do |appointment_form|
= appointment_form.input :date
UPDATE: Providing routes
resources :customers, :only => [:new, :create] do
get :success, :on => :collection
end
If a customer has to have an appointment:
class Customer < ActiveRecord::Base
has_many :appointments
accepts_nested_attributes_for :appointments
attr_accessible :name, :email, :appointments_attributes
validates_presence_of :name, :email, :appointment # <- add your appointment
....
end
This will require each customer has at least one appointment.
EDIT based on comment
Instead of using build in your controller, I think you can use create instead which will then associate that appointment with the customer and force validation.
Customers controller:
def edit
#customer = Customer.find_or_initialize_by_email(params[:customer])
#appointment = #customer.appointments.create
end
And you'd do the same in your new method