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 %>
Related
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'.
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.
I am rails noob and still trying to wrap my mind around how querying associative data works. Here is my simple schema:
create_table "microposts", :force => true do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
My Associations are as follows:
class Micropost < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :user
accepts_nested_attributes_for :user
end
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :microposts
accepts_nested_attributes_for :microposts
end
What I am trying to do is query my microposts in such a way that they include an author attribute that corresponds to the user's name in the user table. Here is my html:
<% #microposts.each do |micropost| %>
<tr>
<td><%= micropost.content %></td>
<td><%= micropost.user_id %></td>
<td>
**<%= micropost.author %>**
</td>
<td><%= link_to 'Show', micropost %></td>
<td><%= link_to 'Edit', edit_micropost_path(micropost) %></td>
<td><%= link_to 'Destroy', micropost, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
How do get an attribute like microposts.author in one of cells above? I tried querying Microposts.users.name but it seems to return ruby object data like this:
[#<Micropost id: 1, content: "Though good for getting a general overview of Rails...", user_id: 2, created_at: "2012-09-02 01:52:47", updated_at: "2012-09-02 01:52:47">, #<Micropost id: 2, content: "This is another", user_id: 2, created_at: "2012-09-02 01:53:09", updated_at: "2012-09-02 01:53:09">, #<Micropost id: 3, content: "A close cousin of create_table is change_table,
What's more the data contains no mention of user name data. What am I doing wrong? How can I get micropost.author to work?
The belongs_to association works by storing (in your case) the user_id in the Micropost. This allows you to reference the User the Micropost belongs to like this:
micropost.user
And at this point you have access to any of the user attributes, such as name:
micropost.user.name
Edit
Two more things:
1) accepts_nested_attributes_for declarations are typically made in the parent class. They provide you with the ability to make calls like this:
# when creating, it infers the user_id attribute thanks to the nested attribute declaration
user.microposts.create(content: "blah blah")
# similarly, user_id is inferred here as well
user.microposts.where( ... )
Including the declaration in your Micropost model implies you intend to create a user (or search for one) from a micropost. Unnecessary in your use cases I think.
2) If you wanted to alias user as "author", you could replace the belongs_to call in Micropost with this:
belongs_to :author, class_name: "User", foreign_key: "user_id"
Which would allow you to query the author name as follows:
micropost.author.name
<%= micropost.user.name %>
You call user to get to the associated user, then call name to get that record's name attribute.