How do I show Users that are "Pending" to join Group? - ruby-on-rails

What I want to do is show the Users that are "pending" to join a Group. I have 3 models: Users, Membership, & Groups. I have enabled Users to be able to request to join a Group. When the request is made there is a attribute in the Membership model called "state" which I made the default value to be "pending". Once the Group Admin clicks accept then the User's state is changed to "active". How do I show only the Users that are in the "pending" state?
I am able to show all Users (both "pending" & "active") who request to join the Group by adding code "#members = #group.users" in the Group controller. When I wanted to show only the "pending" Users I created a method in model Group called "pending_members". This didn't work.
Currently I get an error message that says: "ActionView::MissingTemplate in Groups#show". The error message also highlights the code "<%= render #members %>". [When I use the code #members = #group.users then the webpage does show both "pending" & "active" Users]
Here are each of the Model attributes:
create_table "groups", force: :cascade do |t|
t.string "name"
end
create_table "memberships", force: :cascade do |t|
t.integer "user_id"
t.integer "group_id"
t.string "state", default: "pending"
end
create_table "users", force: :cascade do |t|
t.string "email"
t.string "first_name"
t.string "last_name"
end
Here is the code in each respective model:
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships, dependent: :destroy
has_many :groups, through: :memberships
end
class Group < ActiveRecord::Base
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships
end
Here is the code in the controller for Groups:
def show
#group = Group.find(params[:id])
#members = #group.pending_members
end
Here is the code in the model for Groups:
def pending_members
Membership.where(state: "pending")
end
Here is the code in the view for Groups/show.html.erb:
<div class="container">
<div class="row">
<%= render #members %>
</div>
</div>

If you want to show users in the pending state, then you pending_members method should return a collection of users. As it is it returns a collection of memberships.
You could write it like this:
def pending_members
User.includes(:memberships).where("memberships.state" => "pending)
end
then for your view to work you could put this in
<%= render partial: 'member', collection: #group.pending_members %>
Then you need to have in the app/views/groups directory the partial _member.html.erb which might look something like this:
<div id="user-<%= member.id %>">
<p>name: <%= member.first_name %> <%= member.last_name %></p>
<p>email: <%= member.email %></p>
</div>

Related

How do I submit event on f.collection_select, and change the content displayed, dependent on what is selected in the dropdown?

I am creating a webshop with products. I sell digital game accounts.
I have invented a dropdown menu, with f.collection_select, for the user to choose which server the account must belong to.
If the user selects a server, lets say "North America", I want the page to show the accounts which I have that belong to the server "North America". I have set up two models, the server model, this model just contains name:string, and the model has_many :accounts.
Next model I have is account.rb. This model stores all information in regards to account, and belongs_to :server.
I use this form for selection of server, in my views/accounts/index.html.erb
I can see that it works, and shows my servers, which I have created in the database.
<%= form_with do |f| %>
<%= f.collection_select(:server_ids, Server.all, :id, :name, remote: true) %>
<% end %>
class CreateAccounts < ActiveRecord::Migration[6.0]
def change
create_table :accounts do |t|
t.string :name
t.string :password
t.string :BE
t.integer :level
t.integer :price
t.references :server, null: false, foreign_key: true
t.timestamps
end
end
end
class CreateServers < ActiveRecord::Migration[6.0]
def change
create_table :servers do |t|
t.string :name
t.timestamps
end
end
end
This is how the site looks atm with the dropdown:
So here I have selected EUW server. When I change it to NA, I want the page to display the accounts I have in the association Server.find(name:"Europe West (EUW)".accounts
Here is a video, that shows what I am going for:
https://www.youtube.com/watch?v=bZUxS5oMMOw

Rails 5.0.2 - unable to get data from many-many relationship models

I am stuck at getting data in development.sqlite3 from 3 tables that have many-to-many relationship. I can get the courses that belong to a given location or category but it does not work the way around.
I followed the way I was taught in my class but I think the models might have missed some constraints.
Anyone please takes a look and gives me some suggestion. Thank you so much!
This is my schema.rb:
create_table "categories", force: :cascade do |t|
t.string "title"
end
create_table "categories_courses", force: :cascade do |t|
t.integer "course_id", null: false
t.integer "category_id", null: false
end
create_table "courses", force: :cascade do |t|
t.string "title"
end
create_table "courses_locations", force: :cascade do |t|
t.integer "course_id", null: false
t.integer "location_id", null: false
end
create_table "locations", force: :cascade do |t|
t.string "address"
end
I want to get the locations or the categories that associate with a given course. Then I added the relationships in the models like below:
# Category model
class Category < ApplicationRecord
has_and_belongs_to_many :courses, join_table: 'categories_courses'
end
# Location model
class Location < ApplicationRecord
has_and_belongs_to_many :courses, join_table: 'courses_locations'
end
# Course model
class Course < ApplicationRecord
has_and_belongs_to_many :locations, join_table: 'courses_locations'
has_and_belongs_to_many :categories, join_table: 'categories_courses'
end
Then in courses_controller.rb, I get the locations and categories like this:
class CoursesController < ApplicationController
before_action :set_course, only: [:show, :edit, :update, :destroy]
def categories
#course = Course.find params[:id]
#categories = #course.categories
end
def locations
#course = Course.find params[:id]
#locations = #course.locations
end
private
def set_course
#course = Course.find(params[:id])
end
end
Finally I display data in html.erb file like this:
<% course.categories.each {|category| puts category.title + ' '} %>
<% course.locations.each {|location| puts location.address + ' '} %>
I am so sorry for putting lots of codes here, but I should provide enough information to examine the code.
Update:
I have this in route.rb
Rails.application.routes.draw do
resources :locations
resources :categories
resources :courses
end
I have edited the code as everyone's suggestions but it still prints nothing. I notice the query is correct but there is a warning in rails s console:
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 35], ["LIMIT", 1]]
DEPRECATION WARNING: Passing an argument to force an association to reload is now deprecated and will be removed in Rails 5.1. Please call `reload` on the result collection proxy instead. (called from block in _app_views_courses_index_html_erb___1034719630_104325280 at c:/Users/lovea/RubymineProjects/YourCourse/app/views/courses/index.html.erb:41)
Category Load (0.0ms) SELECT "categories".* FROM "categories" INNER JOIN "categories_courses" ON "categories"."id" = "categories_courses"."category_id" WHERE "categories_courses"."course_id" = ? [["course_id", 4]]
Not sure, but you probably need to add the option join_table
# Category model
class Category < ApplicationRecord
has_and_belongs_to_many :courses, join_table: "categories_courses"
end
# Location model
class Location < ApplicationRecord
has_and_belongs_to_many :courses, join_table: "courses_locations"
end
# Course model
class Course < ApplicationRecord
has_and_belongs_to_many :locations, join_table: "courses_locations"
has_and_belongs_to_many :categories, join_table: "categories_courses"
end
I think the associations should be working. I have tried it myself and got it working in the console. It might be that the issue you are having with routing perhaps.
Also, in the view, instead of:
<% course.categories(course.id).each {|category| puts category.title + ' '} %>
<% course.locations(course.id).each {|location| puts location.address + ' '} %>
You could:
<% course.categories.each {|category| puts category.title + ' '} %>
<% course.locations.each {|location| puts location.address + ' '} %>
Because the association is already there without the need to pass the id.
you can easily create a view for many to many with collecion checkboxes
<%= f.collection_check_boxes(:course_id, Course.all, :id, :title) do |b| %>
<div class="collection-check-box">
<%= b.check_box %>
<%= b.label %>
<%end%>
<%= f.submit 'add course to location', class: "btn btn-primary" %>
<%end%>

Limit association based on rich relation attributes

first question for me here! Im trying to assign 'key companies' to my users. These are found in a many-to-many rich join table. On this table there are attributes like new, key, active and so forth. I want to assign companies to users in a long list and for that Im using SimpleForm.
Everything is working excepts that I want to filter out and limit the association relation based on the attributes on the rich relation. I have company relations for each user but not all of them are akey-relation or a new-relation for example. I only want the association being key to show up and not touch the other ones. I also want to set the attribute active to true when Im assigning these companies to the users. My code looks like this now:
user.rb
class User < ActiveRecord::Base
has_many :company_user_relationships
has_many :companies, through: :company_user_relationships
company.rb
class Company < ActiveRecord::Base
has_many :company_user_relationships
has_many :users, through: :company_user_relationships
schema.rb
create_table "company_user_relationships", force: true do |t|
t.integer "company_id"
t.integer "user_id"
t.boolean "key"
t.boolean "active"
t.datetime "last_contacted"
t.string "status_comment"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "status"
t.boolean "new"
end
users_controller.rb
def assign_key_companies
User.update(params[:users].keys, params[:users].values)
redirect_to(:back)
end
view
= form_for :user_companies,
url: assign_key_companies_users_path,
html: {:method => :put} do |f|
- users.each do |user|
= simple_fields_for "users[]", user do |u|
tr
td.col-md-4
= "#{user.first_name} #{user.last_name}"
td.col-md-8
= u.association :companies, label: false, collection: #key_company_candidates,
input_html: {data: {placeholder: " Assign key companies"}, class: 'chosen-select'}
= submit_tag "Save key companies", class: "btn btn-success pull-right"
I basically want to only show user.companies.where(key: true) and the SQLCOMMIT to always put the key-field to true when updating the record.
How can i filter out to only affect the associations I want?
I can think of two ways.
First to filter it at the association level
class User < ActiveRecord::Base
has_many :company_user_relationships, -> { where("company_user_relationships.key" => true) }
Or a where
user.companies.where("company_user_relationships.key" => true)
When you call user.companies it actually doing the join table among all three tables, so you could specify the condition like my example.

Rails4: How to display the data in many has_many models

I'd like to develop an app for schedule.
Each user create their own schedule.
I'd like to display the data as followings;
schedule title (user name)
day1(mm/dd,yyyy)
09:00 Math
11:00 Science
Room name A
day2(mm/dd,yyyy)
10:00 Physics
13:00 Music
Room name B
Although I can display the schedule title and user name, I couldn't display the date(mm/dd, yyyy) and room name. (I haven't make a model for course name and time)
It would be appreciated if you could give me any suggestion.
user.rb
class User < ActiveRecord::Base
has_many :schedlues
...
schedule.rb
class Schedule < ActiveRecord::Base
belongs_to :user
has_many :rooms
...
room.rb
class Room < ActiveRecord::Base
belongs_to :schedlue
schema
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
...
end
create_table "schedules", force: :cascade do |t|
t.string "title"
t.integer "user_id"
...
end
create_table "rooms", force: :cascade do |t|
t.date "date"
t.string "room_name"
t.integer "schedule_id"
...
end
I haven't made a model for course name, time yet.
users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#schedules = #user.schedules.paginate(page: params[:page])
end
...
show.html.erb
<% if #user.schedules.any? %>
<ol class="schedules">
<%= render #schedules %>
</ol>
<%= will_paginate #schedules %>
<% end %>
_schedule.html.erb
<li>
<span class="content"><%= schedule.title %>(<%= schedule.user.name %>)</span>
#Although I tried to display date and room name here, I couldn't.
</li>
You need to use has_many + through relationship over here.
See, a user has_many schedules, and one schedule has many rooms. So indirectly a user has_many rooms as well. You gotta write the following line of code in User model:
class User < ActiveRecord::Base
has_many :schedules
has_many :rooms, through: :schedules
end
Now, you can directly call rooms on a user object like following:
user = User.last
user.rooms.each do |room|
puts room.room_name # calling it simply `name` would be more appropriate.
end

