I got a small app where I got a simple user model, and I'm currently trying to add some soft delete functionality (yes, I know there are some gems for this). Works fine for users, however when I delete a user, the associated topic view collapses as it cannot find the deleted user anymore due to the default scope I guess.
Any idea how to get arround this?
class User < ActiveRecord::Base
has_many :topics
has_many :comments
default_scope { where(active: true) }
end
def index
#topics=Topic.all
end
class UsersController < ApplicationController
def index
if current_user and current_user.role == "admin"
#users=User.unscoped.all
else
#users=User.all
end
end
Part of the view (topic.user.name is where it stops working):
<% #topics.each do |topic| %>
<tr>
<td><%=link_to topic.title, topic %></td>
<td><%=h topic.description %></td>
<td><%= topic.user.name %></td>
<td><%=h topic.created_at.strftime('%Y %b %d %H:%M:%S') %></td>
<td><%=h topic.updated_at.strftime('%Y %b %d %H:%M:%S') %></td>
</tr>
<% end %>
This is why default_scope is evil. By now you've realized that having a default_scope can lead to a wild goose chase.
You can change user.rb to this:
class User < ActiveRecord::Base
has_many :topics
has_many :comments
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) } # can be used in future when you want to show a list of deleted user in a report or on admin panel.
end
then controller:
class UsersController < ApplicationController
def index
#users = User.scoped
#users = #users.active if current_user && current_user.role != 'admin'
end
end
view will have no problem of your topics#index now:
<% #topics.each do |topic| %>
<tr>
<td><%=link_to topic.title, topic %></td>
<td><%=h topic.description %></td>
<td><%= topic.user.name %></td>
<td><%=h topic.created_at.strftime('%Y %b %d %H:%M:%S') %></td>
<td><%=h topic.updated_at.strftime('%Y %b %d %H:%M:%S') %></td>
</tr>
<% end %>
Whenever you want to display active users just do: #users = User.active.
Use this, and leave the associations as it is.
<td><%= topic.user.try(:name) %></td>
Related
i'll start how my models looks:
User -> has_one :customer, has_one :employee, has_many :appointments
Customer -> belongs_to :user
Employee -> belongs_to :user
Appointment -> belongs_to :user
And now in view:
<% #appointments.order(:appointment_date).each do |appointment| %>
<% if appointment.confirmation == wait_or_confirmed?(#status) %>
<tr>
<td><%= appointment.purpose %></td>
<td><%= appointment.appointment_date %></td>
<td><%= appointment.appointment_time %></td>
<td><%= Employee.find(appointment.employee_id).first_name %> <%= Employee.find(appointment.employee_id).last_name %></td>
<td><%= Customer.find(appointment.customer_id).first_name %> <%= Customer.find(appointment.customer_id).last_name %></td>
<td><%= link_to 'Show', appointment_path(appointment), class: "btn btn-primary" %></td>
</tr>
<% end %>
<% end %>
My brother told me i shouldnt pick up informtaions from database in views so i tried using includes:
#appointments = Appointment.all
#users = User.includes(:appointments)
And after combination in view it still doesn't work. Can someone help me?
Instead of this:
Employee.find(appointment.employee_id).first_name
Do this:
appointment.employee.first_name
For performance, in the controller, you can also replace this:
#appointments = Appointment.all
With this:
#appointments = Appointment.all.includes(:employee, :customer)
By doing so, you are eager-loading all associated employee and customer data from the database in a single query, rather than one at a time.
I want to set up sort order for active record collection proxy in table.
It should be sorted by number of available rooms (from highest to lowest).
The trick is that #rooms.reserved is a boolean and to calculate quantity of free/reserved rooms I have to use helper method to avoid record collection proxy errors. I get proper results, but I need to sort table by number of available rooms.
I have two models: Room and Hotel.
class Room < ApplicationRecord
belongs_to :hotel, optional: true # avoiding rails 5.2 belongs_to error
accepts_nested_attributes_for :hotel
end
and
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
end
I have table:
<table>
<tr>
<th>Name</th>
<th>Rooms count</th>
<th>Rooms status: in reserve || free</th>
</tr>
<% #hotels.each do |hotel| %>
<tr>
<td><%= hotel.name %></td>
<td><%= hotel.rooms_count %></td>
<td><%= rooms_reservation_status(hotel.rooms) %></td> <!-- rooms_reservation_status helper method in application_helper.rb -->
<td ><%= link_to 'Show', hotel_path(hotel) %></td>
<td><%= link_to 'Destroy', hotel, method: :delete, data: { confirm: 'Are you sure?' } %>
</tr>
<% end %>
</table>
Helper method
# rooms_reservation_status iterates throught ActiveRecord::Associations::CollectionProxy
# and calculates the sum of free rooms aswell as a sum of reserved rooms
def rooms_reservation_status(rooms)
reserved = 0
free = 0
rooms.each do |r|
r.reserved == true ? reserved+=1 : free+=1
end
"#{reserved} || #{free}"
end
Active Record table for rooms:
class CreateRooms < ActiveRecord::Migration[5.1]
def change
create_table :rooms do |t|
t.boolean :reserved, :default => false
t.belongs_to :hotel, index: true
t.timestamps
end
end
end
I would add a class method on the Room model in order to return for a given collection the number of free rooms and reserved rooms:
class Room < ApplicationRecord
belongs_to :hotel, optional: true
accepts_nested_attributes_for :hotel
def self.reserved_count
where(reserved: true).count
end
def self.free_count
where(reserved: false).count
end
end
Once you have implemented, you can call it from the relationship declared in Hotel model:
class Hotel < ApplicationRecord
has_many :rooms, dependent: :destroy
accepts_nested_attributes_for :rooms
def reserved_rooms
rooms.reserved_count
end
def free_rooms
rooms.free_count
end
end
Your view will look finally like this:
<table>
<tr>
<th>Name</th>
<th>Rooms count</th>
<th>Rooms status: in reserved || free</th>
</tr>
<% #hotels.each do |hotel| %>
<tr>
<td><%= hotel.name %></td>
<td><%= hotel.rooms_count %></td>
<td><%= "#{hotel.reserved_rooms} || #{hotel.free_rooms}" %></td>
<td ><%= link_to 'Show', hotel_path(hotel) %></td>
<td><%= link_to 'Destroy', hotel, method: :delete, data: { confirm: 'Are you sure?' } %>
</tr>
<% end %>
</table>
Sorting the Hotels in your controller
In your controller make sure that you eager load Rooms for Hotel:
#hotels = Hotel.includes(:rooms).sort_by { |h| h.free_rooms.to_i }.reverse
You could eventually implement it as Hotel.includes(:rooms).sort_by(&:free_rooms).reverse.
In this way you won't need any join or helper.
Regarding your comment, free_rooms is implemented as an instance method (e.g. Hotel.first.free_rooms), so it will not be available for an ActiveRecord_Relation (e.g. Hotel.all.free_rooms)
i have this .html
<% #resources.each do |resource| %>
<tr>
<!-- -1 es para mostrar todos los recursos que faltan % -->
<% if (resource.curso_id = params[:id] or params[:id] ="-1") and resource.cantidad>0 %>
<td><%= resource.title %></td>
<td><%= #curso.nombre %></td>
<td><%= #user.name %></td>
<!-- %= image_tag(resource.imagen_url, :width => 190, :height => 190) if resource.imagen.present? % -->
<td><%= resource.cantidad %></td>
<td><%= link_to 'Show ', resource %></td>
and this controller:
def index
if params[:id] != "-1"
#resources = Resource.where(:curso_id => params[:id])
#curso = Curso.find(params[:id])
#user = User.find(#curso.user_id)
else
#resources = Resource.all
# HERE IS THE ERROR. I WANT TO GET THE COURSE BUT I WANT THE FIELD CURSO_ID THAT IS IN RESOURCE TABLE
#cursos = Cursos.find(#resource.curso_id)
#user = User.find(#curso.user_id)
end
end
the part that is above the if works ok. but the part below doesnt work. i want to get an attribute from resource in html and use in my controller. how could it be possible? thank you!
It seems like your model associations are not set up.
class Resource < ActiveRecord::Base
belongs_to :curso
end
class Curso < ActiveRecord::Base
has_many :resources
belongs_to :user
end
class User < ActiveRecord::Base
has_many :cursos
end
After that, you can simply access them in view template:
<% #resources.each do |resource| %>
<% resource.curso %>
<% resource.curso.user %>
<% end %>
Lastly, I'd like to add that using localized names for your model attributes is a real bad practice.
#resource is not defined. You have defined #resources = Resource.all(notice the s).
Plus Resource.all returns an array or objects of type Resource. Please explain what you're trying to achieve.
I have this in the index view:
<% #submissions.each do |submission| %>
<tr>
<td><%= submission.id %></td>
<td><%= User.find_by_id(submission.user_id).name.to_s %></td>
</tr>
<% end %>
I know I am not supposed to use find_by in the view.
How can I move this to the controller (or model)?
I tried to insert this in the index method of my submission controller and using the username variable but it doesn't work.
def index
#submissions = Submission.all
#submissions.each do |submission|
username = User.find_by_id(submission.user_id).name.to_s
end
end
Model, add relation to user
class Submission
belongs_to :user
end
Controller, eager load users to avoid N+1 queries.
def index
#submissions = Submission.includes(:user).all
end
View, just project the user from each submission
<% #submissions.each do |submission| %>
<tr>
<td><%= submission.id %></td>
<td><%= submission.user.name.to_s %></td>
</tr>
<% end %>
#in controller
def index
#submissions = Submission.all
end
#in view
<% #submissions.each do |submission| %>
<tr>
<td><%= submission.id %></td>
<td><%= submission.user.name %></td>
</tr>
<% end %>
This code implies you have declared the following relations:
Submission belongs_to :user
User has_many :submissions (or has_one)
You can use eager loading (uses less queries to the DB) to improve the previous code:
#submissions = Submission.includes(:user).all
You can use an ActiveRecord association:
class Submission
belongs_to :user
# etc
end
Then in your view:
<td><%= submission.id %></td>
<td><%= submission.user.first_name</td> <!-- or whatever -->
I have two controllers: tasks, tasksperson.
I have views/tasks/index.html.erb:
<table>
<% #tasks.group_by(&:name).each do |name, tasks| %>
<tr>
<td><%= name %></td>
<td><%= tasks.size %></td>
<td><%= tasks.select{ |task| task.done != true }.size %></td>
</tr>
<% end %>
</table>
I want to create a link in views/tasks/index.html to views/tasksperson/index.html.erb.I want also to send the name into 'index' in Tasksperson_controller.. I tried to do this by getting params[:name] but I think it's wrong
maybe, I need to do something like:
<td><%= link_to 'Show Tasks', tasksperson_path(name) %></td>
this is my tasksperson_controller:
class TaskspersonController < ApplicationController
def index
#tasks = Task.where(:name => params[:name]) respond_to do |format|
format.html # index.html.erb
format.json { render json: #tasks }
end
end
end
and views/tasksperson/index.html.erb:
<table>
<tr>
<th>Name</th>
<th>num of tasks</th>
<th>num tasks left</th>
<th>test</th>
</tr>
<% #tasks.each do |f| %>
<tr>
<td><%= f.name %></td>
<td><%= f.task %></td>
<td><%= f.done %></td>
</tr>
<% end %>
</table>
You need to add :name as a parameter to the rule that defines the route to TaskspersonController#index in routes.rb
so it would be something like this:
match 'tasksperson/index/:name' => 'tasksperson#index', as: :tasksperson_path
Based on your comment "...so Task have many taskpersons" I think you want a data model similar to below
class Task < ActiveRecord::Base
has_many :assigned_tasks
has_many :people, :through => :assigned_tasks
end
# maybe this is just the User class?
class Person < ActiveRecord::Base
has_many :assigned_tasks
has_many :tasks, :through => :assigned_tasks
end
# was TaskPerson
class AssignedTask < ActiveRecord::Base
belongs_to :task
belongs_to :person
end
See http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association for information about "The has_many :through Association"
task = Task.create(:title => "Go up the hill")
jack = Person.find(00000)
jill = Person.find(00000)
task.people << jack
task.people << jill
task.assigned_tasks.each do |join|
puts join.created_at
puts join.person.name
# 0 - jack
# 1 - jill
end
task.people.each do |person|
puts person.name
end
I am not exactly sure what you are trying to display in your views, it looks like you are grouping by a task name attribute in task/index, is that the Persons name?