Ruby on Rails Difficult Form/Associations - ruby-on-rails

I have a Rails 4.2 app which has 'Rooms', 'Bookings' and 'Extras'.
When making a booking it is for a room e.g. website.com/rooms/1/bookings/1
I have extras which I want to be associated with the booking for that room via check-boxes.
How can this be implemented? I've been reading about has_many :foo, :through => :bar associations but I'm not sure if that's the way to go.
The relevant code looks like this:
<!-- app\views\bookings\_form.html.erb -->
<%= form_for([#room, #booking]) do |f| %>
<p>
<%= f.label 'Select Customer:' %>
<%= f.collection_select :user_id, User.all, :id, :customer_name %>
</p>
<p>
<%= f.label 'start_time', 'Start Date and Time:' %>
<%= f.datetime_select :start_time, { minute_step: 15 } %>
</p>
<p>
<%= f.label 'length', 'Length of booking in hours:' %>
<%= f.number_field 'length', min: 1 %>
</p>
<p>
<%= f.label 'Room Price:' %>
<%= number_to_currency #room.price, unit: "£" %>
</p>
<p>
<%= f.label 'Extras:' %>
<%= f.collection_check_boxes :extra_ids, Extra.all, :id, :extra_info %>
</p>
<%= f.submit 'Submit' %>
<% end %>
# app\models\booking.rb
class Booking < ActiveRecord::Base
belongs_to :room
belongs_to :user
has_many :additions
has_many :extras, :through => :additions
end
# app\models\extra.rb
class Extra < ActiveRecord::Base
belongs_to :extracat
has_many :additions
has_many :bookings, :through => :additions
def extra_info
"#{name}"
end
end
# This model is for the has_many through testing I tried
# app\models\addition.rb
class Addition < ActiveRecord::Base
belongs_to :booking
belongs_to :extra
end
# Relevant section of schema
create_table "additions", force: :cascade do |t|
t.integer "booking_id"
t.integer "extra_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "bookings", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.integer "length"
t.integer "room_id"
t.integer "user_id"
t.integer "extra_id"
end
EDIT - The section within the bookings show page.
# app\views\bookings\show.html.erb
<% #booking.extras.each do |e| %>
<%= e.name %>,
<% end %>
EDIT - Adding bookings controller
class BookingsController < ApplicationController
respond_to :html, :xml, :json
before_action :find_room
def index
#bookings = Booking.where("room_id = ? AND end_time >= ?", #room.id, Time.now).order(:start_time)
respond_with #bookings
end
def new
#booking = Booking.new(room_id: #room.id)
end
def create
#booking = Booking.new(params[:booking].permit(:room_id, :start_time, :length))
#booking.room = #room
if #booking.save
redirect_to room_bookings_path(#room, method: :get)
else
render 'new'
end
end
def show
#booking = Booking.find(params[:id])
end
def destroy
#booking = Booking.find(params[:id]).destroy
if #booking.destroy
flash[:notice] = "Booking: #{#booking.start_time.strftime('%e %b %Y %H:%M%p')} to #{#booking.end_time.strftime('%e %b %Y %H:%M%p')} deleted"
redirect_to room_bookings_path(#room)
else
render 'index'
end
end
def edit
#booking = Booking.find(params[:id])
end
def update
#booking = Booking.find(params[:id])
# #booking.room = #room
if #booking.update(params[:booking].permit(:room_id, :start_time, :length))
flash[:notice] = 'Your booking was updated succesfully'
if request.xhr?
render json: {status: :success}.to_json
else
redirect_to resource_bookings_path(#room)
end
else
render 'edit'
end
end
private
def save booking
if #booking.save
flash[:notice] = 'booking added'
redirect_to room_booking_path(#room, #booking)
else
render 'new'
end
end
def find_room
if params[:room_id]
#room = Room.find_by_id(params[:room_id])
end
end
def booking_params
params.require(:booking).permit(:user_id, :extra_id)
end
end
How is it possible to associate the extras with a booking? As so far they are not being saved with the booking into the database. Is this a controller issue?

You're not permitting the parameters correctly - the name is extra_ids. In addition since the parameter is an array you need to permit it like so:
params.require(:booking).permit(:room_id, :start_time, :length, :extra_ids => [])
Personally I recommend setting action controller to raise an error when unpermitted parameters are encountered in development or tests - very easy otherwise to miss the log messages

Related

How to call worker role permission through role_permission

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

Couldn't find Student with 'student_id'= RUBY ON RAILS

I am trying to pull up an edit page for my simple system.
Here's my controller for student look like:
class StudentsController < ApplicationController
def index
#students = Student.newest_first
end
def show
#student = Student.find(params[:student_id])
end
def new
#student = Student.new
end
def create
#student = Student.new(student_params_create)
if #student.save
flash[:notice] = "Student record created successfully"
redirect_to(students_path)
else
render('new')
end
end
def edit
#student = Student.find(params[:student_id])
end
def update
#student = Student.find(params[:student_id])
if #student.update_attributes(student_params_update)
flash[:notice] = "Students updated"
redirect_to(students_path)
else
render('edit')
end
end
def delete
#student = Student.find(params[:student_id])
end
def destroy
#student = Student.find(params[:student_id])
#student.destroy
end
private
def student_params_create
params.require(:student).permit(:first_name, :last_name, :birthday, :email, :subjects, :username, :password)
end
def student_params_update
params.require(:student).permit(:first_name, :last_name, :birthday, :email, :subjects)
end
end
And I am trying to here's my index page where I can click edit on my student.
<% #students.each do |student|%>
<%= student.first_name %>
<%= student.student_id %>
<%= link_to("Show", student_path(student), :class => 'action show') %>
<%= link_to("Edit", edit_student_path(student), :class => 'action edit') %>
<%= link_to("Delete", delete_student_path(student), :class => 'action delete') %>
<% end %>
For my form edit, I got the ff:
<h2>Update Subject</h2>
<%= form_for(#student) do |f| %>
<%= f.text_field(:first_name) %>
<%= f.datetime_select(:created_at, :order => [:month, :day, :year]) %><
<%= f.submit("Update Subject") %>
<% end %>
When I tried to go to my resource for students: http://localhost:3000/students/
And click on one of my students I got the ff: error:
Any idea what am I doing wrong????
EDIT:
I set the 'id' to false during migration.
def up
create_table :students, :id => false do |t|
t.integer "student_id",:primary_key => true
t.string "first_name", :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => ' ', :null => false
t.string "birthday"
t.string "subjects"
t.string "teachers"
t.string "username", :limit => 25
t.string "password_digest"
t.timestamps
end
reversible do |dir|
dir.up { execute "ALTER TABLE students AUTO_INCREMENT = 1000" }
end
# execute "CREATE SEQUENCE students_student_id_seq OWNED BY students.student_id INCREMENT BY 1 START WITH 1001"
end
def down
drop_table :students
# execute "DELETE SEQUENCE students_student_id_seq"
end
Routes:
resources :students do
member do
get :delete
end
end
UPDATE
You could need to update your routes.rb with
resources :students, param: :student_id
And your Student model with
self.primary_key = :student_id
NOTE that you're having the ActiveRecord::RecordNotFound exception because find method throws exception when it can't actually find the record needed. Try to use find_by_id method instead. It will just return nil in that situation. You'll be able to handle it easily and your app will not crash here. So you could edit your show action like this
def show
#student = Student.find_by_id(params[:student_id])
end

(Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it)

I have a very basic Photo and Comments model that works and then I have a built a Cflags model that is used to flag comments. I am getting the following error from Heroku log when I visit the photos/show.html.erb view.
LoadError (Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it)
photos/show.html.erb
.
.
<% #photo.comments.each do |comment| %>
<%= form_for([comment, Cflags.new]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
PhotosController
def show
#photo = Photo.approved.find(params[:id])
end
CommentsController
def create
#photo = Photo.find(params[:photo_id])
#comment = #photo.comments.build(comment_params)
#comment.save
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
class CFlag < ActiveRecord::Base
belongs_to :comment, counter_cache: true
belongs_to :user, counter_cache: true
validates :user_id, presence: true
validates :comment_id, presence: true
validates :user_id, uniqueness: {
scope: [:comment_id],
message: 'You can only flag a comment once. Thank you for your feedback.'
}
default_scope -> { order(created_at: :desc) }
end
class CflagsController < ApplicationController
before_action :logged_in_user
def create
#comment = Comment.find(params[:comment_id])
#cflag = #comment.cflags.build(cflag_params)
if #cflag.save
if #comment.cflags_count > 1
#comment.update_attribute(:approved, false)
flash[:success] = "Flag created! Comment ##{#comment.id} has been removed for review. Thank you for your feedback"
redirect_to :back
else
flash[:success] = "Flag created! Thank you for your feedback"
redirect_to :back
end
else
redirect_to :back, notice: #cflag.errors.full_messages
end
end
private
def cflag_params
params.require(:cflag).permit(:user_id, :comment_id).merge(user_id: current_user.id)
end
end
resources :photos do
resources :comments do
resources :cflags
end
end
create_table "cflags", force: :cascade do |t|
t.integer "comment_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "cflags", ["comment_id"], name: "index_cflags_on_comment_id"
add_index "cflags", ["user_id"], name: "index_cflags_on_user_id"
If I change the form to:
<% #photo.comments.each do |comment| %>
<%= form_for([comment, comment.cflags.build]) do |f| %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.submit "Report Inappropiate" %>
<% end %>
<% end %>
I get the same error:
LoadError (Unable to autoload constant Cflag, expected /app/app/models/cflag.rb to define it):
There is a typo in the controller name.
Your model is CFlag with F uppercase but your controller has f lowercase.
Change it to CFlagsController
class CFlagsController < ApplicationController
before_action :logged_in_user
def create
...
end
end

Dealing With Multiple Objects

I am new to Rails and currently trying to add a patient to an existing dentist appointment. I am having difficulty setting up my views and controllers properly. How can I properly accomplish this?
Note: With the way I have set things up, I can create an appointment and tie it to a dentist. Of course, the patient_id is missing.
Models:
class Dentist < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :dentists
belongs_to :patients
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :dentists, :through => :appointments
end
Schema:
ActiveRecord::Schema.define(version: 20151107052115) do
create_table "appointments", force: :cascade do |t|
t.integer "dentist_id"
t.integer "patient_id"
t.datetime "appt_date"
end
create_table "dentists", force: :cascade do |t|
t.string "name"
end
create_table "patients", force: :cascade do |t|
t.string "name"
end
end
Routes:
Rails.application.routes.draw do
concern :commentable do
resources :appointments
end
resources :dentists, concerns: :commentable
resources :patients, concerns: :commentable
end
Dentists Controller:
class DentistsController < ApplicationController
def new
#dentist = Dentist.new
end
def create
#dentist = Dentist.new(dentist_params)
if #dentist.save
redirect_to dentists_path
else
render :new
end
end
...
end
Appointments Controller:
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new(appt_params)
if Appointment.exists?(:appt_date => #appointment.appt_date)
render :new
else
#appointment.save
redirect_to dentist_path(#dentist)
end
end
...
end
Patients Controller:
TBD
Dentists View (Show):
<p><%= #dentist.name %> DDS</p>
<% if #dentist.appointments.any? %>
<% #dentist.appointments.each do |appt| %>
<ul>
<li><%= appt.appt_date %></li>
<p><%= link_to "Edit", edit_dentist_appointment_path(#dentist, appt) %> |
<%= link_to 'Delete', dentist_appointment_path(#dentist, appt), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %> |
<%= link_to 'Add Patient', new_patient_path %></p>
</ul>
<% end %>
<% else %>
<p>There are currently no appointments scheduled</p>
<% end %>
<p><%= link_to 'Delete Dentist', dentist_path(#dentist), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %></p>
<p><%= link_to 'Create an appointment', new_dentist_appointment_path(#dentist) %></p>
<p><%= link_to 'Return to list', root_path %></p>
I am new to Rails
Welcome!
You need to change your belongs_to references to be singular:
class Appointment < ActiveRecord::Base
belongs_to :dentist
belongs_to :patient
end
--
Because I can't see where you're trying to achieve this functionality, I'll show you what I'd do (using the appointment#edit action):
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def edit
#appointment = Appointment.find params[:id]
end
def update
#appointment = Appointment.find params[:id]
#appointment.save appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/edit.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
--
If you're trying to set the patient from your appointments#create method, you'll be best doing this:
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/new.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
I think what you are asking, is can you create an appointment through both the Dentist model and the Patient model at the same time eg. #dentist.#patient.apointment.new
you cannot do that. Based on the relationships you have set up, you will either want to create the appt from the Dentist, like you have now, and pass in the patient ID as an attribute, or vice-versa. OR, create through your Appointment model eg. Appointment.new(dentist: #dentist, patient: #patient, ...)

I can't insert to my database rails

i can't insert to my database whats is my problem?
it's bowling game and i have two tables with name "Player" and "Result"
view
<%= form_for player_new_path(#player) do |f|%>
<div class="text_field">
<p>
<%= f.label "Spelare namn" %>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit "Lägg till en spelare"%>
</p>
</div>
Controller
def create
#player = Player.new(params[:players])
if #player.save
redirect_to players_new_path
else
render :action => "new"
end
end
Not work :/
my model:
class Player < ActiveRecord::Base # attr_accessible :title, :body
belongs_to :result
end
and my migrations:
class CreatePlayers < ActiveRecord::Migration
def change
create_table :players do |t|
t.string "name"
t.references :results
t.timestamps
end
Check your params hash. I bet the key isn't 'players', it's probably 'player'.
#player = Player.new(params[:players]) should probably be #player = Player.new(params[:player]) (You are getting a single player as a param)
Otherwise, what error are you getting

Resources