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.
Related
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.
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)
Not sure on my Ruby syntax here.
I want to define a method that I can call like this: client.invoices.average_turnaround. So my average_turnaround method needs to work with a collection of ActiveRecord objects.
Here's my code thus far:
class Invoice < ActiveRecord::Base
...
def self.average_turnaround
return self.map(&:turnaround).inject(:+) / self.count
end
end
So I'm trying to find the sum of the turnaround times for each invoice, then divide it by the total number of invoices.
Ruby is complaining that there is no map method defined for Class. I was expecting self to be an Array.
How do I write a method that works on a collection of Invoices and uses the map function? Where am I going wrong?
If you want to use map within the class method as opposed to through an association extension. For example if it would be useful to call Invoice.average_turnaround directly or Invoice.where(x: y).average_turnaround. Place all. in front of map.
class Invoice < ActiveRecord::Base
...
def self.average_turnaround
all.map(&:turnaround).inject(:+) / all.count
end
end
Use average_turnaround using any collection.
You defined a class method, which is called on the class itself. What you need is an association extension. The method should be defined on your client model like this:
class Client < ActiveRecord::Base
has_many :invoices do
def average_turnaround
return map(&:turnaround).inject(:+) / count
end
end
(Warning: Clueless Rails Newbie!)
In my show.html.erb for my albums view, I call a public method in my albums controller:
<% albums_feature = find_albums_with_feature(feature.id) %>
It generates a NoMethodError.
So I copied the method into my Album model and tried calling it from the view as:
<% albums_feature = Album.find_albums_with_feature(feature.id) %>
But this also gets a NoMethodError.
Where should I define this method?
For what it's worth, the method looks like this:
def find_albums_with_feature(feature_id)
albums_for_feature = Albums.find_by_sql(
["select al.* from albums al, albums_features alfe
where al.id = alfe.album_id
and alfe.feature_id = ?", feature_id])
end
If you want to have method that is accesible from view, you have few options:
put it in the model
put it in the helper
put it in the controller and add a line "helper_method :find_albums_with_feature"
But I think you can do it better. Firstly, don't put any finding methods in view. Put it in the controller. Secondly, you don't need to specify your own finding method. Probably you have something like this in your models:
class Album << ActiveRecord::Base
has_many :albums_features
has_many :features, :through => :albums_features
end
class AlbumsFeature << ActiveRecord::Base
belongs_to :album
belongs_to :feature
end
class Feature << ActiveRecord::Base
has_many :albums_features
has_many :albums, :through => :albums_features
end
With it, you can find albums with specific feature like this:
#feature = Feature.find(id)
#albums = #feature.albums
or
#albums = Feature.find(id).albums
and it should be in your controller. In view you should only display results.
If you are looking for more informations about associations, take a look here: http://guides.rubyonrails.org/association_basics.html. I think it's the best place where you can learn about Rails - at least it is for me.
In the Album model. Needs self in front though:
def self.find_albums_with_feature(feature_id)