Count the amount of records are associated to another record

In my Ruby on Rails application I am creating a cinema system, and on the bookings/new page I am allowing the user to choose the amount of seats they require through a drop down menu. But what I want to do is display the number of seats that are currently free in the screen, for example if a screen has 50 seats and 7 have been booked I want the system to display: "There are 43 seats available." I know I will need a method for this but am unsure about how I would implement it and how I would show this message.
It is worth noting that a seat would only be booked for one showing, so it would be free for others, which means that the method would have to be able to count the amount of seats available for that showing.
Can someone please help.
bookings/form.html.erb:
<%= form_for #booking do |f| %>
<%= f.hidden_field :user_id %>
<%= f.hidden_field :showing_id %>
<%= image_tag "thor_hammer.jpg",:size => "900x250" %>
<h1>NEW BOOKING:</h1>
<tr>
<td width="350px">
<br><%= f.label :seats_quantity, 'Please Select The Amount of Seats Required:' %>
</td>
<td width="300px">
<br><%= f.select :seats_quantity, '1'..'10' %><br>
</td>
<td width="300px">
<div class="actions">
<br><%= f.submit 'Book Showing' %>
</div>
<br><%= render "/error_messages", :message_header => "Cannot save: ", :target => #booking %>
</td>
</tr>
<% end %>
Screen.rb:
class Screen < ActiveRecord::Base
has_many :seats
has_many :showings
def screens_info
"#{name}"
end
end
Seat.rb:
class Seat < ActiveRecord::Base
belongs_to :screen
end
Booking.rb:
class Booking < ActiveRecord::Base
belongs_to :user
belongs_to :showing
end
Showing.rb:
class Showing < ActiveRecord::Base
belongs_to :film
has_many :bookings
belongs_to :screen
end
Schema:
create_table "bookings", force: :cascade do |t|
t.integer "user_id"
t.integer "showing_id"
t.integer "seats_quantity"
end
create_table "screens", force: :cascade do |t|
t.string "name"
end
create_table "showings", force: :cascade do |t|
t.date "show_date"
t.time "show_time"
t.integer "film_id"
t.integer "screen_id"
end
create_table "seats", force: :cascade do |t|
t.string "row_letter"
t.integer "row_number"
t.integer "screen_id"
end
It is worth noting that whilst the seats table contains the attributes row_letter and row_number a user IS NOT booking a specific seat, just the quantity of seats they require.
In your Screen class add:
has_many :bookings, through: :showings
And then your code becomes something like:
def remaining_seats
seats.count - bookings.sum(:seats_quantity) # <-- edited when I realized there was a quantity in a booking
end
def screens_info
"#{name} (#{remaining_seats}/#{seats.count} remaining)"
end
You need to figure out two values: the total seats for a specific showing and how many of those seats are already booked. Supposing you have two variables called scr_id and shw_id where the first represents the Screen Id and the second the Showing Id.
Total seats:
total_seats = Seat.where(screen_id: scr_id).count
Total bookings:
total_bookings = Booking.where(showing_id: shw_id).count
And then you only need to compute the differente between both.
available_seats = total_seats - total_bookings
EDIT: SPECIFIC IMPLEMENTATION
It should be implemented as a method in the screen model:
def available_seatings(shw_id)
total_seats = Seat.where(screen_id: this.id).count
total_bookings = Booking.where(showing_id: shw_id).count
return total_seats - total_bookings
end
Then in the controller
#available_seatings = screen.available_seatings(shw_id)
You can then use the #available_seatings variable in the view
EDIT
The manual way of doing this would be like so:
class Showing < ActiveRecord::Base
def booked_seats
bookings.pluck(:seats_quantity).sum
end
def available_seats
seats.count - booked_seats
end
end
OLD
This looks like a good use of Rails' counter_cache.
class Booking < ActiveRecord::Base
belongs_to :showing, counter_cache: true
end
This will store the count in a column on the Showing model (which you have to add). Then when you do #showing.bookings.size (not count), it will refer to that column.
"With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method." http://guides.rubyonrails.org/association_basics.html

Resources