ActiveRecord::UnknownAttributeError when using STI models in Rails - ruby

I made the following models:
class Request < ActiveRecord::Base
end
class UrgentRequest < Request
has_one:note
end
class Note < ActiveRecord::Base
attr_accessible :request_id,....
belongs_to :urgent_request, :foreign_key=>'request_id', :class_name=>'Request'
end
In my controller I've set up an action to create an UrgentRequest object:
def new_scheduled_request
#request = UrgentRequest.new
#request.build_note #<-- getting error here
respond_to do |format|
format.html # new.html.erb
format.json { render json: #request }
end
end
I'm getting the following error:
ActiveRecord::UnknownAttributeError in RequestsController#new_urgent_request
unknown attribute: urgent_request_id
The line number is where I'm invoking the build_note call. The form on the page is supposed to be a nested form. What's going on here and how can I fix it?

Uh never mind I found out the issue. Apparently I had to explicitly mention in the UrgentRequests model in the has_one:note association the foreign key and class name parameters. Works fine now!

Related

Undefined local variable or method for class

What did I miss?
ERROR: undefined local variable or method `feedback' for #<#<Class:0x007f66dc8dca30>:0x007f66dc8cee80>
Migration:
class CreateFeedbacks < ActiveRecord::Migration
def change
create_table :feedbacks do |t|
t.references :user
t.text :body
t.timestamps
end
end
end
Model:
class Feedback < ActiveRecord::Base
attr_accessible :body, :user
belongs_to :user
end
Controller:
class FeedbacksController < ApplicationController
before_action :authenticate_user!
def index
#feedback = Feedback.all
end
def new
#feedback = Feedback.new
end
def create
#user = User.find(params[:user])
#feedback = #user.feedbacks.create(params[:feedback])
respond_to do |format|
if #feedback.save
format.html { redirect_to #user, notice 'Comment was successfully created.' }
format.json { render json: #feedback, status: :created, location: #feedback }
else
format.html { render action: "new" }
format.json { render json: #feedback.errors, status: :unprocessable_entily }
end
end
end
User model:
has_many :feedback
Routes:
resources :feedbacks
resources :users do
resources :feedbacks
end
Firstly, it helps a lot to style your question and code correctly as well as mark your files (routes.rb, controllers, models correctly). Else it would be very difficult for readers to understand your question and spot issues with your code. You can refer to https://meta.stackoverflow.com/editing-help for the styleguide/ markdown.
Secondly, it looks like your routes.rb are incorrect - but again, this could be due to your formatting. Based on your question, it looks like your routes are:
resources :feedbacks
resources :users do
resources :feedbacks
end
end
When it should be:
resources: users do
resources: feedbacks
end
Thirdly, your seem to have a typo in your model (feedback instead of feedbacks. That is:
class User < ActiveRecord::Base
has_many :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :user
end
Lastly, do add specifics and what exactly you ran that caused the error. E.g. did you run something in console, what page did you load?

Undefined Method 'add_product ' for model object

I think my error is kind of silly, but it's keeping me stuck. I'm following the Agile Web Development with Rails 4 and developing the app Depot from it. I'm getting a
undefined method `add_product' for #<Cart:0x007f2ee4cfb8f8>
error. My code is as follows,
class LineItemsController < ApplicationController
def create
find_cart
product = Product.find(params[:product_id])
byebug
#line_item = #cart.add_product(product.id) // line with error
#line_item.product = product
respond_to do |format|
if #line_item.save
format.html {redirect_to #line_item.cart,
notice: 'Line Item was succesfully created'}
format.json {render json: #line_item,
status: :created, location: #line_item}
else
format.html {render action: "new"}
format.json {render json: #line_item.errors,
status: "Unprocessable Entry"}
end
end
end
end
Cart.rb
class Cart < ActiveRecord::Base
has_many :line_items, dependent: :destroy
def add_products(product_id)
current_item = line_items.find_by_product_id(product_id)
if current_item
current_item.qunatity += 1
else
current_item = line_items.build(product_id: product_id)
end
current_item
end
end
Also, I want to know, how can a method from different model be directly called into a separate controller ?
The object cart has value, I have debugged to make sure, as well as the error line also has the object present.
Thanks for your help.
I found the mistake, it was a typo I made in Cart.rb. I had named the method add_product's' and was calling add_product in the controller.

Validation not working on Class that extends ActiveModel::Naming, include ActiveModel::Validations

I and new to ruby and rails; I have the below ruby class definition that I am using in my rails 3 app. This class is simply used as a property container for contact information populated in my view on submit (form_for). I read a post where you can use ActiveModel directly apart from ActiveRecord, to perform validation, so I am trying it. I am getting the following exception when I check to see if the the object is valid? in my controller on postback. I assumed that valid? would be available being that I included ActiveModel::Validations; perhaps I am doing a few other things a$$ backwards. Any help would be appreciated:
undefined method `valid?' for #
Here's my class definition, further down is how I am handling it in my controller action:
require 'active_model'
class ContactModel
extend ActiveModel::Naming
include ActiveModel::AttributeMethods
include ActiveModel::Validations
include ActiveModel::Conversion
validates_presence_of :first_name, :last_name, :email_address, :email_address_confirmed, :subject, :contact_message
attr_accessor :first_name, :last_name, :email_address, :email_address_confirmed,
:telephone_number, :subject, :contact_message
Just messing around testing.
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == z
end
...
end
In my controller/action...
def send_email
##contact_model = ContactModel.new().initialize_copy(params[:contact_model])
#contact_model = params[:contact_model].dup
respond_to do |format|
if (#contact_model.valid?)
# Tell the UserMailer to send a welcome Email after save
ContactMailer.contact_email(#contact_model).deliver
format.html { redirect_to(#contact_model, notice: 'Email successfully sent.') }
format.json { render json: #contact_model, status: :created, location: #contact_model }
else
# What to do here?
end
end
end
In your controller you are setting #contact_model to a hash, params[:contact_model], and then calling valid? on it. You need create an instance of ContactModel and call valid on that. Like so:
#contact_model = ContactModel.new(params[:contact_model])
if (#contact_model.valid?)
...
I see commented out code that calls ContactModel.new(), but that's not how you want to do it anyway. Also, there is no reason to dup() or initialize_copy() on the params stuff.

How to select appropriate Association in Rails?

I'm trying to learn Rails by creating a very simple application which just creates a website where someone can create a list of authors and books with an association that the book is written by an author. I was hoping this would be simple and DRY, but I've been having an unexpected amount of trouble with it.
First looking at my models, I've set up the association and made every data point required (author.name, book.title, and book.author). I do not want to add :author or :author_id to the attr_accessible lists because I want to use the appropriate Rails conventions.
app/models/author.rb:
class Author < ActiveRecord::Base
attr_accessible :name
validates_presence_of :name
has_many :books
end
app/models/book.rb:
class Book < ActiveRecord::Base
attr_accessible :title
validates_presence_of :title
belongs_to :author
validates_associated :author
end
The books controller and view I think is exactly from the scaffolding and very uninteresting. What is interesting is the books controller. Looking at the new method, all I did was add a collector which gets the array of author names with ids to pass to the view. (Honestly, I think I would prefer to not pass the id at all.)
app/controllers/books_controller.rb
# GET /books/new
# GET /books/new.json
def new
#book = Book.new
#authors = Author.all.collect {|a| [a.name, a.id]}
respond_to do |format|
format.html # new.html.erb
format.json { render json: #book }
end
end
Now over to the views, I used the default new.html.haml, but made changes to _form.html.haml. I added a select field using the values in #authors.
app/views/books/_form.html.haml
= form_for #book do |f|
- if #book.errors.any?
#error_explanation
%h2= "#{pluralize(#book.errors.count, "error")} prohibited this book from being saved:"
%ul
- #book.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :author
= f.select(:author, #authors, {:include_blank => ""})
.actions
= f.submit 'Save'
Lastly, back to my controller for the create method. I try to save the basic parameters and create an author association from the selected author.
app/controllers/books_controller.rb
# POST /books
# POST /books.json
def create
#book = Book.new(params[:book])
#book.author = Author.find_by_id(params[:author])
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render json: #book, status: :created, location: #book }
else
format.html { render action: "new" }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
When I click 'Save' I get the following error:
Can't mass-assign protected attributes: author
I understand that this is because the value I selected was put in params[:book] instead of params[:author]. So I have two questions.
1) How do I fix my select statement to have it send in params[:author] instead of params[:book]?
2) Is there an even better way to do this that completely hides the id association?
"Can't mass-assign protected attributes: author" means that your attribute isn't listed as attr_accessible in your model
I think I've mostly figured it out, and as I suspected, I didn't need to change my model code at all.
I changed my controllers new method definition of #authors to only return author names with the following:
#authors = Author.pluck(:name)
This accomplished my goal of hiding the id though it's probably a tad slower when I need to search by name instead of id in the controller create method (below).
Next, I fixed my views to set params[:author] instead of params[:book][:author].
= label :author, :name, 'Author'
= select_tag(:author, options_for_select(#authors), {:include_blank => ""})
Finally, I changed the new methods creation of the #book:
#book = Author.find_by_name(params[:author]).books.create(params[:book])
I'm fairly happy with this. The only thing I don't really like is that the label creates an "author_name" label instead of simply "author".

Rails Find with Active Resource Ignoring Parameters

I have two Rails apps, one client using ActiveResource and one service. I am testing the following command via the console:
User.find(:all, :params => {:email_address => "myemail#domain.com"})
I get back all the records in my user table and not just the one specified in my email parameter.
When I go look at the log for my service app it shows as follows
Started GET "/users.json?email_address=myemail%40domain.com" for 127.0.0.1 at 2011-12-29 11:29:06 -0600
(0.4ms) SHOW search_path
Processing by UsersController#index as JSON
Parameters: {"email_address"=>"myemail#domain.com"}
User Load (0.7ms) SELECT "users".* FROM "users"
Completed 200 OK in 40ms (Views: 35.7ms | ActiveRecord: 3.3ms)
My parameter was not included in the SQL statement.
Any insight?
Based on your comment on the original question, your controller still looks like this:
class UsersController < ActionController::Base
def index
#users = User.all
respond_to do |format|
format.json { render json: #users }
end
end
#other methods down here
end
But in order to get your index view to render just the ones that match the email, you need to update the index method to:
class UsersController < ActionController::Base
def index
#users = User.find_all_by_email_address(email_address)
respond_to do |format|
format.json { render json: #users }
end
end
#other methods down here
end
Conditions are passed to ActiveRecord find method via :conditions option and not :params. So your find call should be like that:
User.find(:all, :conditions => { :email_address => "myemail#domain.com" })
or in more Rails 3 style:
User.where(:email_address => "myemail#domain.com").all
You can find full list of available parameters for find method in docs.

Resources