Ruby on rails - model missing required attr_accessor for [RAILS] - html

I'm using ruby on rails and I have some problem with it !
I tried to create a database but it seems not to work !
I generated a model and a database file thank's to the command:
rails g model photos
And here my codes
photos_controller.rb :
class PhotosController < ApplicationController
# POST /photos
# POST /photos.json
def create
#photo = Photo.new(photo_params)
photo_controller.rb
respond_to do |format|
if #photo.save
format.html { redirect_to #photo, notice: 'Photo was successfully created.' }
format.json { render action: 'show', status: :created, location: #photo }
else
format.html { render action: 'new' }
format.json { render json: #photo.errors, status: :unprocessable_entity }
end
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def photo_params
params.require(:photo).permit(:image)
end
end
in the model photo.rb :
class Photo < ActiveRecord::Base
has_attached_file :image
end
in the file 2011234116731_create_photos.rb :
class CreatePhotos < ActiveRecord::Migration
def self.up
add_column :photos, :image_file_name, :string
add_column :photos, :image_content_type, :string
add_column :photos, :image_file_size, :string
add_column :photos, :image_update_at, :string
end
def self.down
remove_column :photos, :image_file_name, :string
remove_column :photos, :image_content_type, :string
remove_column :photos, :image_file_size, :string
remove_column :photos, :image_update_at, :string
end
end
But when I try to load a page which use an element "image" of the model, I ha the following error :
Photo model missing required attr_accessor for 'image_file_name'
Extracted source (around line #27):
POST /photos.json
def create
#photo = Photo.new(photo_params)
respond_to do |format|
if #photo.save
I noticed that the migration seems not to work because in my scheme.rb :
( I've done the rake db:migrate command )
ActiveRecord::Schema.define(version: 20131124183207) do
create_table "photos", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
end
end

Doesn't seem like the migration ran correctly. And it also doesn't look like a migration that would get generated by the rails g model command. It's missing the create_table method. It looks like you previously created the Photo model and then created another migration to add the image fields.
My first hunch would be to try rolling the migration back:
rake db:migrate:down VERSION=2011234116731
Then running rake db:migrate again and check your schema file to make sure all the columns are there.

Related

Why isn't Ruby on Rails loading my associated objects with .includes()?

My Problem
I'm attempting to retrieve data through a foreign key association in my Ruby on Rails application. The data from the primary table is loaded correctly, but the associated objects are not being loaded and are always nil.
Background Info (Migrations, Database tables, and Model classes)
I'm currently working with two tables:
eval_forms
user_details
The tables are created through Rails migrations.
The user_details table is created through a single migration:
class CreateUserDetails < ActiveRecord::Migration[5.2]
def change
create_table :user_details do |t|
t.string :eduPersonPrincipalName, unique: true
t.string :DisplayName, default: 'NULL'
t.string :Email, default: 'NULL'
t.string :Role, default: 'Student'
t.boolean :hasAppointment, default: '0'
t.timestamps
end
end
def self.down
drop_table :user_details
end
end
and the eval_forms table has had a few migrations to create and update it:
class CreateEvalForms < ActiveRecord::Migration[6.1]
def change
create_table :eval_forms do |t|
t.belongs_to :form_builder, foreign_key: 'form_builder_id'
t.belongs_to :course, foreign_key: 'course_id'
t.string :Description
t.datetime :OpenDate
t.datetime :CloseDate
t.timestamps
end
end
end
class UpdateEvalForms < ActiveRecord::Migration[6.1]
def change
add_column :eval_forms, "Author_user_details_id", :bigint, null: false
add_foreign_key :eval_forms, :user_details, column: "Author_user_details_id"
add_column :eval_forms, "Year", :integer
add_column :eval_forms, "Semester", :string
add_column :eval_forms, "IsArchived", :boolean
end
end
I know that the foreign key is set up correctly as it is listed correctly in MySQL. Here's a reference from MySQL of the 2 tables and their relation:
Additionally, I've set up the model classes in my Rails app.
eval_form:
class EvalForm < ApplicationRecord
has_many :eval_forms_roles
has_many :roles, through: :eval_forms_roles
has_many :eval_forms_courses
has_many :courses, through: :eval_forms_courses
has_many :eval_responses
has_many :eval_reminders
belongs_to :user_detail
validates :formName, presence: true
validates :formData, presence: true
end
user_detail:
class UserDetail < ApplicationRecord
has_one :la_detail
has_many :eval_responses
has_many :eval_forms
end
So What's Wrong?
Lastly, here is the code to retrieve the objects from the database and the section where I'm getting my error.
My controller action:
def index
# list *all* existing evaluation forms, with options to filter by OpenDate, CloseDate, etc (todo)
#EvalForms = EvalForm.includes(:user_detail)
end
My view:
<td><%= ef.user_detail.DisplayName %></td>
My error:
NoMethodError in Evaluations::EvalForms#index
undefined method `DisplayName' for nil:NilClass
Extracted Source location: <td><%= ef.user_detail.DisplayName %></td>
Restating the problem
In conclusion, I'm really confused as to why the associated user_detail objects are not being retrieved despite my .includes() statement in the controller action. I'm pretty new to Ruby as well as Rails, but there are other sections in the application that look similar to this and work correctly so I don't see what my issue is.
I would start by using conventional naming which in Rails means snake_case everywhere:
class CreateUserDetails < ActiveRecord::Migration[5.2]
def change
create_table :user_details do |t|
t.string :edu_person_principal_name, unique: true
t.string :display_name
t.string :email
t.string :role, default: 'Student'
t.boolean :has_appointment, default: false # let the driver handle conversion
t.timestamps
end
end
end
class UpdateEvalForms < ActiveRecord::Migration[6.1]
def change
change_table :eval_forms do |t|
t.belongs_to :author_user_details, foreign_key: { to_table: :user_details }
t.integer :year # consider using `YEAR(4)` instead
t.string :semester
t.boolean :is_archived, default: false
end
end
end
If you continue using a strange mix of camelCase and PascalCase you will need to explicitly configure all your associations and you will lose all the advantages of convention over configuration. I would not recommend this at all unless you're stuck with a legacy database as its a surefire recipe for developer confusion and bugs.
You will also get a missing constant error if you call the PascalCase methods without an explicit recipient (self):
class EvalForm < ApplicationRecord
def short_description
# uninitialized constant Description (NameError)
Description.truncate(27, separator: ' ')
end
end
While you can fix this with self.Description.truncate(27, separator: ' ') its still very smelly.
In this case if you want to call the column author_user_details_id instead of user_details_id which is derived from the name you need to configure the assocation to use the non-conventional name:
class EvalForm < ApplicationRecord
belongs_to :user_detail, foreign_key: :author_user_details_id
end
class UserDetail < ApplicationRecord
has_many :eval_forms, foreign_key: :author_user_details_id
end
If the rest of your schema looks like this you'll have to do this across the board.

Rails rollback, change type and migrate again

I have a requests table;
class CreateRequests < ActiveRecord::Migration
def change
create_table :requests do |t|
t.string :from
t.string :to
t.timestamps null: false
end
end
end
I would like to rollback the database with rake db:rollback STEP = 5
destroy the Request model and create request table with;
t.datetime :from
t.datetime :to
However, I have a migration table to Request model on STEP = 2,
class AddStatusToRequest < ActiveRecord::Migration
def change
add_column :requests, :status, :string, :default => "Pending"
end
end
The problem is, if I destroy Request table and create new Request table with datetime types it creates after STEP = 2 and when I rake db:migrate rails does not add Status column to Request table. How can I overcome this?
You can create a new migration to change the column type using change_column:
command line: rails g migration change_request_to_from_column_types'
new migration:
class ChangeRequestToFromColumnTypes < ActiveRecord::Migration
def change
change_column :requests, :from, :datetime
change_column :requests, :to, :datetime
end
end
It's best not to retroactively change migrations after they've been applied to avoid state conflicts like the one you have. Perhaps the simplest solution is to create a new migration to add the types to the requests table using a guard to do nothing if the column already exists:
class AddStatusToRequestIfNotExists < ActiveRecord::Migration
def change
unless column_exists? :requests, :status
add_column :requests, :status, :string, :default => "Pending"
end
end
end
EDIT
You'll also want to make sure that you can run migrations from scratch, so you might want to update your existing migration to guard against a missing table as follows:
class AddStatusToRequest < ActiveRecord::Migration
def change
unless table_exists? :requests
add_column :requests, :status, :string, :default => "Pending"
end
end
end
Not ideal, but it's probably the safest approach.

"Can't write unknown attribute" to HABTM join table when using custom foreign key

A User can HABTM Games and a Game can HABTM Users as Storytellers, to differentiate them from a later relationship where a Game will have many Users as Participants.
Rails throws the error "can't write unknown attribute `user_id'" when I attempt to add the user-input User objects to #game.storytellers via the create method in the Games controller.
I'm pretty sure the problem lies in my last migration (at the bottom of this post).
models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :games, association_foreign_key: "storyteller_id"
end
models/game.rb
class Game < ActiveRecord::Base
has_and_belongs_to_many :storytellers, class_name: "User",
foreign_key: "storyteller_id"
attr_accessor :storyteller_group
end
controllers/games.rb
def create
#game = Game.new(game_params)
#game.storytellers << params[:game][:storyteller_group].split(",").collect { |n| User.find_by(name: n) }
respond_to do |format|
if #game.save
format.html { redirect_to #game, notice: 'Game was successfully created.' }
format.json { render :show, status: :created, location: #game }
else
format.html { render :new }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
views/games/_form.html.erb
<%= form_for(#game) do |f| %>
[snip]
<!-- Additional storytellers-->
<div class="field">
<%= f.label :storyteller_group, id: "create-game" %>
<div class="input">
<%= f.text_field :storyteller_group, value: current_user.name %>
</div>
</div>
[snip]
<% end %>
db/migrations/create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.index :name
t.string :email
t.index :email
[snip]
t.timestamps
end
end
end
db/migrations/create_games.rb
Class CreateGames < ActiveRecord::Migration
def change
create_table :games do |t|
t.string :name
t.text :description
t.string :source
t.timestamps
end
end
end
db/migrations/games_users.rb
class GamesUsers < ActiveRecord::Migration
def change
create_table :games_users, id: false do |t|
t.belongs_to :game, index: true
t.integer :storyteller_id, index: true
end
end
end
You have written your HABTM foreign keys definitions in just the opposite way. Quoting from Rails guides:
:association_foreign_key
By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to the other model is the name of that model with the suffix _id added. The :association_foreign_key option lets you set the name of the foreign key directly.
and
:foreign_key
By convention, Rails assumes that the column in the join table used to hold the foreign key pointing to this model is the name of this model with the suffix _id added. The :foreign_key option lets you set the name of the foreign key directly
So, the following should work:
class User < ActiveRecord::Base
has_and_belongs_to_many :games, foreign_key: "storyteller_id"
end
class Game < ActiveRecord::Base
has_and_belongs_to_many :storytellers, class_name: "User",
association_foreign_key: "storyteller_id"
end

UnknownAttributeError in ContactsController#create

This error is coming up after pressing submit on my form saving to db as I'm taking a course in Ruby. I have also already attempted rake:db migrate to no avail.
ActiveRecord::UnknownAttributeError in ContactsController#create
unknown attribute: comments
Extracted source (around line #7):
5
6
7
8
9
10
def create
#contact = Contact.new(contact_params)
if #contact.save
redirect_to new_contact_path, notice: "Message sent."
my contact controler.rb code
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
redirect_to new_contact_path, notice: "Message sent."
else
redirect_to new_contact_path, notice: "Error occurred."
end
end
private
def contact_params
params.require(:contact).permit(:name, :email, :comments)
end
end
My contact.rb
class Contact < ActiveRecord::Base
def name
end
def email
end
def comments
end
end
--------------
class CreateContacts < ActiveRecord::Migration
def change
create_table :contacts do |t|
t.string :name
t.string :email
t.text :commments
t.timestamps
end
end
end
In your migration file the column comments has 3m's (:commments) instead of two
def change
create_table :contacts do |t|
t.string :name
t.string :email
t.text :commments
t.timestamps
end
Now you have to change the column by creating a migration
How can I rename a database column in a Ruby on Rails migration?
Is :comments a db field in your Contact model? It should be for it to work in here:
def
contact_params params.require(:contact).permit(:name, :email, :comments)
end

How do I save images via options_for_select with Paperclip and Image Picker on Rails 4?

I'm trying to save images via a form that populates a "Select" field with a number of options. The select form uses Image Picker to change the options when an user clicks on a selected image. When the form is submitted, the selected image should be saved with the Paperclip gem.
I think I just need to point the image url from the selected option to a method that will open it in Paperclip, but I'm not quite sure how to do that. I'm new to Rails, and humbly appreciate anything that will point me in the right direction!
Here's what I have:
_form.html.erb
<%= f.select( :medium_id,
options_for_select(#story.fetched_images.collect{ |v,i| [v, #story.fetched_images.index(v), 'data-img-src' => v ] } )) %>
If I use ":medium", as I would normally do when setting the form input, I get an error that says "ActiveRecord::AssociationTypeMismatch at /stories
Medium(#70334728193840) expected, got String(#70334669736760)", so I have tried using ":medium_id", which displays the value of the selected option instead of the url.
Using ":medium_id" allows the rest of the form fields to save properly without an error, but only the option value is saved for the image, not the image itself.
story.rb
class Story < ActiveRecord::Base
belongs_to :medium
accepts_nested_attributes_for :medium
stories_controller.rb
class StoriesController < ApplicationController
def new
#title = "Submit Story"
#cur_url = "/stories/new"
#story = Story.new
#story.build_medium
end
private
def story_params
p = params.require(:story).permit(
:title, :url, :description, :moderation_reason, :seen_previous,
:merge_story_short_id, :is_unavailable, :medium, :medium_id, :tags_a => [], medium_attributes: [:medium, :image],
)
medium.rb
require "open-uri"
class Medium < ActiveRecord::Base
has_one :story
before_save :image_from_select
def image_from_select(url)
self.image = open(url)
end
end
media_controller.rb
def create
#medium = Medium.new(medium_params)
respond_to do |format|
if #medium.save
format.html { redirect_to #medium, notice: 'Medium was successfully created.' }
format.json { render action: 'show', status: :created, location: #medium }
else
format.html { render action: 'new' }
format.json { render json: #medium.errors, status: :unprocessable_entity }
end
end
end
private
def set_medium
#medium = Medium.find(params[:id])
end
def medium_params
params.require[:medium].permit(:image)
end
end
schema.rb
create_table "stories", force: true do |t|
t.integer "medium_id"
end
create_table "media", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "story_id"
end
I have tried referencing the following without success:
Save image from URL by paperclip
Which is the proper way to upload an image from an external url with Paperclip on rails 4?
http://runnable.com/UnsMH7fYN2V6AAAT/how-to-save-images-by-url-with-paperclip-for-ruby-on-rails-and-upload
https://github.com/thoughtbot/paperclip/wiki/Attachment-downloaded-from-a-URL
Thanks in advance.