Display Unique Results from Two Models - ruby-on-rails

I have two models: games and pickems.
Here is my schema for these models:
create_table "games", force: :cascade do |t|
t.integer "week_id", limit: 4
t.integer "home_team_id", limit: 4
t.integer "away_team_id", limit: 4
t.integer "home_score", limit: 4
t.integer "away_score", limit: 4
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "season_id", limit: 4
end
create_table "pickems", force: :cascade do |t|
t.integer "user_id", limit: 4
t.integer "game_id", limit: 4
t.integer "winner_id", limit: 4
t.integer "score", limit: 4
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Game model:
class Game < ActiveRecord::Base
belongs_to :home_team, class_name: 'Team'
belongs_to :away_team, class_name: 'Team'
belongs_to :week
belongs_to :season
end
Pickem model:
class Pickem < ActiveRecord::Base
has_one :user
has_one :game
has_one :winner, class_name: 'Team'
end
In my view, I want to display all Games that do not have a Pickem associated that is referencing it. I also want to display below all the Pickems and the game attributes associated. What do I need to be calling from the controller and/or add to the model(s) to display this information?

You will need to correct the Games/Pickem association, by adding this to the Game model:
has_one :pickem
and this to the Pickem model:
belongs_to :game
This query will retrieve all of the Games that do not have a Pickem associated:
#games_without_pickems = Game.joins(:pickem).group("games.id").having("COUNT('pickems.id') = 0").order("games.id")
change table references
This will retrieve all of the Pickems and associated Games information:
#pickems = Pickem.includes(:games).all
In your view, simply loop over both #games_without_pickems and #pickems, like this:
<table>
<th>
<td>Week</td>
<td>Home Team</td>
<td>Away Team</td>
<td>Home Score</td>
<td>Away Score</td>
<td>Season</td>
</th>
<% #games_without_pickems.each do |game| %>
<tr>
<td><%= game.week.name %></td>
<td><%= game.home_team.name %></td>
<td><%= game.away_team.name %></td>
<td><%= game.home_score %></td>
<td><%= game.away_score %></td>
<td><%= game.season.name %></td>
</tr>
<% end %>
</table>
<table>
<th>
<td>User</td>
<td>Winner</td>
<td>Score</td>
<td>Week</td>
<td>Home Team</td>
<td>Away Team</td>
<td>Home Score</td>
<td>Away Score</td>
<td>Season</td>
</th>
<% #pickems.each do |pickem| %>
<tr>
<td><%= pickem.user.name %></td>
<td><%= pickem.winner.name %></td>
<td><%= pickem.score %></td>
<td><%= pickem.game.week.name %></td>
<td><%= pickem.game.home_team.name %></td>
<td><%= pickem.game.away_team.name %></td>
<td><%= pickem.game.home_score %></td>
<td><%= pickem.game.away_score %></td>
<td><%= pickem.game.season.name %></td>
</tr>
<% end %>
</table>
That should do it.

First of all, you have
# in `pickems` table
t.integer "game_id", limit: 4
and
# in Pickem Model
class Pickem < ActiveRecord::Base
...
has_one :game
...
end
I think for has_one belongs_to, you need to put foreign_key in games table rather than pickems table and it will start making sense.
i.e.
create_table "games", force: :cascade do |t|
...
t.integer "pickem_id", limit: 4
...
end
class Game < ActiveRecord::Base
...
belongs_to :pickem
...
end
Now
I want to display all Games that do not have a Pickem associated that
is referencing it
Game.where(pickem_id: nil)
One Suggestion
Since your Game model belongs to multiple models like Pickem, Team, etc. so you can use Polymorphic Association instead.
Reason: If one of your game record belongs to pickem then other fields will be empty.

Related

Not able to display the user's name

