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
Related
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
I'm creating a new .html.erb file where I'm attempting to list all of the teams and their opponents. I have 2 tables: a users (team) table and a records table. The records table has 2 columns (user_id, opponent_user_id) referencing the same users table.
My view file:
<% #record.each do |record| %>
<tr>
<td><%= record.user.name %></td>
<td><%= record.opponent_user_id %></td>
</tr>
<% end %>
Rather than using record.user_id, I was able to replace the id with the name of the user by using record.user.name. I am unable to do the same with the opponent team however. I tried using
<td><%= record.opponent_user_id.user.name %></td>
but that didn't work. How can I replace the opponent_user_id with the actual name?
Additional info below
Controller
def prof
#user = User.find(params[:id])
#record = Record.where(user_id: #user.id)
end
Table schema
create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
end
create_table "records", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "opponent_user_id"
t.index ["user_id"], name: "index_records_on_user_id"
end
Thanks for the help. I'm still new to rails. Any additional feedback for what I shouldn't be doing this way would likewise be appreciated.
class Record < ActiveRecord::Base
belongs_to :user, class_name: "User", foreign_key: "user_id"
belongs_to :opponent_user, class_name: "User", foreign_key: "opponent_user_id"
end
In User model:-
class User < ActiveRecord::Base
has_many :records, class_name: "Record", foreign_key: "user_id"
has_many :opponent_user_records, class_name: "Record", foreign_key: "opponent_user_id"
end
Here by:-
def prof
#user = User.find(params[:id])
#records = #user.records
end
And
<% #records.each do |record| %>
<tr>
<td><%= record.user.name %></td>
<td><%= record.opponent_user.name %></td>
</tr>
<% end %>
Please note that "applications" was a poor naming convention. This is because it is a scholarship application portal.
I get the following error when I try to open the index for applications.
NoMethodError in Applications#index
Showing /home/zane/scholarship-application/app/views/applications/_current_user_application.html.erb where line #22 raised:
undefined method `name' for nil:NilClass
Extracted source (around line #22):
20
21
22
23
24
25
<tbody>
<tr>
<td><%= current_user.application.name %></td>
<td><%= current_user.application.name %></td>
<td><%= current_user.application.gender %></td>
<td><%= current_user.application.date_of_birth %></td>
Here's what the code for that page looks like:
<% if user_signed_in? && current_user.role == "User" %>
<h1 class="center">Your Application</h1>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Gender</th>
<th>Date of birth</th>
<th>GPA</th>
<th>Address</th>
<th>State</th>
<th>University</th>
<th>Essay</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<tr>
<td><%= current_user.application.name %></td>
<td><%= current_user.application.gender %></td>
<td><%= current_user.application.date_of_birth %></td>
<td><%= current_user.application.gpa %></td>
<td><%= current_user.application.address %></td>
<td><%= current_user.application.state %></td>
<td><%= current_user.application.university %></td>
<td><%= current_user.application.essay %></td>
<td><%= link_to 'Show', current_user.application %></td>
<td><%= link_to 'Edit', current_user.edit_application_path(application) %></td>
<td><%= link_to 'Destroy', current_user.application, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
</tbody>
</table>
<% end %>
User Model:
class User < ActiveRecord::Base
#Defining different roles
enum role: [:Guest, :User, :Admin]
#Users can only have one scholarship application
has_one :applications
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Ability Model:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role == "Admin"
can :manage, :all
elsif user.role == "User"
can :manage, Application
can :manage, User
else
can :read, Static_Page
end
end
end
Application Model:
class Application < ActiveRecord::Base
belongs_to :user
#Users may only submit one application
validate :limit_applications, :on => :create
#User must fully fill out all forms application
validates :name, presence: true
validates :gender, presence: true
validates :date_of_birth, presence: true
validates :gpa, presence: true
validates :university, presence: true
validates :address, presence: true
validates :state, presence: true
private
def limit_applications
limit = 1
if self.user.applications.(:reload).count >= limit
errors.add(:base, "You can only create #{limit} application.")
end
end
end
Schema.rb
create_table "applications", force: :cascade do |t|
t.string "name"
t.string "gender"
t.date "date_of_birth"
t.string "gpa"
t.text "essay"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "university"
t.string "address"
t.string "state"
t.integer "user_id"
end
create_table "entries", force: :cascade do |t|
t.string "name"
t.boolean "winner"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "nasa_apis", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", default: "", 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 "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.integer "role", default: 1
t.boolean "winner", default: false
t.integer "application_id"
end
I'm going to assume you made a model called application.rb. When you type current_user.application, Rails searches for a method called application. This will only work if you specified a database association in your model to connect it to a table called applications. I don't know what the association is, but if you're calling .application (singular), I'd imagine you need to add one of these to your User model: has_one :application or belongs_to :application. Keep in mind you'll also need to add the corresponding database association to the Application Model. There's also a chance that Rails doesn't like the word Application and that could just be screwing up everything.
Also, the attributes you named sound an awful lot like attributes that should belong to a user, not an application. Is there any chance you don't need the .application at all and these are attributes of your user model? That would mean writing code like current_user.email, not current_user.application.email.
Now that you've included more code the answer is pretty clear: change has_one :applications to has_one :application
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.
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'.