I have a model with a belongs to relationship.
class Product < ActiveRecord::Base
attr_accessible :name, :price, :request_id, :url
# Relationships
belongs_to :request
end
class Request < ActiveRecord::Base
attr_accessible :category, :keyword
# Relationships
has_many :products
end
This is the code in my controller function
product = Product.where({ :asin => asin }).first
# See if the product exists
begin
#This throws a method not found error for where
product = Product.where({ :name => name }).first
rescue
Product.new
# This throws a method not found error for request_id
product.request_id = request.id
product.save
end
I'm trying to create a new product object like so
product = Product.first(:conditions => { :name => name })
When I call that I get an error saying undefined method 'first' for Product:Class
I tried doing Product.new and I can't access any attributes. I get this for every one undefined method 'request_id=' for #<Product:0x007ffce89aa7f8>
I've been able to save request objects. What am I doing wrong with products?
EDIT:
So as it turns out there was an old Product data type that was being imported that wasn't an ActiveRecord class. It was using that instead of my Product::ActiveRecord. I deleted that import and it's good to go. Sorry to have wasted everybody's time.
Not sure what the proper protocol is here for what to do with this question.
Is your Product class an ActiveRecord::Base class? You can find out by running:
Product.ancestors.include?(ActiveRecord::Base)
If this returns false, it's getting the class loaded from somewhere else.
First check to see that your Product class is set up correctly by typing in:
rails c
# after console has loaded
Product
If this looks correct then we will try to instantiate a product by calling:
# Create a new product
product = Product.new(name: "first product", price: 100, url: "http://www.example.com")
# Persist this object to the database
product.save
If you are missing any attributes run another migration to add them to the Product table.
If none of those suggestions work, check to make sure that there isn't an existing class with the same name in your project. This would cause all kinds of errors and would explain certain methods not being found.
Related
From inside edit action in the contacts controller have ...
#programs << #contact.program
Which produces the following error:
NoMethodError - undefined method `<<' for Program::ActiveRecord_Relation
Contacts Model:
belongs_to :program
Program Model:
has_many :contacts
validates :name, presence: true, uniqueness: true
#programs.class
Program::ActiveRecord_Relation
#contact.program.class
Program(id: integer, name: string, active: boolean, created_at: datetime, updated_at: datetime)
Question: Why does this operation fail? Why can't the record get added to the record collection. What is preventing the collection(ActiveRecord_Relation) from adding the record?
You're contradicting yourself here:
Program has_many contacts vs Programs << Contact.program
If you're trying to add a Contact to a particular program, you would be looking at adding the contact:
program.contacts << contact
And if you're trying to set the program for the contact:
contact.program = program
What does not make sense, however, is to try to add something to “programs”, which isn't a relationship. Nothing in this system as you've defined it has_many :programs, so #programs.<< cannot possibly act on a relationship.
You're receiving this error because the ActiveRecord::Relation class is only a collection of results returned by an ActiveRecord query. You probably got it by running Program.where or a similar query. It is not an ActiveRecord::Association and therefore you cannot add more records to it.
You must instead use the association returned by the parent object.
Here's an example of what you're doing, vs what you should be doing:
class User < ApplicationRecord
has_many :programs
end
class Program < ApplicationRecord
belongs_to :user
end
new_program = Program.new
# What you're attempting.
programs_where = Program.where(user_id: User.first) # Class is Program::ActiveRecord_Relation
programs_where << new_program # Throws Error b/c << is not available on ActiveRecord::Relation objects.
# What you should be attempting.
user = User.first
programs_assoc = user.programs # Returns Programs::ActiveRecord_Associations_CollectionProxy
programs_assoc << new_program # Returns Correctly
Note: It's not clear how #programs is defined. Is this answer does not work for you then please provide the complete controller code, as well as the other model you're using.
I have a class student with has_many tests. The test class has a student_id, marks, name. Here the test name should be unique. The test is a nested attribute for student. So the parameters are this way:
:student => {:first_name => "abc",
:email => "dfsdf#sfdsdsd.bbb",
:tests_attributes => { "0" => {:name => "bgc", :marks => "470"}}}
I have a problem with update. If I update_attributes with the tests_attributes, it throws a validation error saying the name for test is not unique. I am actually addressing the same record here. How do I overcome this?
Without seeing your models (& validations), it's going to be quite difficult to diagnose your error directly.
--
Nested Attributes
We've done something like this, and found that your nested data is passed to the child model as if it were receiving a new object (without being nested). This means if you've got validates uniqueness for that model, it should be okay:
#app/models/test.rb
Class Test < ActiveRecord::Base
belongs_to :student
validates :name, uniqueness: true
end
Reason I write this is because there's a method called inverse_of, which basically allows you to access the parent model data in your child model
--
Update
I think the problem will likely lie with your use of update_attributes. Problem being you're trying to update both the student and the test attributes at one time.
I'm not sure exactly why this would be a problem, but I'd test this:
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def update
#student = Student.find params[:id]
#student.test.update(name: params[:test_name], marks: params[:marks])
end
end
I think if you can explain your methodology a little more, it will be much more helpful. I.E are you trying to update student or test? If you're updating student & adding a new test, how are you updating the studet?
Thanks for the reply guys. I ended up finding the answer myself. I did have a uniqueness validation for name.
I had a situation where initially I wouldn't know the student but have only his details. So I would have to create this hash and pass it to update. The trick to not trying to create a new record for the same name in test is to pass the actual record's ID along with it. This solved the problem
Nested Attributes
I think the problem with nested_attributes. For update need to pass nested_attributes with ID.
Ex.
:student => {:first_name => "abc",
:email => "dfsdf#sfdsdsd.bbb",
:tests_attributes => { "0" => {id: 1, :name => "bgc", :marks => "470"}}}
I have tried below-given example it is worked for me:
Update
#app/controllers/students_controller.rb
class StudentsController < ApplicationController
def update
#student = Student.find params[:id]
#student.update_attributes(student_params)
end
private
def student_params
params.require(:student).permit(:first_name, :email,
tests_attributes: [:id, :name, :marks])
end
end
I have the following two models:
class Product < ActiveRecord::Base
belongs_to :shop
validates_numericality_of :price, :greater_than_or_equal_to => 0
end
class Shop < ActiveRecord::Base
has_many :products
validates_presence_of :name
end
Here is the create method of my ProductsController:
def create
if params[:product][:shop_id] == "new_shop"
#shop = Shop.find_by_name(params[:new_shop]) || Shop.create(:name => params[:new_shop]) # Is there a simpler method to do this ?
params[:product][:shop_id] = #shop.id
end
#product = Product.new(params[:product])
if #product.save
redirect_to(:action => 'index')
else
render('new')
end
end
When user adds a new product he has a select box to choose the shop. The last option in this select box lets user to add a new shop (an additional input text field appears). The value of this last option is new_shop.
If the validation of the new entered shop fails, I would like the validation of the product to fail and display an appropriate error (currently an error displayed only if the validation of the product itself fails).
What would be the most "Rails 3 method" to achieve this ?
I think it would be simpler if you use accepts_nested_attributes_for. So to your Product model add:
accepts_nested_attributes_for :shop
And then in view depending on your select list value you can modify form (in js), so there will be either shop_id field or a whole set of fileds for a shop:
<% f.fields_for :shop do |sf| %>
...
<% end %>
Then if user selects existing shop, it will only pass shop_id, but if users selects new shop, then form will pass also new associated object.
If you want shop name to be unique, then just add validates_uniqueness_of to Shop model.
If validation of a shop fails, then product won't be saved. Basicaly, your controller stays as simple as it could be (just creating new product object from params - you don't care about shop there).
I agree with #klew, you should probably be using accepts_nested_attributes_for.
But, the simple and direct answer to your question is to use validates_associated.
Also, the nicer way of doing:
#shop = Shop.find_by_name(params[:new_shop]) || Shop.create(:name => params[:new_shop])
would be:
#shop = Shop.find_or_create_by_name(params[:new_shop])
i have table AuditLog with fields including: audited_id | audited_type
That results in data like:
108 | Photo
303 | Comment
What I want to do is create a link to the item, so for the example above:
here is the photo
I'm trying to use a polymorphic_path but am getting an error: "undefined method `model_name' for Fixnum:Class"
When using:
<%= link_to 'Here she is', polymorphic_path([audited_id, audited_type]) %>
Ideas? Thanks
Updating with code based on the answer by Luke below:
class NewsFeed < ActiveRecord::Base
default_scope :order => 'news_feeds.created_at DESC'
belongs_to :feeded, :polymorphic => true
end
class Note < ActiveRecord::Base
has_many :newsfeed, :as => :feeded
end
In the partial which is being passed the local storyitem:
<%= link_to 'Here she is', polymorphic_path(storyitem.feeded) %>
The DB migration file, contains the following line for CreateNewsFeeds
t.references :feeded, :polymorphic => true
You should have a method #auditable (or whatever your polymorphic association is called) on AuditLog objects. If you pass the result of that method to polymorphic_path it will return the correct path for you.
Update:
Assuming you have the following associations (or are using acts_as_auditable or something that sets up the relationships for you):
class AuditLog
belongs_to :auditable, :polymorphic => true
end
class AuditedObject
has_many :audits, :as => :auditable
end
You'll be able to call auditable on any instance of AuditLog, and it will return the associated audited object. So you can call
<%= link_to 'Here she is', polymorphic_path(audit_log.auditable) %>
to get a link to the audited object.
So, anywhere that you have a polymorphic association in a class, there is an instance method setup with the name of that association that will return the associated object.
Gosh, I'm hoping that makes sense. Let me know if you need me to clarify it further.
The problem with polymorphic_path it needs an object, so you first need to fetch the object from the database.
Depending on your use case this can be a big performance problem.
In case of a log viewer, where you have a list of for example 100 entries,
and just want to show links to the entires, you will fetch 100 objects, just to get their path.
I had a similar problem, my solution was to hack a method to construct the path from the class name and id:
class AuditLog
...
def audited_path
"/#{self.audited_type.tableize}/#{self.audited_id}"
end
The method will return for example "/photos/302". But of course it will work only for quite simple routings.
It seems that when a child object has a reference to its parent in its setter, it fails to get initialized unless the foreign key is given first in the parameter hash.
class Bot < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :bot
def name=(text)
write_attribute(:name, "#{self.bot.name}'s #{text}")
end
end
Item.new(:name => 'pitchfork', :bot_id => 1, ... )
# => undefined method `name' for nil:NilClass (NoMethodError)
Item.new(:bot_id => 1, :name => 'pitchfork', ... )
# => #<Item id: nil, bot_id: 1, name: "r2d2's pitchfork", ... >
Note that the order of hash keys is preserved in Ruby 1.9, but the point is, bot_id must be set before the accessor that has a reference to its parent.
So, the following code works too:
item = Item.new
item.bot_id = 1
item.attributes = { :name => 'pitchfork', ... }
What's really annoying is that the build method on has_many collection doesn't work either, which I think is the right place to patch if I'd have to.
Bot.find(1).items.build(:name => 'pitchfork')
# => undefined method `name' for nil:NilClass (NoMethodError)
What's the best idea to get around this, or patch this, or am I doing anything wrong here?
You could move the string merging to an after_update callback. That way you won't have to access the Bot model until after it's properly setup.
However, I would probably keep name as a simple string and then add a virtual attribute for the merged string. That way it's also updated if the name of Bot is changed.
class Item < ActiveRecord::Base
belongs_to :bot
def full_name
#full_name ||= "#{bot.name}'s #{name}"
end
end