I am working on a rails application, where I have two models, user and team. team should have a captain_id which is actually a user_id, implemented through a has one association. Now my problem is that when I am creating a team, I am not able to display the captain's name in the show page of the team.
This is my model definition:
class Team < ApplicationRecord
has_one :captain, class_name: 'User' , foreign_key: 'captain_id'
has_many :users, dependent: :destroy
validates :ground_name, presence:true
validates :team_name, presence:true
validates :captain_id, presence:true ,uniqueness: true
end
class User < ApplicationRecord
belongs_to :team,optional: true
end
This is my show view:
<table class="table table-striped">
<tr>
<th>Id</th>
<th>Team Name</th>
<th>Ground Name</th>
<th>Captain Name</th>
<th>Manage</th>
</tr>
<% #team.each do |f| %>
<tr>
<td><%= f.id %></td>
<td><%= f.team_name %></td>
<td><%= f.ground_name %></td>
<td><%= f.captain_id&.first_name %></td>
</tr>
<%end%>
</table>
This generates the error:
undefined method `first_name' for 2:Integer
This is my database schema:
ActiveRecord::Schema.define(version: 2019_02_19_090032) do
create_table "teams", force: :cascade do |t|
t.string "team_id"
t.string "team_name"
t.string "ground_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "captain_id"
end
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "photo"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "team_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["team_id"], name: "index_users_on_team_id"
end
end
The associated user should be available under the attribute captain. So your markup should look like this:
<td><%= f.captain&.first_name %></td>
Instead of :has_to, you should define a :belongs_to relation, so that the foreign key is defined in the team table:
belongs_to :captain, class_name: 'User', foreign_key: :captain_id
If you do not have the captain_id column defined in your teams table, you have to create a migration and run it.
To create the migration:
rails g migration add_captain_id_to_teams
Now you have a new migration file in db/migrate, something like db/migrate/<timestamp>_add_captain_id_to_teams.rb
Open this file and edit it to look like this:
class AddCaptainIdToTeams < ActiveRecord::Migration[5.0]
def change
add_column :teams, :captain_id, :integer
end
end
Now run this command to create the column:
bundle exec rake db:migrate
Now you can assign the captain id and save it in the database.
Following can solve issue,
<td><%= f.captain&.first_name %></td>
but following can also you can use,
<td><%= f.captain.first_name %></td>
No need to add optional: true, remove it & remove following from team.rb
validates :captain_id, presence:true ,uniqueness: true
because we removed optional: true
In user.rb, correction is needed,
belongs_to :team, optional: true, foreign_key: :captain_id

Related reference value not showing up on list

I have been struggling with this problem for much too long and am hoping that one of you will be able to see what I cannot. It has to be a simple stupid error since I do this everywhere in the application with no errors.
Problem: I have a billing_type (reference) table which contains a number of records. I also have a billing table which contains bills. When I display the billing table in a list, the billing_type is show as:
"#<BillingType:0x00000006f49470>"
which I am assuming is its pointer.
The billing_type model :
class BillingType < ActiveRecord::Base
validates :billing_type, presence: true, uniqueness: true
has_many :billings
accepts_nested_attributes_for :billings
end
The billing model :
class Billing < ActiveRecord::Base
belongs_to :billing_type
belongs_to :horse
validates :billing_type_id, presence: true
validates :horse_id, presence: true
end
The schema:
create_table "billing_types", force: :cascade do |t|
t.string "billing_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "billing_types", ["billing_type"], name: "index_billing_types_on_billing_type", unique: true
create_table "billings", force: :cascade do |t|
t.date "billing_date"
t.integer "billing_type_id"
t.integer "horse_id"
t.string "notes"
t.decimal "cost"
t.date "date_billed"
t.date "date_received"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The query in the controller (note: I exported the database and put this in the SQL and it returned everything, including the billing_type, o.k.):
def index
#billings = Billing.find_by_sql("SELECT billings.id, billings.billing_date, horses.horse_name, billings.billing_type_id, billing_types.billing_type, billings.notes, billings.cost, billings.date_billed, billings.date_received FROM billings JOIN horses ON billings.horse_id = horses.id JOIN billing_types ON billings.billing_type_id = billing_types.id ORDER BY billings.billing_date, LOWER(horses.horse_name)")
end
The index page:
.
.
.
<div class = "table-container">
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>Billing date</th>
<th>Horse name</th>
<th>Billing type</th>
.
.
.
</tr>
</thead>
<tbody>
<% #billings.each do |billing| %>
<tr>
<% if billing.billing_date %>
<td><%= billing.billing_date.strftime("%d/%m/%Y") %></td>
<% else %>
<td><%= billing.billing_date %></td>
<% end %>
<td><%= billing.horse_name %></td>
<td><%= billing.billing_type %></td>
.
.
.
</tr>
<% end %>
</tbody>
</table>
Thanking you in advance for any help you can give!
That's exactly what I would expect to see
billing.billing_type is the representation of the the whole BillingType class as an object.
If you wanted that then maybe
billing.billing_type.inspect is what you are expecting but I suspect that what you really want is the name in which case you should be looking to display a property of the object not the object itself. i.e.
billing.billing_type.name

Rails ActiveRecord belongs_to association not loading

I am trying to render a list of Active Records as follows:
<% #workout_sets.each do |workout_set| %>
<tr>
<td><%= workout_set.reps %></td>
<td><%= workout_set.exercise.name %></td>
<td><%= link_to 'Show', workout_set %></td>
<td><%= link_to 'Edit', edit_workout_set_path(workout_set) %></td>
<td><%= link_to 'Destroy', workout_set, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
My AR setup looks like:
class WorkoutSet < ActiveRecord::Base
belongs_to :workout
belongs_to :exercise, class_name: 'Exercise', foreign_key: 'exercises_id'
end
class Exercise < ActiveRecord::Base
end
class Workout < ActiveRecord::Base
has_many :workout_set
end
and my schema is
create_table "exercises", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "exercises", ["name"], name: "index_exercises_on_name", unique: true
create_table "workout_sets", force: :cascade do |t|
t.integer "reps", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "exercises_id"
t.integer "workouts_id"
end
add_index "workout_sets", ["exercises_id"], name: "index_workout_sets_on_exercises_id"
add_index "workout_sets", ["workouts_id"], name: "index_workout_sets_on_workouts_id"
create_table "workouts", force: :cascade do |t|
t.string "location", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
In attempting to render the page I get the following error
undefined method `name' for nil:NilClass
When I change the path in my template to <%= workout_set.exercise %> it renders each row like 444 #<Exercise:0x007fbde9dde998> Show Edit Destroy which is what I expect.
Why is the the attempted access of the name property causing this error?
One of your WorkoutSet does not have an associated Exercise. You can enforce that a WorkoutSet has an exercise Exercise in your WorkoutSet model but there are implications to that. Mainly, you could not create a WorkoutSet without first creating the Exercise. If that's what you want then add the following to the WorkoutSet model.
validates_presence_of :exercise_id
More likely though, you just want to handle the page crashing when there is no associated Exercise.
<td><%= workout_set.exercise.name unless workout_set.exercise.blank? %></td>
That will give you a blank cell but you can do something like this to have a placeholder.
<td><%= workout_set.exercise.blank? ? "No exercise for this set" : workout_set.exercise.name %></td>
You haven't set up the relationship in the Exercise model
class Exercise < ActiveRecord::Base
has_many :workout_sets
has_many :workouts, through: :workouts_sets #not needed but good to setup
end
or if you're trying to do a 1-to-1 relationship between Exercise and WorkoutSet
class Exercise < ActiveRecord::Base
has_one :workout_set
end
Also having an 's' at the end of the foreign keys in your workout_sets table (i.e. 'workouts_id') is somewhat bad form. I'm pretty sure Rails will be smart enough to make it work but if you run into more bugs I'd try changing those to 'workout_id' and 'exercise_id'.

