Rails: NoMethodError in Applications#index - ruby-on-rails

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

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

Get data from different table using a foreign key in a rails view file

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 %>

Active Record Association with models/controllers of Host and Tours

Been trying multiple methods to implement the association of tours to a unique host.
Host (user)has been setup using devise by using email and password only, when later on will add further profile data fields.
Created all the relevant basic setups for the tours (title and text fields only) but just want to get the association working before moving the Tour layouts further.
The tours can only be created once the host has signed in which is currently working.
Note: there are # for comments on lines of code as I was trying to see if these work but with no success.
Below are my working structure:
host.rb
class Host < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
#before_save { |host| host.fullname }
validates :email, presence: true #length: {maximum: 50}
has_many :tours, dependent: :destroy
end
tour.rb
class Tour < ActiveRecord::Base
belongs_to :host
#validates :host_id, presence: true
validates :title, presence: true, length: { minimum: 5 }
end
migrate: devise_create_hosts.rb
class DeviseCreateHosts < ActiveRecord::Migration
def change
create_table(:hosts) do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps
end
add_index :hosts, :email, unique: true
add_index :hosts, :reset_password_token, unique: true
# add_index :hosts, :confirmation_token, unique: true
# add_index :hosts, :unlock_token, unique: true
end
end
migrate: create_tours.rb
class CreateTours < ActiveRecord::Migration
def change
create_table :tours do |t|
t.string :title
t.text :text
t.references :host, index: true
t.timestamps null: false
end
add_foreign_key :tours, :hosts
add_index :tours, [:host_id, :created_at]
end
end
So the basics of it is I want to add the tours to the host and also update the layouts html.
Hosts - show.html.erb - this needs to add some lines for the tours that are related.
So under hosts/1 I need the basics to show the tours output for title and text fields.
<div class="row">
<div class="col-md-3">
<div class="center">
</div>
<div class="panel panel-default">
<div class="panel-heading">Verification</div>
<div class="panel-body">
Email Address<br>
Phone Number here andmpre
</div>
</div>
</div>
<div class="col-md-9">
<h2><%= #host.email %></h2>
</div>
</div>
Tours - index.html.erb
- like to add line of code for to show the host id
<h1>Index TOURS</h1>
<%= link_to 'New Tour', new_tour_path %>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% #tours.each do |tour| %>
<tr>
<td><%= tour.title %></td>
<td><%= tour.text %></td>
<td><%= link_to 'Show', tour_path(tour) %></td>
<td><%= link_to 'Edit', edit_tour_path(tour) %></td>
<td><%= link_to 'Destroy', tour_path(tour),
method: :delete,
data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
hope there is a quick way that someone can help me with.
tried tutorials but as I am a newbee to ROR sometimes they confuse when I am trying to keep it simple.

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'.

Resources