Couldn't find Course without an ID - ruby-on-rails

When I try to access app/views/companies/courses/show.html.erb(which is redirected after app/views/companies/courses/new.html.erb), rails server says that it cannot find a course without an ID.
Also, when I run Course.all on rails console, the courses have the correct id, the correct description and the correct company id but no name attribute.
App/controllers/companies/courses_controller:
class Companies::CoursesController < ApplicationController
before_action :authenticate_company!
def new
#course = Course.new
end
def create
#course = current_company.courses.create(course_params)
if #course.save
redirect_to companycourse_path(:course => #course.id)
else
render 'new'
end
end
def show
#course = current_company.courses.find(params[:id])
end
def index
#courses = current_company.courses.all
end
private
def course_params
params.require(:course).permit(:title, :description)
end
end
App/models/course.rb:
class Course < ApplicationRecord
belongs_to :company
end
App/models/company.rb:
class Company < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :courses
end
Config/routes.rb:
Rails.application.routes.draw do
devise_for :companies
devise_for :users
root 'static_pages#home'
get 'users/:id' => 'users#show'
resources :users, only: [:show]
get 'companies/:id' => 'companies#show'
resources :companies, only: [:show] do
resources :courses, only: [:show,:new,:create,:index]
end
devise_scope :user do
get 'signup', to: 'users/registrations#new'
get 'login', to: 'users/sessions#new'
end
devise_scope :company do
get 'companysignup', to: 'companies/registrations#new'
get 'companylogin', to: 'companies/sessions#new'
get 'newcourse', to:'companies/courses#new'
post 'newcourse', to:'companies/courses#create'
get 'companycourse', to:'companies/courses#show'
get 'companycoursesindex', to:'companies/courses#index'
end
end

I had a similar problem building my app not long ago and my problem was coming from controllers.
In your routes.rb I would add resources :companies, :has_many => :courses.I also would add a validation to your course model in models/course.rb to make sure that when you save it to the database your object has a title: class Course < ApplicationRecord
belongs_to :company validates :title, presence: :true
end
Also try using this or something along lines in your courses_controller.rb instead of your current code:
def create
#company = find_company
#course = Course.new(course_params)
#course.company_id = current_company.id
if #course.save
your conditions here
else
your conditiona here
end
end
private
def find_company
params.each do |name, value|
if name == 'company_id'
return #company = Company.find(value.to_i)
end
end
nil
end
I hope it helps.

Related

Can't assign created_by as current_user. Database is saving as nil. Rails

I created a userstamps method to know which user created and edited a data. I have this table Illustrator that I am trying to assign created_by to the current_user id. But when it saves, created_by is nil and don't show any error. When I put raise right before illustrator.save, the #illustrator.created_by is exactly the current_user.id but when I check the value in db, is nil.
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:custom_authenticatable
belongs_to :account
belongs_to :role
cattr_accessor :current_user
validates :email, presence: true, uniqueness: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password,
length: { minimum: 6 },
if: -> { new_record? || !password.nil? }
enum gen: { masculino: 1, feminino: 2 }
private
#apenas usuário com role de admim pode logar.
def valid_for_custom_authentication?(password)
#self.role.name == 'admin'
self.role_id === 2
end
end
class ApplicationController < ActionController::Base
before_action :authenticate_user!
helper_method :current_user
private
def current_user=(user)
#current_user = user
end
end
class IllustratorsController < ApplicationController
before_action :authenticate_user!
def new
#illustrator = Illustrator.new
end
def create
#illustrator = Illustrator.new(illustrator_params)
#illustrator.created_by = current_user.id
if #illustrator.save
redirect_to illustrator_path(#illustrator), notice: 'O autor foi criado com sucesso.'
else
render :new
end
end
private
def illustrator_params
params.require(:illustrator).permit(:name, :display_name, :email, :phone, :status, :notes, :created_by)
end
end
Rails.application.routes.draw do
get 'payments/index'
devise_for :users
root to: 'pages#home'
resources :books
resources :authors
resources :publishers
resources :illustrators
resources :plans do
resources :accounts, only: %i[new create] do
end
end
resources :payments, only: %i[index]
resources :accounts, only: %i[index show edit update destroy] do
resources :users, only: %i[new create] do
resources :roles
end
end
resources :users, only: %i[edit update index show destroy]
#API routes
namespace :api do
namespace :v1 do
get 'default/:id', to: 'plans#change_plans'
get 'plans', to: 'plans#index'
get 'plans/id', to: 'plans#show'
get 'accounts', to: 'accounts#return'
post 'users', to: 'users#create'
#resources :users
post '/auth/login', to: 'authentication#login'
get '/*a', to: 'application#not_found'
post 'password/forgot', to: 'users#forgot_password'
end
end
end
If you are using devise, you dont need this part:
helper_method :current_user
private
def current_user=(user)
#current_user = user
end
Where did you even find it? Remove it from your codebase!
With devise you don't need any getter or setter methods.
It can possibly be the reason it's not working for you.
If in application_controller you have the before_action :authenticate_user!, current_user method should work right out of the box!
As well you don't need to add the before_action :authenticate_user! again here
class IllustratorsController < ApplicationController
before_action :authenticate_user!
, because you already made it application-wide in application_controller.
Your create method is good. Try removing the abundancies and it should all work.
You have just setter method in your controller where you are setting current_user
def current_user=(user)
#current_user = user
end
You need to add getter method as well like following so that you can use it in your controller
def current_user
#current_user
end
In ruby there is short cut for this attr_accessor :current_user Ref this

Rails - Model not saving to db

I'm completing this airbnb clone course (https://code4startup.com/projects/build-airbnb-with-ruby-on-rails-level-1) but have diverted a bit in order to complete my own project; a marketplace for education camps. Therefore I've added an additional model 'Courses'. It now has User>Listing>Course. This Courses model is working in rails console but not saving to my database when I'm running the server. Any suggestions would be appreciated...
Error Message
ActiveRecord::RecordInvalid in CoursesController#create
Validation failed: Listing must exist
Models
class User < ApplicationRecord
has_many :listings
has_many :courses, :through => :listings
end
class Listing < ApplicationRecord
belongs_to :user
has_many :courses
validates :listing_type, presence: true
validates :course_type, presence: true
validates :accommodate, presence: true
end
class Course < ApplicationRecord
belongs_to :listing
validates :curriculum_type, presence: true
validates :course_places, presence: true
end
Course Controller
class CoursesController < ApplicationController
before_action :set_course, except: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def index
#courses = current_user.courses
end
def new
#course = current_user.courses.build
end
def create
#course = current_user.courses.build(course_params)
if #course.save!
redirect_to course_listing_path(#course), notice: "Saved..."
else
render :new, notice: "Something went wrong..."
end
end
def show
end
def listing
end
def pricing
end
def description
end
def photo_upload
end
def amenities
end
def location
end
def update
if #course.update(course_params)
flash[:notice] = "Saved..."
else
flash[:notice] = "Something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name, :curriculum_type, :summary, :address, :course_places, :start_date, :finish_date, :price)
end
end
Routes
Rails.application.routes.draw do
root 'pages#home'
devise_for :users,
path: '',
path_names: {sign_in: 'login', sign_out: 'logout', edit: 'profile', sign_up: 'registration'},
controllers: { omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations' }
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :users, only: [:show]
resources :listings, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
end
end
resources :courses, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
end
end
end
below you can read my comments with the # sign
You are trying to save an object Course that has belongs_to listings so it is expected that it has as course.listing_id the id of an existing Listing
def create
#course = current_user.courses.build(course_params)
# You need to set #course.listing_id to an existing Listing
# You need to find that listing and save it in a variable.
# I am not getting into your logic, because your code is confused and need many adjustments
listing = Listing.find() # include hear your logic to find an existing listing from the db
#course.listing_id = listing.id
if #course.save!
redirect_to course_listing_path(#course), notice: "Saved..."
else
render :new, notice: "Something went wrong..."
end
end

How to set a like button in rails

Hello I have an exercise app where a user should be able to Like some products.
I could find a way to display the product he liked, but I really can't figure how to create and make work the like button.
I am not using any gem, I wan't to understand how to do it from Scratch.
Here are my models:
class User < ApplicationRecord
has_many :likes
has_many :liked_products, through: :likes, source: :product
end
class Product < ApplicationRecord
has_many :likes
end
class Like < ApplicationRecord
belongs_to :user
belongs_to :product
end
In my view product show where I want the like button:
<h1><%= #product.name %></h1>
<%= link_to "Like", product_likes_path(#product), method: :put, remote: true %>
my routes:
Rails.application.routes.draw do
root to: 'visitors#index'
devise_for :users
resources :users
resources :products do
resource :likes
end
end
That's my products controller, I think things must come in here but I don't know HOW!
class ProductsController < ApplicationController
before_action :find_product, only: :show
def index
#products = Product.all
end
def show
##product.like => gives an error 404
end
private
def find_product
#product = Product.find(params[:id])
end
end
I had created a likes controller but it seems it is not useful.... So... I gave up there...
class LikesController < ApplicationController
def new
#like = Like.new(like_params)
end
def create
#like = Like.new(like_params)
end
private
def like_params
params.require(:likes).permit(:user_id, :product_id)
end
end
I would really enjoy some light on this please :)
Finally found out how to set the controller
class LikesController < ApplicationController
def create
#user = current_user.id
#product = params[:product_id]
likes = {user_id: #user, product_id: #product}
#like = Like.new(likes)
#like.save!
if #like.save
redirect_to user_path(#user)
else
redirect_to product_path
end
end
end
the buttton
<%= link_to "Like", product_likes_path(#product), method: :post %>
routes
Rails.application.routes.draw do
root to: 'products#index'
devise_for :users
resources :users
resources :users do
resources :products do
resources :likes
end
end
end
You could try something along these lines:
Routes:
Rails.application.routes.draw do
root to: 'visitors#index'
devise_for :users
resources :users do
resources :products do
resources :likes
end
end
resources :products do
resource :likes
end
end
Which will give you something like:
... other routes ...
user_product_likes GET /users/:user_id/products/:product_id/likes(.:format) likes#index
POST /users/:user_id/products/:product_id/likes(.:format) likes#create
new_user_product_like GET /users/:user_id/products/:product_id/likes/new(.:format) likes#new
edit_user_product_like GET /users/:user_id/products/:product_id/likes/:id/edit(.:format) likes#edit
user_product_like GET /users/:user_id/products/:product_id/likes/:id(.:format) likes#show
PATCH /users/:user_id/products/:product_id/likes/:id(.:format) likes#update
PUT /users/:user_id/products/:product_id/likes/:id(.:format) likes#update
DELETE /users/:user_id/products/:product_id/likes/:id(.:format) likes#destroy
... other routes ...
Then:
<%= link_to "Like", user_product_likes_path(#user, #product), method: :post, remote: true %>
And in your LikesController:
class LikesController < ApplicationController
def new
#like = Like.new(like_params)
end
def create
#like = Like.new(like_params)
if #like.save
... do something happy
else
... do something sad
end
end
private
def like_params
params.require(:likes).permit(:user_id, :product_id)
end
end
Untested, so buyer beware. You might need to fiddle with your like_params and other stuff.

Has many through or Has and belongs to many missing ID

Just trying to create a simple workout log here I'm getting this error when I try and create weights
"Workout ID must exist"
Models
class Workout < ApplicationRecord
belongs_to :user
has_many :exercises
has_many :weights, through: :exercises
accepts_nested_attributes_for :exercises, :allow_destroy => true
accepts_nested_attributes_for :weights, :allow_destroy => true
validates :bodypart, presence: true, length: { maximum: 255 }
end
class Weight < ApplicationRecord
belongs_to :exercise
belongs_to :workout
validates :amount, presence: true, length: { maximum: 255 }
validates :reps, presence: true, length: { maximum: 255 }
end
class Exercise < ApplicationRecord
belongs_to :workout
has_many :weights
accepts_nested_attributes_for :weights
validates :name, presence: true
validates_associated :weights
end
After reading a few things I thought that a has many through would be the option for these associations but now I'm not so sure. When I try and create a weight for exercise I get the exercise ID but can't seem to get the workout ID despite a number of attempts. I'm not sure if its simply a controller issue or if Im missing something bigger here.
My Current Weights Controller
class WeightsController < ApplicationController
before_action :authenticate_user!
def new
#weight = Weight.new
end
def create
#exercise = Exercise.find(params[:exercise_id])
#weight = #exercise.weights.build(weight_params)
if #weight.save
flash[:notice] = "Set saved"
redirect_to exercise_path(#exercise)
else
redirect_to exercise_path(#exercise)
flash[:notice] = "#{#weight.errors.full_messages.to_sentence}"
end
end
def edit
end
def update
end
def destroy
weight = Weight.find(params[:id])
#weight.destroy
redirect_to root_path
end
private
def weight_params
params.require(:weight).permit(:amount, :reps)
end
end
Routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: { omniauth_callbacks: "callbacks" }
root 'articles#index'
get '/demo', to: 'static_pages#demo'
get '/about', to: 'static_pages#about'
get '/test', to: 'static_pages#index'
resources :articles
resources :workouts do
resources :exercises
end
resources :exercises do
resources :weights
end
resources :workouts do
member do
put :is_finished
put :unfinish
end
end
resources :exercises do
member do
put :is_finished
put :unfinish
end
end
resources :books, :only => :index
end
Exercises controller
class ExercisesController < ApplicationController
before_action :authenticate_user!
# before_action :set_exercise, except: [:index, :new, :create]
def new
#exercise = Exercise.new
#exercise.weights.new
end
def index
#workout = Workout.find(params[:workout_id])
#exercise = #workout.exercises
end
def create
#workout = Workout.find(params[:workout_id])
#exercise = #workout.exercises.build(exercise_params)
if #exercise.save
redirect_to workout_exercise_path(#workout, #exercise)
flash[:notice] = "Exercise created, enter weight and reps for your first set"
else
redirect_to #workout
flash[:notice] = "#{#exercise.errors.full_messages.to_sentence}"
end
end
def edit
end
def update
#exercise = Exercise.find(params[:id])
if #exercise.update_attributes(exercise_params)
redirect_to exercise_path(#exercise)
else
flash[:notice] = "Something went wrong"
end
end
def show
#exercise = Exercise.find(params[:id])
#weights = #exercise.weights.order('created_at DESC').paginate(page: params[:page], per_page: 5)
end
I do have an ID column for workout_id set on my Weights model but it always populates NULL whenever I can manage to create an exercise. Also, in the rails console I can find a workout and call #workout.weights and it returns the weights associated with the workout just fine. I just can't get the workout ID to populate in Weight model. Wondering if has and belongs to many would be better OR if my has many through is just set up wrong. I did try "inverse_of" without any luck. Any help would be appreciated.
The answer is quite simple. To add reverse functionality, you have to explicitly state it, through the exercise.
in weight.rb remove belongs_to :workout and add the following
def workout
self.exercise.workout
end
Perhaps you will need to add logic to avoid nilClass errors.
Now you do have #weight.workout through #weight.exercise

Nested resources, undefined method nilClass

I am getting an error "undefined method " when I try to build a nested resource on action 'new' in rails 4.2
Here's my routes:
devise_for :medics
resources :patients, shallow: true do
resources :consultations do
resources :prescriptions
end
end
I have Devise for system logon, and rather than use "User" to the model name, I use "Medic" in order to use the registry to devise to create a type of medical profile with a new fields like name, phone, etc. (I don't know if here's the problem)...
Patients controller:
class PatientsController < ApplicationController
before_action :set_medic
def new
#patient = #medic.patients.new
end
def create
#patient = #medic.patients.new(patient_params)
end
def set_medic
#medic = Medic.find_by_id(params[:medic_id])
end
Model:
class Medic < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :patients, :dependent => :destroy
end
class Patient < ActiveRecord::Base
belongs_to :medic, :foreign_key => :medic_id, :dependent => :destroy
has_many :consultations
accepts_nested_attributes_for :consultations
end
View:
<%= link_to 'New Patient', new_patient_path(#medic) %>
rake routes:
new_patient GET /patients/new(.:format) patients#new
Error:
undefined method `patients' for nil:NilClass
in this line: #patient = #medic.patients.new
Any idea? thaks in advance
The problem is very simple.
You're calling the following on each request:
def set_medic
#medic = Medic.find_by_id(params[:medic_id])
end
The problem is that you're not passing medic_id through your routes:
devise_for :medics
resources :patients, shallow: true do #-> no medic_id here
resources :consultations do #-> or here
resources :prescriptions #-> or here
end
end
Therefore, what happens is that you're trying to find a Medic without any id, hence the NilClass error.
You're getting confused with the nested resources directive in Rails routes:
#DON'T use this - it's just an example
#config/routes.rb
resources :medics do
resources :patients #-> url.com/medics/:medic_id/patients/:id
end
As you're using Devise, I think you'd be able to get away with scoping your calls around the current_medic helper (which is what I presume you're doing)...
-
Fix
#app/controllers/patients_controller.rb
class PatientsController < ApplicationController
def new
#patient = current_medic.patients.new
end
def create
#patient = current_medic.patients.new(patient_params)
end
end
This way, you'll be able to use (as you're using current_medic):
<%= link_to "New", new_patient_path %>
Since you didn't put the medic_id in your route, you probably have to clarify the parameter in your view like this:
<%= link_to 'New Patient', new_patient_path(:medic_id => #medic.id) %>

Resources