Adding Tags in Rails

Adding Tags and I Get this ERROR:
SQLite3::SQLException: no such column: taggings.available_work_id:
SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" =
"taggings"."tag_id" WHERE "taggings"."available_work_id" = ?
Available_work.index.html
<tbody>
<% #available_works.each do |available_work| %>
<tr>
<td><%= available_work.title %></td>
<td><%= available_work.description.html_safe %></td>
<td>Tags: <%= raw available_work.tags.map(&:name).map { |t| link_to t, tag_path(t) }.join(', ') %></td>
<td><%= image_tag available_work.image_url(:thumb) %></td>
<td><%= link_to 'Show', available_work %></td>
<td><%= link_to 'Edit', edit_available_work_path(available_work) %></td>
<td><%= link_to 'Destroy', available_work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
Available_work.rb
class AvailableWork < ActiveRecord::Base
attr_accessor :image, :remote_image_url, :tag_list
mount_uploader :image, ImageUploader
has_many :taggings
has_many :tags, through: :taggings
def self.tagged_with(name)
Tag.find_by_name!(name).available_work
end
def self.tag_counts
Tag.select("tags.*, count(taggings.tag_id) as count").
joins(:taggings).group("taggings.tag_id")
end
def tag_list
tags.map(&:name).join(", ")
end
def tag_list=(names)
self.tags = names.split(",").map do |n|
Tag.where(name: n.strip).first_or_create!
end
end
end
Tag.rb
class Tag < ActiveRecord::Base
has_many :taggings
has_many :available_work, through: :taggings
end
Taggings.rb
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :availble_work
end
Schema
create_table "available_works", force: :cascade do |t|
t.string "title"
t.string "description"
t.string "tags"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "taggings"
end
You would have to add an available_work_id column the to taggings table. It might be better however to create a polymorphic relation to the tagged object though so that you can use tags for more than the AvailableWork model.
EDITED. There where several mistakes in my example. Here is a complete running version:
class Tag < ActiveRecord::Base
has_many :taggings
def self.tag_counts
select("tags.*, count(taggings.tag_id) as count")
.joins(:taggings)
.group("taggings.tag_id")
end
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :tagged,
polymorphic: :true,
inverse_of: :taggings
end
class AvailableWork < ActiveRecord::Base
has_many :taggings, as: :tagged
has_many :tags, through: :taggings
def self.tagged_with(name)
# We have to do this in two queries since Rails does not
# do joins on polymorphic relations.
ids = Tagging.where(tagged_type: self.name)
.joins(:tag)
.where(tags: { name: name }).pluck(:tagged_id)
find(ids)
end
def self.tag_counts
Tag.tag_counts.where(taggings: { tagged_type: self.name })
end
def tag_list
tags.map(&:name).join(", ")
end
def tag_list=(names)
self.tags = names.split(",").map do |n|
Tag.where(name: n.strip).first_or_create!
end
end
end
ActiveRecord::Schema.define(version: 20150621234032) do
create_table "available_works", force: :cascade do |t|
t.string "title"
t.string "description"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "tagged_id"
t.string "tagged_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id"
add_index "taggings", ["tagged_id"], name: "index_taggings_on_tagged_id"
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
<table>
<tbody>
<% #available_works.each do |available_work| %>
<tr>
<td><%= available_work.title %></td>
<td><%= available_work.description.try(:html_safe) %></td>
<td>Tags: <%= available_work.tag_list %></td>
<td><%#= image_tag available_work.image_url(:thumb) %></td>
<td><%= link_to 'Show', available_work %></td>
<td><%= link_to 'Edit', edit_available_work_path(available_work) %></td>
<td><%= link_to 'Destroy', available_work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
Note that I commented out the images part to save time since it is not directly relevant to the question.
The Rails app I created to answer the question is available at https://github.com/maxcal/playground/tree/adding-tags-in-rails.

