I have the following models
class Book < ActiveRecord::Base
has_many :chapters
end
and
class Chapter < ActiveRecord::Base
belongs_to :book
end
in /chapters/edit/id I get
undefined method `book' for #<ActiveRecord::Relation:0x0000010378d5d0>
when i try to access book like this
#chapter.book
Looks like #chapter is not a single Chapter object. If #chapter is initialized something like this:
#chapter = Chapter.where(:id => params[:id])
then you get a Relation object (that can be treated as a collection, but not a single object). So to fix this you need to retrieve a record using find_by_id, or take a first one from the collection
#chapter = Chapter.where(:id => params[:id]).first
or
#chapter = Chapter.find_by_id(params[:id])
As the others have said - adding the .first method will resolve this. I have experienced this issue when calling a #chapter by it's unique ID. Adding .first (or .take in Rails 4) will ensure only one object is returned.
Try: Chapter.find(params[:id]).first
I got a similar error
Books.chapters
NoMethodError: undefined method `chapters' for #<Book::ActiveRecord_Relation:0x00007f8f9ce94610>
what I needed was:
Books.includes(:chapters)
Related
When creating objects through a has_many association like User.first.books.create!(...), the new book gets the user_id automatically from the association.
Is there any way to get that user_id if I call my own create method? i.e. User.first.books.own_create_method
def self.own_create_method
# how to get the user object?
end
Thanks!
To define User.first.books.own_create_method you would use:
def self.own_create_method
book = build
# something custom you want to do with book
book.save
end
self. allows you to define class methods in Ruby.
Digging into ActiveRecord new method, I found that you can call scope_attributes and you'll get a hash with all the attributes that are scoped.
def self.own_create_method
attributes = scope_attributes
# attributes["user_id"] would be the user_id that is scoped by
...
end
Not sure if this is a good practice though...
I have such two models:
class Article < ActiveRecord::Base
belongs_to :articles_type
end
class ArticlesType < ActiveRecord::Base
has_many :articles
end
and in controller i write:
#articles = Article.where(article_type_id: params[:id])
and in view(haml) i try:
= #articles.articles_type.id
= #articles.articles_types.id
= #articles.first.articles_type.id
= #articles.first.articles_types.id
how could i display this articles_type.id but for only first row?
now i get
undefined method `articles_type'
but why? what i do wrong? how to display nested model id?
#articles will be a collection of items, not just a single one (because you used the where method). You will have to do:
#articles.first.articles_type_id
(Also note that you don't have to do .articles_type.id, because the #articles.first already has the ID of the type)
The undefined method message is because #articles does not have an articles_type method. You have to get to a single instance of an Article in order to use that method. You could do that with a call to #articles.first or by iterating on the collection.
= #articles.first.articles_type.id
is the line you want to use.
Looks like you've got your logic backwards.
Based on your models, an article belongs to an article_type.
#articles.first.article_type.id
# OR #articles.first.article_type_id
Just looks like you're incorrectly pluralizing .article_types when it should be .article_type.
I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations
I have the following models:
class Post < ActiveRecord::Base
has_and_belongs_to_many :countries
end
class User < ActiveRecord::Base
has_many :entitlements
has_many :countries, :through => :entitlements
end
Posts on the Post index page must have at least one country that is the same as one of the Users' countries.
I have tried various scopes in my models and lengthy controller code but I can't figure out how to check what should be a simple relationship: whether at least one item in Post.countries exists in User.countries.
Any help greatly received.
UPDATED:
Ok, so I've got the following in my controller:
def index
#user = current_user
#user.countries.each do |user_country|
#user_country_posts += Country.find(user_country.id).posts
end
#posts = #user_country_posts
end
Which is iterating through the user.countries and finding each post for those countries. But when I run it I get:
NoMethodError: undefined method `+' for nil:NilClass
Any ideas what I'm doing wrong?
The problem is that you're trying to use the #user_country_posts instance variable which was not defined before, so its value is nil.
At the line:
#user_country_posts += Country.find(user_country.id).posts
You're actually calling the + method on the #user_country_posts variable, which is equivalent therefore with calling + on a nil.
Try to initialize the variable in the beginning of the method, like:
#user_country_posts = []
I would also consider using ruby's union approach:
ie:
[1,2,4] & [1,4,5]
=> [1,4]
So if you have list of user countries and a list of post countries then maybe the below would work:
ie:
#shared_country_ids = #user.countries.map(&:id) & #post.countries(&:id)
From your update above what it seems like you want to do is show all posts that have one of the user's country codes. If that's the case I would do the below:
ie:
#posts = Post.where(:countries => #user.countries)
The above should work assuming you configured the relationships correctly.
Consider a simple example, where we have 2 models, Article and Category.
class Article < ActiveRecord::Base
belongs_to :category
def self.search(title)
where(:title => title)
end
end
class Category < ActiveRecord::Base
has_many :articles
end
Now, on the rails console :
Article.all.search('test article')
As expected returns an error
NoMethodError: undefined method `search' for #<Array:0x9aa207c>
But when I do this
Category.first.articles.search('test article')
returns a set of records!
This prompts a check on the classes of
Article.all
and
Category.first.articles
both returning the Array class.
Obviously, Article's class method 'search' is being inducted in run time and prompting a 'correct' return of records when accessed through its association (Category) but behaving otherwise when accessed by the class itself (Article).
So, What's happening?
This is because when you perform .all the query is actually executed, so the object returned will be a basic array. However, when you perform Category.first, it will return a Category object, then articles is actually making use of ActiveRecord::Reflection::AssociationReflection and doing an extension of an Array. For example, in rails c try:
Category.first.articles.ancestors
vs
Category.first.articles.all.ancestors #throws an error
The second one throws an error, because at this point the object is just a simple Array. The first, however, is made up of something like this:
Article(...), ActiveRecord::Base, ActiveRecord::Reflection, Object (and so on)
As another example, try this:
a = Category.first.articles; ObjectSpace.each_object(Class).select {|k| a < k }
#=> [Object, BasicObject, ActiveRecord::Base]
You can see that although it looks like an Array, it's inheriting those other classes.
Article.all
This returns an array that's giving you the error as you saw, you're trying to run a class function of Article on the Array class.
I'm not sure what exactly you're trying to accomplish but you could do this, I think
Article.search("soem content")
However, I'm not sure if that would return a result, as it might just return an