I am building a repo that imitates Evernote, and I have established the relationship between the models and their respective columns. Among them, I rely on the column email in the model User to identify the user.
However, when I try to print <%= note.user.email %> in index.html.erb, I get an "undefined method `email' for nil:NilClass" error. I don't understand, I have established valid has_many and belongs_to, and email is also an actual column. note is derived from the entity variable #note in the controller (where other fields are valid), I don't understand which link is wrong.
This is part of the schema
create_table "users", force: :cascade do |t|
t.string "nickname"
t.string "password"
t.string "email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
This is part of model User
class User < ApplicationRecord
validates :nickname, presence: true
validates :email, presence: true
validates :password, presence: true, confirmation: true
before_create :encrypt_password
has_many :notes
This is model Note
class Note < ApplicationRecord
validates :title, presence: true
validates :content, presence: true
default_scope { where(deleted_at: nil) }
belongs_to :user
end
This is part of NotesController
def index
#notes = Note.includes(:user).order(id: :desc)
end
this is index.html.erb
<table>
<tr>
<td>Author</td>
<td>Title</td>
<td>Actions</td>
<% #notes.each do |note| %>
<tr>
<td>
<%= note.user.email %>
</td>
<td>
<%= link_to note.title, note_path(note) %>
</td>
<td>
<%= link_to "TO EDIT", edit_note_path(note) %>
</td>
<td>
<%= link_to "TO DELETE", note_path(note), method: 'delete', data: { confirm: "確定嗎?" } %>
</td>
</tr>
<% end %>
</table>
undefined method `email' for nil:NilClass"
This error means you are looking for the method email on a nilClass object which mean your note.user is nil.
Rails can't find any user related to the note. You can first check if your note as a user.
Also you should check if you have a column user_id in your Note model it's needed for making the belongs_to relationship working. You probably did something like this in your note migration:
create_table "notes", force: :cascade do |t|
t.belongs_to :user
...
end
If you want your view to keep rendering and ignore the error if a note doesn't have any user you can do like this.
<% if note.user.present? %>
<td>
<%= note.user.email %>
</td>
<% end %>
or even using the safe navigation operator but it has its pros & cons
<td>
<%= note.user&.email %>
</td>
Related
I am building a Portfolio website that has a simple view file for projects I have worked on. On my "work" view I render a collection of "technologies" that I used to build a particular product. This is working great, and each technology renders just fine. However, right below the that renders the technologies the plain hash is also being rendered, and I cannot figure out why.
I am following a tutorial for this and I have double checked that my code is the same as the instructor's.
Work View
<%= image_tag #work_item.main_image unless #work_item.main_image.nil? %>
<h1>Title: <%= #work_item.title %></h1>
<em><%= #work_item.subtitle %></em>
<p><%= #work_item.body %></p>
<h2>Technologies Used:</h2>
<%= #work_item.technologies.each do |t| %>
<p><%= t.name %></p>
<% end %>
Schema
create_table "technologies", force: :cascade do |t|
t.string "name"
t.bigint "work_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["work_id"], name: "index_technologies_on_work_id"
end
controller method being used via def show via a before_action
def set_work
#work_item = Work.find(params[:id])
end
Technology Model
class Technology < ApplicationRecord
belongs_to :work
end
Work Model
class Work < ApplicationRecord
has_many :technologies
accepts_nested_attributes_for :technologies,
reject_if: lambda { |attrs| attrs['name'].blank? }
include Placeholder
validates_presence_of :title, :body, :main_image, :thumb_image
def self.react
where(subtitle: "React")
end
scope :ruby_on_rails, -> { where(subtitle: "Ruby on Rails") }
after_initialize :set_defaults
def set_defaults
self.main_image ||= Placeholder.image_generator(height: 600, width: 400)
self.thumb_image ||= Placeholder.image_generator(height: 350, width: 200)
end
end
Here is a screenshot of what I'm seeing
https://imgur.com/a/2T3SRZv
Because the = in
<%= #work_item.technologies.each do |t| %>
indicates that you want #work_item.technologies to be output to the view.
Instead, use
<% #work_item.technologies.each do |t| %>
BTW, that's not a hash, it's an enumerable.
UPDATE: Problem solved, thanks to Sebastian and Gabriel for the helpful pointers.
The relevant changes to my code are as follows:
app/controllers/pomodoro_cycles_controller.rb
def pomodoro_collections
{
pomodoro_collection_0: Pomodoro.offset(0).first(100),
pomodoro_collection_1: Pomodoro.offset(100).first(100)
}
end
app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p>
<%= pomodoro.id %>
<%= pomodoro.color %>
</p>
<% end %>
<% end %>
NOTA BENE:
The #first method in ActiveRecord returns an Array, so the keys in my original Hash were nested Arrays. Instead, the following was sufficient to return an Array of Pomodoro objects:
Pomodoro.offset(0).first(100)
DESCRIPTION OF ORIGINAL PROBLEM
Rails 5, PostgreSQL
PROBLEM: I cannot access Pomodoro.all from within PomodoroCycleController
I have two scaffolds: Pomodoro and PomodoroCycle, and I want to access the full list of Pomodoros within the PomdoroCycle controller.
The following code is kept simple, in order to make as clear as possible what I'm trying to do. If I can do these things, then I'll be able to do much more, but one step at a time.
Regarding the db migration files, I have already run bundle exec rails db:migrate
I want to display a full list of Pomodoros in the PomodoroCycle Show View (later to be displayed in Index), but I don't know what is missing.
From app/controllers/pomodoro_cycles_controller.rb
def show
#pomodoros_collections = pomodoro_collections
end
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
From app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |collection| %>
<p><%= collection %></p>
<% end %>
However, this displays nothing in the browser.
app/models/pomodoro_cycle.rb
class PomodoroCycle < ApplicationRecord
has_many :pomodoros
end
app/models/pomodoro.rb
class Pomodoro < ApplicationRecord
belongs_to :pomodoro_cycle
end
Updated db/migrate/20180103032759_create_pomodoro_cycles.rb:
class CreatePomodoroCycles < ActiveRecord::Migration[5.1]
def change
create_table :pomodoro_cycles do |t|
t.string :activity
t.integer :iteration
t.integer :matrix_side_length
t.datetime :created_at
t.datetime :completed_at
t.string :category_labels, array:true, default: []
t.string :category_colors, array:true, default: []
t.string :username
t.timestamps
end
create table :pomodoros do |t|
t.belongs_to :pomodoro_cycle, index: true
t.datetime :completed_at
t.timestamps
end
add_index :pomodoros, :pomodoro_cycle_id
end
end
Untouched db/migrate/20180103054425_create_pomodoros.rb
class CreatePomodoros < ActiveRecord::Migration[5.1]
def change
create_table :pomodoros do |t|
t.boolean :status
t.string :category
t.string :color
t.datetime :completed_at
t.string :username
t.timestamps
end
end
end
First of all, as #SebastianPalma pointed out in the comments, the syntax is wrong
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
should be:
def pomodoro_collections
{
pomodoro_collection_0: [Pomodoro.offset(0).first(100)],
pomodoro_collection_1: [Pomodoro.offset(100).first(100)]
}
end
Make the keys in the hash symbols
Then to display each Pomodoro put something like:
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p><%= pomodoro.id %></p> #Or the attribute you want to display
<% end %>
<% end %>
Hope this help
In our Rails 4 app, there are four models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
class Post < ActiveRecord::Base
belongs_to :calendar
end
Here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.references :calendar, index: true, foreign_key: true
t.date :date
t.time :time
t.string :focus
t.string :format
t.string :blog_title
t.text :long_copy
t.text :short_copy
t.string :link
t.string :hashtag
t.string :media
t.float :promotion
t.string :target
t.integer :approval
t.text :comment
t.timestamps null: false
end
end
end
We have the following form to create posts:
<h2>Create a new post</h2>
<%= form_for(#post) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<tr>
<td class="field"><%= f.date_field :date, placeholder: "When do you want to publish this post?" %></td>
<td class="field"><%= f.time_field :time, placeholder: "What time do you want to publish this post?" %></td>
<td class="field"><%= f.text_field :focus, placeholder: "What is this post about?" %></td>
<td class="field"><%= f.text_field :format, placeholder: "What type of post is this?" %></td>
<td class="field"><%= f.text_field :blog_title, placeholder: "If this post is about a blog post, what is the title of the blog post?" %></td>
<td class="field"><%= f.text_area :long_copy, placeholder: "What is the copy of the post?" %></td>
<td class="field"><%= f.text_area :short_copy, placeholder: "What is the short copy of the post (to be used on Twitter for instance)?" %></td>
<td class="field"><%= f.url_field :link, placeholder: "Which link to you want to embed in this post?" %></td>
<td class="field"><%= f.text_field :hashtag, placeholder: "Which hashtag(s) do you want to you in this post?" %></td>
<td class="field"><%= f.text_field :media, placeholder: "Which media file (image, video) do you want to include in this post?" %></td>
<td class="field"><%= f.number_field :promotion, placeholder: "What advertising budget should be allocated to this post?" %></td>
<td class="field"><%= f.text_field :target, placeholder: "Who do you want to target with this post?" %></td>
<td class="field"><%= f.select(:approval, %w[Approved Needs edits To be deleted], {prompt: 'How does this post look?'}) %></td>
<td class="field"><%= f.text_area :comment, placeholder: "Any comment?" %></td>
<td><%= f.submit "Create", class: "btn btn-primary" %></td>
</tr>
<% end %>
This form is embedded into the Calendars#Show view, so that as soon as a post is created, it appears in the corresponding calendar.
EDIT 2: And here is ou PostsController:
class PostsController < ApplicationController
def create
#post = Post.create!
if #post.save
redirect_to root_url
else
render root_url
end
end
end
Currently, whenever we submit the form, a post is actually created (we checked through the console).
EDIT: Actually, the new #post is created and saved to the database but all values are set to nil. It seems we also have a problem with the saving of data from the form and into the database.
However, it does not appear in the corresponding calendar.
We believe this is due to the fact that, when a post is created, the calendar_id of the active calendar (the one in which the form is embedded in) is not added to the new post instance.
However, we don't know how to implement it:
Should we create an active_calendar method and then use it to automatically assign the active_calendar id of the active calendar to the newly created #post?
Should we simply declare the active calendar id as a variable, either in the post controller or the post model, so that we can add it when a new #post is created?
Should we include the active calendar id in the newly created #post through a hidden field in the form?
We kind of hit a wall here and are not sure of what we are doing wrong.
Any idea of how to fix this?
UPDATE: as suggested by #Fire-Dragon-DoL in the comments of his answer, we are going to use a hidden field.
Would the following code work?
<td type="hidden" value="#{#calendar.id}"><% f.number_field :calendar_id %></td>
I'm not sure how or where is stored your "active calendar" information, you definitely need it. Make it a field for User, it's a good option.
Then I would create an application controller method like
def active_calendar
# Assuming you have a method to return current logged in user
current_user.try(:active_calendar)
end
Then when creating post you can easily perform the following
attrs = post_params.merge(calendar: active_calendar)
#post = Post.new(attrs)
# Or whatever logic you want
#post.save
Use the hidden field only if user is allowed to specify different calendars than the active one, otherwise it might just be a security hole
Notice that the code I wrote is also nil-proof
You may want to nest your routes, for example:
resources :calendars do
resources :posts
end
Now change your form to also include the calendar you want to be associated with the post like:
form_for([#calendar, #post])...
Then in your posts controller create path you can find your calendar and construct your post with something like:
def create
my_calendar = Calendar.find(params[:calendar_id])
post = my_calendar.posts.create(post_params)
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:date, :time, :focus, :format, :blog_title, :long_copy, :short_copy, :link, :hashtag, :media, :promotion, :target, :approval, :comment)
end
I'm new to Ruby on Rails and have not programmed for many years. I'm trying some simple code that is similar to the sample provided in the "Getting Started with Rails" guide for Rails 3.2. Instead of Posts and Comments my models are States and Counties. I've reviewed the code for problems with pluralization, but don't see anything out of place. My system is configured with Rails 4.0 and ruby 1.9.3. I encounter the error after I list the states from the index page. Once the states are listed, I select to show a state, which should allow me to add a County, but instead I get the following error on the page:
uninitialized constant State::County
The code listed with the error is from /app/views/states/show.html.erb
</p>
<h2>Add a County:</h2>
<%= form_for([#state, #state.counties.build]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
I'm providing other MVC files and the DB schema below.
Models:
state.rb
class State < ActiveRecord::Base
attr_accessible :abbr, :name
validates :abbr, :presence => true,
:length => { :maximum => 2 },
:format => { :with => /\A[A-Z]+\z/,
:message => "only 2 uppercase letters allowed" }
validates :name, :presence => true
has_many :counties, :dependent => :destroy
end
county.rb
class County < ActiveRecord::Base
attr_accessible :name, state_id
validates :name, :presence => true
belongs_to :state
end
Views
State/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Abbr:</strong>
<%= #state.abbr %>
</p>
<p>
<strong>Name:</strong>
<%= #state.name %>
</p>
<h2>Add a County:</h2>
<%= form_for([#state, #state.counties.build]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<br />
<%= link_to 'Edit', edit_state_path(#state) %> |
<%= link_to 'Back', states_path %>
Routes.rb
resources :states do
resources :counties
end
Controllers
counties_controller.rb
class CountiesController < ApplicationController
def create
#state = State.find(params[:state_id])
#county = #state.counties.create(params[:county])
redirect_to state_path(#state)
end
def destroy
#state = State.find(params[:state_id])
#county = #state.counties.find(params[:id])
#county.destroy
redirect_to state_path(#state)
end
end
states_controller.rb
This is a standard file created by rails with the scaffold generator. No changes have been made to this file. If you need it to help with this issue I will post it, but it is rather long.
schema.rb
ActiveRecord::Schema.define(version: 20141013234441) do
create_table "counties", force: true do |t|
t.string "name"
t.integer "state_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "counties", ["state_id"], name: "index_counties_on_state_id", using: :btree
create_table "states", force: true do |t|
t.string "abbr", limit: 2, null: false
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Any help is appreciated...
Never mind... I found the answer to my own question. I knew it had to be something simple. I was missing a colon in the county.rb file.
Original:
class County < ActiveRecord::Base
attr_accessible :name, state_id
Corrected:
class County < ActiveRecord::Base
attr_accessible :name, :state_id
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.