Rails, has_many, belongs_to

Schema:
create_table "reports", :force => true do |t|
t.integer "user_id"
t.string "apparatus"
t.string "capt"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "login", :limit => 40
t.string "name", :limit => 100, :default => ""
t.string "email", :limit => 100
t.string "crypted_password", :limit => 40
t.string "salt", :limit => 40
t.datetime "created_at"
t.datetime "updated_at"
t.string "remember_token", :limit => 40
t.datetime "remember_token_expires_at"
t.string "rank"
t.integer "shift"
t.integer "access"
end
user model:
Class User < ActiveRecord::Base
has_many :reports
# bunch of other stuff thats not important
end
report model:
Class Report < ActiveRecord::Base
belongs_to :user
end
views/reports/index
<% #reports.each do |report| %>
<tr>
<td><%= report.user_id %></td> # ****THIS IS THE LINE IN QUESTION****
<td><%= report.apparatus %></td>
<td><%= report.capt %></td>
<td><%= report.body %></td>
<td><%= link_to 'Show', report %></td>
<td><%= link_to 'Edit', edit_report_path(report) %></td>
<td><%= link_to 'Destroy', report, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
I would like to be able to display the name of the user that created the report. I was under the assumption that declaring the belongs_to and has_many associations would make this possible by writing report.user.name or something like that. Where have I gone wrong?
I'm 99% sure it's because one or more of your reports do not have an associated user. Try
<%= report.user.name rescue "none" %>
When there is no value in user_id field on a report then report.user will return nil. So report.user.name would be like calling nil.name, which raises an error.
UPDATE: here's a better way:
<%= report.user.try(:name) %>
You can do:
<%= report.user.name %>
But for efficiency, in your controller you can do a join to get the users name in the same query used to fetch #reports.
This query might look something like:
#reports = Report.select("reports.id as id, reports.apparatus as apparatus, reports.capt as capt, reports.body as body, users.name as user_name").joins("LEFT JOIN `users` ON `users`.`id` = `reports`.`user_id`")
Then your output would look like:
<%= report.user_name %>

Resources