Rails undefined method `email' for nil:NilClass - mysql

I get the following error in Rails application. Can you help me?
I get an error when I connect two tables.
The user wants to use a table. I'm trying to manage the manager ID and user ID fields from a single table.
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
Rails 5.2.2
Database Design
Error Message
Database design and tables
schema.rb:
ActiveRecord::Schema.define(version: 2019_02_24_160401) do
create_table "projects", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "name"
t.text "description"
t.string "company"
t.bigint "manager_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["manager_id"], name: "index_projects_on_manager_id"
end
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "username", default: "", null: false
t.string "fullname", default: ""
t.bigint "manager"
t.string "company", default: ""
t.string "department", default: ""
t.boolean "isadmin", default: 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.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "projects", "users", column: "manager_id"
end
projects_controller.rb
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
before_action :find_users, only: [:index, :show, :new, :edit]
# GET /projects
# GET /projects.json
def index
#projects = Project.all
end
# GET /projects/1
# GET /projects/1.json
def show
end
# GET /projects/new
def new
#project = Project.new
end
# GET /projects/1/edit
def edit
end
# POST /projects
# POST /projects.json
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
# DELETE /projects/1.json
def destroy
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url, notice: 'Project was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
end
def find_users
#users = User.all.order('created_at desc')
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_params
params.require(:project).permit(:name, :description, :user_id)
end
end
Model files in model folder
project.rb and user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
has_many :projects
end
class Project < ApplicationRecord
belongs_to :user
end
calling my code email column
index.html.erb
<p id="notice"><%= notice %></p>
<h1>Projects</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Company</th>
<th>Manager</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #projects.each do |project| %>
<tr>
<td><%= project.name %></td>
<td><%= project.description %></td>
<td><%= project.company %></td>
<td><%= project.user.email %></td>
<td><%= link_to 'Show', project %></td>
<td><%= link_to 'Edit', edit_project_path(project) %></td>
<td><%= link_to 'Destroy', project, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Project', new_project_path %>

Your Project has a manager_id, but no table called managers, instead it's users. So in your relationship between projects and users, it needs to use the correct key.
class User < ApplicationRecord
has_many :projects, foreign_key: :manager_id
end
class Project < ApplicationRecord
belongs_to :user, foreign_key: :manager_id
end

It seems the data is not well drafted in the schema. Is User the same thing as Manager? If it is not, you should have a user_id reference in the Projects table else, you'll have to add proper associations tying manager_id to Users then you can have project.manager.email

If user is optional for Project just call project.user&.email

Related

Getting error (undefined method) for form_with

I have _form.html.erb. and on the first line of code, it is throwing this error:
undefined method `assessments_path' for #ActionView::Base:0x00000000229750
Did you mean? asset_path
couple of points:
in routes 'poweruser' is a namespace
in models assessment.rb is
not folder poweruser (as I plan to use it for other pages that anyone can access not just powerusers).
Any ideas what I am missing?
Here is the code:
<%= form_with(model: [#assessment], local: true) do |form| %>
<%= render "shared/error_messages", resource: form.object %>
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: "form-control" %>
</div>
<% end %>
Here is the controller
module Powerusers
class AssessmentsController < ApplicationController
before_action :authenticate_user!
# before_action :set_assessment
# before_action :set_assessment, only: [:set_assessment, :show, :edit, :update, :destroy]
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
# def update
# super
# send_foo_updated_email(requested_resource)
# end
def index
#pagy, #assessments = pagy(Assessment.sort_by_params(params[:sort], sort_direction))
# We explicitly load the records to avoid triggering multiple DB calls in the views when checking if records exist and iterating over them.
# Calling #assessments.any? in the view will use the loaded records to check existence instead of making an extra DB call.
#assessments.load
end
# GET /assessments/new
def new
#assessment = Assessment.new
#assessment.assessment_sections.new
end
here is the assessment.rb (not it is not in a sub-folder poweruser)
class Assessment < ApplicationRecord
belongs_to :company
has_many :assessment_sections, inverse_of: :assessment
has_many :questions, through: :assessment_sections
accepts_nested_attributes_for :assessment_sections, reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :questions, reject_if: :all_blank,
allow_destroy: true
end
Here is the routes
# PowerUser
authenticated :user, lambda { |u| u.admin? } do
namespace :powerusers do
resources :assessments do
resources :assessment_sections do
resources :questions
end
end
end
end
As you have namespace in routes, you need to specify that with form_with
<%= form_with(model: [:powerusers, #assessment], local: true) do |form| %>

How to place a form to create a new entry on the main page. RoR

I install Ubuntu, RoR and all RoR components. Then I update question.
I need to get the form as in this screenshot. I need this part. So that it appears after clicking the Add button. I understand the logic of adding a form after clicking a button, but I can’t understand how the code of the form itself should look. Even a form with only text_field launches a site with an error 500. I may not know a lot, I'm just learning RoR.
index.html.erb
<style type="text/css">
p{
font-family: Times New Roman
}
</style>
<p>
<strong><h1><%= #title %></h1>
<br><br><br>
<% #projects.each do |project| %>
<h2 style="text-transform: uppercase"><%= project.title %></h2>
<ul>
<% #todos.each do |todo| %>
<% if todo.project_id == project.id %>
<li><%= todo.text %></li>
<% end %>
<% end %>
</ul>
<% end %>
<h2>Новая задача</h2>
<%= form_with(model: #todo, local: true) do |todo| %>
<%= todo.text_field :text %>
<%= todo.select (:project_id, option_for_select(project_array)) %>
<%= todo.submit "Отмена"%>
<%= todo.submit "ОК" %>
<% end %>
</strong>
</p>
projects_controller.rb
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
# GET /projects
# GET /projects.json
def index
#projects = Project.all
end
# GET /projects/1
# GET /projects/1.json
def show
end
# GET /projects/new
def new
#project = Project.new
end
# GET /projects/1/edit
def edit
end
# POST /projects
# POST /projects.json
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /projects/1
# PATCH/PUT /projects/1.json
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
# DELETE /projects/1
# DELETE /projects/1.json
def destroy
#project.destroy
respond_to do |format|
format.html { redirect_to projects_url, notice: 'Project was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_project
#project = Project.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def project_params
params.require(:project).permit(:title)
end
end
todos_controller.rb
class TodosController < ApplicationController
before_action :set_todo, only: [:show, :edit, :update, :destroy]
# GET /todos
# GET /todos.json
def index
#todos = Todo.all
end
# GET /todos/1
# GET /todos/1.json
def show
end
# GET /todos/new
def new
#todo = Todo.new
end
# GET /todos/1/edit
def edit
end
# POST /todos
# POST /todos.json
def create
#todo = Todo.new(todo_params)
respond_to do |format|
if #todo.save
format.html { redirect_to #todo, notice: 'Todo was successfully created.' }
format.json { render :show, status: :created, location: #todo }
else
format.html { render :new }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /todos/1
# PATCH/PUT /todos/1.json
def update
respond_to do |format|
if #todo.update(todo_params)
format.html { redirect_to #todo, notice: 'Todo was successfully updated.' }
format.json { render :show, status: :ok, location: #todo }
else
format.html { render :edit }
format.json { render json: #todo.errors, status: :unprocessable_entity }
end
end
end
# DELETE /todos/1
# DELETE /todos/1.json
def destroy
#todo.destroy
respond_to do |format|
format.html { redirect_to todos_url, notice: 'Todo was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_todo
#todo = Todo.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def todo_params
params.require(:todo).permit(:text, :isCompleted, :project_id)
end
end
todoshnik_controller.rb
class TodoshnikController < ApplicationController
def index
#title = 'Задачи'
#projects = Project.all
#todos = Todo.all
#todo = Todo.new
#project_array = #projects.map { |project| [project.title, project.id] }
end
def create
end
def update
end
end
routes.rb
Rails.application.routes.draw do
root 'todoshnik#index'
resources :todos
resources :projects
end
Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.6.5'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.0'
# Use postgresql as the database for Active Record
gem 'pg', '>= 0.18', '< 2.0'
# Use Puma as the app server
gem 'puma', '~> 3.11'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
end
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Easy installation and use of web drivers to run system tests with browsers
gem 'webdrivers'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
Add a #todo instance variable in the index action
def index
#title = 'Задачи'
#projects = Project.all
#todos = Todo.all
#todo = Todo.new
#project_array = #projects.map { |project| [project.title, project.id] } #minimize the use of logic in your views.
end
Add the form in your view.
<%= form_for #todo do |todo| %>
<%= todo.text_field :text %>
<%= todo.select :project_id, options_for_select(#project_array) %>
<%= todo.submit "Отмена" %>
<%= todo.submit "ОК" %>
<% end %>
If you have a TodoController with create and update action
<%= form_with(model: #todo, local: true) do |todo| %>
<%= todo.text_field :text %>
<%= todo.select :project_id, options_for_select(#project_array) %>
<%= todo.submit "ОК" %>
<% end %>
This will always point to the create action of the TodoController
The problem is #todo is not referenced in your index_controller.rb, so your index_controller.rb #index action must be:
def index
#title = 'Задачи'
#projects = Project.all
#todos = Todo.all
#todo = Todo.new
end
Then in your index.html.erb
<%= form_for #todo do |todo| %>
<%= todo.text_field :text %>
<%= project_array = #projects.map { |project| [project.title, project.id] } %>
<%= todo.select (:project_id, option_for_select(project_array)) %>
<%= todo.submit "Отмена" %>
<%= todo.submit "ОК" %>
<% end %>
try to install simple_form_for gem. It will allow you makes form without any troubles.

Ruby on Rails - rake db:migrate is not working

I am using def change for models. Here is migration output of my models:
$ rake db:migrate
== 20150802221545 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.3319s
== 20150802221545 CreateUsers: migrated (0.3322s) =============================
== 20150802221550 CreatePages: migrating ======================================
-- create_table(:pages)
-> 0.2457s
-- add_index(:pages, :subject_id)
-> 0.2487s
-- add_index(:pages, :permalink)
-> 0.2464s
== 20150802221550 CreatePages: migrated (0.7416s) =============================
== 20150802221558 CreateSubjects: migrating ===================================
-- create_table(:subjects)
-> 0.2453s
== 20150802221558 CreateSubjects: migrated (0.2456s) ==========================
== 20150802221603 CreateSections: migrating ===================================
-- create_table(:sections)
-> 0.2776s
-- add_index(:sections, :page_id)
-> 0.2484s
== 20150802221603 CreateSections: migrated (0.5265s) ==========================
Now, I created a migration named AlterUsers and wrote following code.
- class AlterUsers < ActiveRecord::Migration
def change
reversible do |dir|
dir.up {
rename_table :users, :admin_users
add_column :admin_users, :username, :string, :limit => 25
#add_column("admin_users", "username", :string, :limit => 25)
change_column :admin_users, :email, :string, :limit => 100
#change_column("admin_users", "email", :string, :limit => 100)
rename_column :admin_users, :password, :hashed_password
#rename_column("admin_users", "password", "hashed_password")
add_column :admin_users, :salt, :string, :limit => 40
#add_column("admin_users", "salt", :string, :limit => 40)
puts "*** about to add index ***"
add_index :admin_users, :username
#add_index("admin_users", "username")
}
dir.down{
remove_index :admin_users, :username
remove_column :admin_users, :salt
rename_column :admin_users, :hashed_password, :password
change_column :admin_users, :email, :string, :default=> "", :null => false
remove_column :admin_users, :username, :string, :limit => 25
raname_table :admin_users, :users
}
end
end
end
I used def change as I am using rails4. But rake db:migrate doesn't affect database. Then I wrote same code as instructor's code:
class AlterUsers < ActiveRecord::Migration
def self.up
rename_table :users, :admin_users
add_column :admin_users, :username, :string, :limit => 25
change_column :admin_users, :email, :string, :limit => 100
rename_column :admin_users, :password, :hashed_password
add_column :admin_users, :salt, :string, :limit => 40
puts "*** about to add index ***"
add_index :admin_users, :username
end
def self.down
remove_index :admin_users, :username
remove_column :admin_users, :salt
rename_column :admin_users, :hashed_password, :password
change_column :admin_users, :email, :string, :default=> "", :null => false
remove_column :admin_users, :username, :string, :limit => 25
raname_table :admin_users, :users
end
end
It worked. After all these migrations, I am trying to undo by using this command rake db:migrate VERSION=0. But it's not working. Here is db migration status:
$ rake db:migrate:status
database: mysqlapp_development
Status Migration ID Migration Name
--------------------------------------------------
up 20150802221545 Create users
up 20150802221550 Create pages
up 20150802221558 Create subjects
up 20150802221603 Create sections
up 20150802221735 Alter users
And here is rake db:migrate --trace output
$ rake db:migrate --trace
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
** Invoke db:load_config (first_time)
** Execute db:load_config
** Execute db:migrate
** Invoke db:_dump (first_time)
** Execute db:_dump
** Invoke db:schema:dump (first_time)
** Invoke environment
** Invoke db:load_config
** Execute db:schema:dump
Now I have following major queries in addition to above problem.
1) In which cases rake db:migrate doesn't affect? (I'm confused because of it's working behaviour)
2) How to redo the undo/roll back migrations?
You should simply split this into several migrations. Just use change and let rails deal with creating the reverse migration.
Given:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password
t.timestamps null: false
end
end
end
Migrations:
class ChangeUsersTable < ActiveRecord::Migration
def change
rename_table :users, :admin_users
end
end
class AddHashedPasswordToAdminUsers < ActiveRecord::Migration
def change
# you should not just migrate the plaintext passwords!
remove_column :admin_users, :password, :string
add_column :admin_users, :password_hash, :string
add_column :admin_users, :salt, :string, limit: 40
end
end
class AddUserNameToAdminUser < ActiveRecord::Migration
def change
add_column :admin_users, :username, :string
end
end
Result:
ActiveRecord::Schema.define(version: 20150802210303) do
create_table "admin_users", force: :cascade do |t|
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_hash"
t.string "salt", limit: 40
t.string "username"
end
end
Don't roll your own password encryption!
I would also recommend that you use has_secure_password rather than rolling your own password encryption. Its way to easy to mess it up.
In that case the migration would be:
class AddPasswordDigestToAdminUsers < ActiveRecord::Migration
def change
# you should not just migrate the plaintext passwords!
remove_column :admin_users, :password, :string
add_column :admin_users, :password_digest, :string
# has_secure_password concatenates the salt into the digest.
# no salt column needed.
end
end

rake db:migrate not creating table

I'm just following this RoR tut, I'm doing it the same way but I'm stuck creating a table:
$ rails generate model User
invoke active_record
create db/migrate/20140718180319_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
This is my xxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration
def Up
create_table :users do |t|
t.column "first_name", :string, :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => "", :null => false
t.string "password", :limit => 40
t.timestamps
end
end
def down
drop_table :users
end
end
When I run db:migrate the table is not being created:
$ rake db:migrate
== 20140718182504 CreateUsers: migrating ======================================
== 20140718182504 CreateUsers: migrated (0.0000s) =============================
Is missing
create_table(:users)
-> x.xxxxxs
What am I doing wrong? Thanks.
Isn't it a typo with your "Up" migration method? Try with:
def up
instead of:
def Up

No such column: admin_users.password

When I run my migrations in rails, I get back a sql error saying that no such column:admin_users.password exists. When I SHOW FIELDS it's clearly there. I'm sure this is because I missed something somewhere. In this case, understanding what went wrong is much more important to me than fixing the issue since I keep getting stuck with my database in a broken state.
First Migration:
class CreateUsers < ActiveRecord::Migration
def up
create_table :users do |t|
t.column "first_name", :string, :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => "", :null => false
t.string "password:", :limit => 40
# t.datetime "created_at"
# t.datetime "updated_at"
# The above two are created automatically by the below command
t.timestamps
end
end
def down
drop_table :users
end
end
Second Migration:
class AlterUsers < ActiveRecord::Migration
def up
rename_table("users", "admin_users")
add_column("admin_users", "username", :string, :limit => 25, :after => "email")
change_column("admin_users", "email", :string, :limit => 100)
rename_column("admin_users", "password", "hashed_password")
puts "*** Adding an index is next***"
add_index("admin_users", "username")
end
def down
remove_index("admin_users", "username")
rename_column("admin_users", "hashed_password", "password")
change_column("admin_users", "email", :default => "", :null => false)
remove_column("admin_users", "username")
rename_table("admin_users", "users")
end
end
=>== CreateUsers: migrated (0.0163s) ===========================================
== AlterUsers: migrating =====================================================
-- rename_table("users", "admin_users")
-> 0.0034s
-- add_column("admin_users", "username", :string, {:limit=>25, :after=>"email"})
-> 0.0158s
-- change_column("admin_users", "email", :string, {:limit=>100})
-> 0.2273s
-- rename_column("admin_users", "password", "hashed_password")
rake aborted!
An error has occurred, all later migrations canceled:
No such column: admin_users.password
That's because you called your column password: with a colon in the first migration.
I bet that's because of a typo in the rails generate model commandline. It's horribly un-foolproof.