today my error is Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id.
I’ve got models & controllers for: User, Ticket, Answers defined in such way:
User has many Answers & Tickets
Ticket has many Answers
Answer belongs to User & belongs to Ticket
The error appears when in Ticket view I’m trying to display Answers author (User) doing it like this:
Ticket Load (0.2ms) SELECT "tickets".* FROM "tickets" LIMIT 1
=> #<Ticket id: 8, topic: "This is sample question", message: "This is Sample question with <h1>Some</h1> HTML! <p...", user_id: 1, category_id: 2, created_at: "2013-01-15 12:24:20", updated_at: "2013-01-15 12:24:20", answers_count: 1>
Answer Load (0.2ms) SELECT "answers".* FROM "answers" LIMIT 1
=> #<Answer id: 6, ticket_id: 8, user_id: 1, message: "This is answer to the sample question with use of h...", created_at: "2013-01-15 12:26:46", updated_at: "2013-01-15 12:26:46">
User Load (0.2ms) SELECT "users".* FROM "users" LIMIT 1
=> #<User id: 1, username: "mickula", email: "xx@xx.com", password_hash: "$2a$10$Hty2ekM3t8Dqf0CvZm5zEOwVnXAoytimW9tIOxtfjwNG...", password_salt: "$2a$10$Hty2ekM3t8Dqf0CvZm5zEO", created_at: "2013-01-12 16:55:00", updated_at: "2013-01-12 16:55:00", permission_level: nil>
User.first.answers
User Load (0.2ms) SELECT "users".* FROM "users" LIMIT 1
Answer Load (0.1ms) SELECT "answers".* FROM "answers" WHERE "answers"."user_id" = 1
=> [#<Answer id: 6, ticket_id: 8, user_id: 1, message: "This is answer to the sample question with use of h...", created_at: "2013-01-15 12:26:46", updated_at: "2013-01-15 12:26:46">]
When I try to display answer a.user.username it returns error:
undefined method username for nil:NilClass
So it seems like that answer is not connected with user. Could you point me where I did mistake and why it behaves like that? I would like to mention that I did it this way for Tickets and there I can display author of ticket with
@ticket.user.username
Models:
class Ticket < ActiveRecord::Base
attr_accessible :topic, :message, :user, :category, :category_id
has_many :answers
belongs_to :user
belongs_to :category
end
class Answer < ActiveRecord::Base
attr_accessible :ticket, :user, :message
belongs_to :user
belongs_to :ticket, :counter_cache => true
end
class User < ActiveRecord::Base
attr_accessible :username, :email, :password, :password_confirmation
has_many :tickets
has_many :answers
end
Controller’s partials:
class TicketsController < ApplicationController
before_filter :login_required
before_filter :admin_required, :only => [:index, :show]
def index
@tickets = Ticket.all
end
def show
@ticket = Ticket.find(params[:id])
end
end
class AnswersController < ApplicationController
def create
t_attr = params[:answer].merge :user => current_user
@ticket = Ticket.find(params[:ticket_id])
@answer = @ticket.answers.create(t_attr)
redirect_to ticket_path(@ticket)
end
end
Edit:
Through console I can obtain answer’s author userdata:
Ticket.first.answers.first.user
Ticket Load (0.2ms) SELECT "tickets".* FROM "tickets" LIMIT 1
Answer Load (0.1ms) SELECT "answers".* FROM "answers" WHERE "answers"."ticket_id" = 8 LIMIT 1
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
=> #<User id: 1, username: "mickula", email: "xx@xx.com", password_hash: "$2a$10$Hty2ekM3t8Dqf0CvZm5zEOwVnXAoytimW9tIOxtfjwNG...", password_salt: "$2a$10$Hty2ekM3t8Dqf0CvZm5zEO", created_at: "2013-01-12 16:55:00", updated_at: "2013-01-12 16:55:00", permission_level: nil>
Ticket view:
<span class="pull-right">by <strong><%=h @ticket.user.username %></strong> on <i><%=h @ticket.created_at %> in <strong><%= @ticket.category.name %></strong></i></span>
<p>
<strong>Message:</strong><br>
<%=h @ticket.message %>
</p>
<h3>Replies</h3>
<%= form_for([@ticket, @ticket.answers.build]) do |f| %>
<%= f.label :message %> <br>
<%= f.text_area :message, :cols => "50", :rows => "6", :class=> "span12" %><br>
<%= f.submit %>
<% end %>
<% @ticket.answers.each do |a| %>
<%= h a.message %> <%= a.user.username %> <%= a.created_at %>
<% end %>
</div>
Workaround:
<% @ticket.answers.each do |a| %>
<%= h a.message %> <%= a.user.username if a.user %> <%= a.created_at %>
<% end %>
With this condition everything works.
Seems like Loop is doing 1 extra iteration, even if there’s no answers.
Rails scaffolding creates new, empty object for this:
to perform form operations like displaying entered form inputs after validation is unsuccesfull (so user won’t loose data entered into form fields),
as specified in answers controller:
Because of this loop was doing 1 extra iteration (with one object at the end).
To fix it I simply iterate over
ticket.answers.length - 1objects.