HABTM messing me up - Rails 3 - ruby-on-rails

I have a simple HABM relationship set up between users and children.
If a child belongs to you, I want you to see the child AND the name of other users that this child object belongs to. For some reason I cannot print the name of the other users the child belongs to.
I select as such:
#children = Child.all(:include => :users, :conditions => ["users.id = ? AND enrolled = ?", current_user.id, true])
and try to print as such:
<% for user in child.users%>
<% if can? :manage, Child %>
<a rel='tipsy'><%= link_to '[#{user.first_and_last_name}]', edit_child_path(child), :title => "#{user.updateChoice}"%></a>
<%else%>
<a rel='tipsy'><%= link_to '[#{user.first_and_last_name}]', new_parentnote_path, :title => "#{user.updateChoice}"%></a>
<%end%>
<%end%>
It selects the right children but DOES NOT print the other users' name. Only the current user's name.
If I select all children Child.all everything works as expected. All names get printed which tells me it is not my authentication system doing something fishy but something else...probably the way I select the children although I tried several ways =(
I am not sure if I am missing something really obvious but this has puzzled me for hours.
Any help is greatly appreciated. Thanks.
EDIT - Add Schema
As requested bellow here is the schema. The relationship is set up fine as far as I can tell. It is just getting multiple users based on current_user that is not happening as explained above. If I use the query above even though the child has multiple users (I see that by doing a child.count) it will only print the current user's name for some reason it is not fetching the other users name that the child belongs to as well.
create_table "children_users", :id => false, :force => true do |t|
t.integer "child_id"
t.integer "user_id"
end
Then I have children.
create_table "children", :force => true do |t|
t.string "first_name"
t.string "last_name"
t.boolean "enrolled", :default => true
t.datetime "unenrolled_datetime"
end
and users
create_table "users", :force => true do |t|
t.string "email"
t.string "encrypted_password"
t.string "first_name"
t.string "last_name"
........ other stuff to reset password etc
end

You're trying to get the children of a user, and at the same time, get all the other parents?
An easy way to list the children of a user and display the parents of each children would be:
#children = current_user.children
<%= #children.each do |child| %>
<h2><%= child.first_name %></h2>
<%= #child.parents.each do |parent| %>
<p><%= parent.first_name %></p>
<% end %>
<% end %>
ps:
in Children model
has_many :children_users
has_many :parents, :through => :children_users, :class_name => "User", :source => :user

I belive it's the condition "users.id = ? ", current_user.id that makes loading only the children of the current_user, and subsequently when you iterate over those the only user there, is current user.
What you probbly want is select based on children.user_id:
#children = Child.where(user_id: current_user.id, enrolled: true).includes(:users)

Related

Filter with association : SQL error no such column

I m trying to implant filter after query. I have 2 models with association 1) Camping 2)Caracteristiquetest. Modele Caracteristiquetest have camping_id. Query works but when i m trying to apply filter with checkbox i have an error. I think it's an association problem.
How I can fix that ? Thanks for your help.
SQLite3::SQLException: no such column: handicap: SELECT "campings".*
FROM "campings" WHERE (piscine LIKE '%%' AND barbecue LIKE '%%' AND
nomdep LIKE '%aube%' AND handicap LIKE '%oui%')
MODEL
Camping.rb
scope :handicap, -> (handicap) { where handicap: "oui" }
belongs_to :caracteristiquetest
def self.search(piscine, barbecue, nomdep, handicap)
return scoped unless piscine.present? || barbecue.present? || nomdep.present? || handicap.present?
where(['piscine LIKE ? AND barbecue LIKE ? AND nomdep LIKE ? AND handicap LIKE ?', "%#{piscine}%", "%#{barbecue}%", "%#{nomdep}%", "%#{handicap}%"])
end
Caracteristiquetst.rb
has_one :camping
CONTROLLER
camping_controller.rb
def resultnohome
if params[:piscine] && params[:barbecue] && params[:nomdep] && params[:handicap].blank?
redirect_to action: :index and return
else
#campings = Camping.search(params[:piscine], params[:barbecue], params[:nomdep], params[:handicap])
end
end
VIEW
_searchfilter.html.erb
<%= text_field_tag :nomdep, params[:nomdep], class:"SearchFilter", placeholder:"Ex : Vendée, Corse..." %>
<li><p><span class="IcoSwim" aria-hidden="true"></span> Piscine : <%= check_box_tag :piscine, "oui", !!params[:piscine], :class => "piscine" %></p></li>
<li><p><span class="IcoSlide" aria-hidden="true"></span>Toboggan aquatique </p></li>
<li><p><span class="glyphicon glyphicon-fire" aria-hidden="true"></span> Barbecue : <%= check_box_tag :barbecue, "oui", !!params[:barbecue], :class => "barbecue" %></p></li>
<li><p><span class="IcoHandi" aria-hidden="true"></span> Accès handicapé : <%= check_box_tag :handicap, "oui", !!params[:handicap], :class => "handicap" %></p></li>
<%= submit_tag "Appliquer les filtres", class:"btn btn-danger2", name: nil %>
EDIT
Some precisions : Filter by piscine or barbecue works because they are on same migration, same model but filtring by handicap doesnt work.
Structure of camping table
class CreateCampings < ActiveRecord::Migration[5.0]
def change
create_table :campings do |t|
t.string :name
t.string :adresse
t.string :code_postale
t.string :commune
t.string :courriel
t.string :site_internet
t.string :tel
t.text :description
t.string :nomdep
t.string :nomregion
t.string :numdep
t.string :slug
t.integer :ville_id
t.integer :region_id
t.integer :departement_id
t.float :latitude
t.float :longitude
t.string :etoile
t.string :piscine
t.string :barbecue
t.integer :user_id
t.integer :caracteristiquetest_id
t.timestamps
end
end
end
Table structure of caracteristiquetest
class CreateCaracteristiquetests < ActiveRecord::Migration[5.0]
def change
create_table :caracteristiquetests do |t|
t.string :handicap
t.integer :camping_id
t.timestamps
end
end
end
EDIT2
After some searches on google I think .joins to join two tables can be the solution. So I made this on my camping.rb
def self.searchi(nomdep, handicap)
return scoped unless nomdep.present?
joins(:caracteristiquetest).where(['nomdep LIKE ? OR name LIKE ? AND handicap LIKE ?', "%#{nomdep}%", "%#{nomdep}%", "%#{handicap}%"])
end
But, now i have another error...
SQLite3::SQLException: no such column: campings.camping_id: SELECT
"campings".* FROM "campings" INNER JOIN "caracteristiquetests" ON
"caracteristiquetests"."id" = "campings"."camping_id" WHERE (nomdep
LIKE '%camping%' OR name LIKE '%camping%' AND handicap LIKE '%%')
What is the method to fix that ? Thanks for your help.
I don't see a handicap field in your table structure. So the error message is self-explanatory.
You are trying to get result from camping and handicap is a column of caracteristiquetests .
Add a field 'handicap' IN camping .
rails g migration addHandicapToCamping handicap
I found the solution, problem was on association.
Camping.rb
has_many :caracteristiquetests
Caracteristiquetest.rb
belongs_to :camping
After i add this on my model and i can filter with params from another table. left_outer_joins(:caracteristiquetests).where(mywhere)

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.

How do I show Users that are "Pending" to join Group?

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>

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

Polymorphic Associations in Rails for different 'author' models

How would a Polymorphic Association (here: Comments) be itself associated with different types of Authors?
Starting from …
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => :true
end
… I'd need to have one author being from model Hosts, ID 5 and another one from Users, 2.
How could path helpers look like…
<%= link_to comment.author.name, user_path(comment.author) %>
… when "user_path" or "host_path" are dynamic, depending on the author model?
EDIT***
There are Events, Places etc. that can have comments like so:
has_many :comments, :as => :commentable
To the polymorphic Comment model i would like to add IDs and Types to refer to Authors of comments:
create_table "comments", :force => true do |t|
t.text "content"
t.integer "commentable_id"
t.string "commentable_type"
t.integer "author_id"
t.string "author_type"
end
An Events page displays comments, and clicking on an author name should take me either to User(5) oder AnotherModel(2), depending on who wrote the comment.
I'd like to know how everybody handles this kind of situation. Should I think about adding a second polymorphic "middle layer", such as "profile", that could hold the subclasses "User", "Host" and so forth?
EDIT 2
Having only one User model would make life easier here obviously, but that cannot be done for other reasons. And in general i'm interested how this could be organized well.
Simply putting
<%= link_to comment.author.name, comment.author %>
should do the trick. If you want more flexibility, I would suggest
link_to ..., action_in_host_path(comment.author) if comment.author.kind_of? Host
This is what I've used in the past (in a view helper), but it uses an eval:
def model_path(model, options = {})
{:format => nil}.merge(options)
format = (options[:format].nil? ? 'nil' : "'#{options[:format].to_s}'")
eval("#{model.class.to_s.downcase}_path(#{model.id}, :format => #{format})")
end
Use like this:
<%= link_to comment.author.name, model_path(comment.author) %>
Would polymorphic_url help?
<%= link_to comment.author.name, polymorphic_url(comment.commentable)
%>

Resources