Rails association works when it doesnt use an instance variable - ruby-on-rails

I'm getting the strangest error I've seen in Rails so far. In my view, I can print out the email associated with a painting if I find the record directly (e.g. Painting.find(15). But if I try to use an instance variable it errors (e.g #painting).
views/paintings/show.html.erb
<%= Painting.find(15).artist.user.email %> # works
<%= #painting.artist.user.email %> # Error: "undefined method 'user' for nil:NilClass"
controllers/paintings_controller.rb
def show
#painting = Painting.find(15)
end
Models: "users", "artists", "paintings".
A user can be an artist. So a user has_one artist.
An artist has_many paintings.

I think you should add associations. This how they should look like:
class User < ActiveRecord::Base
has_one :artist # it's ok
end
class Artist < ActiveRecord::Base
belongs_to :user # add this
has_many :paintings
end
class Painting < ActiveRecord::Base
belongs_to :artist
end
For me both cases works with this associations.

Use
def show
#painting = Painting.find(15).last
end
Currently the second one is returning a 1 element array, but in order to call a dependent method, you must specify 1 item.

Related

ActiveRecord default value if attribute is nil

Looking for a cleaner way to set a default value if attribute is not set yet or has been deleted, and returns nil.
class Category < ActiveRecord::Base
has_and_belongs_to_many :restaurants
belongs_to :picture
def set_picture
if self.picture.nil?
Picture.default_pic
else
self.picture
end
end
end
class Picture < ActiveRecord::Base
belongs_to :review
def self.default_pic
Picture.new(url: "/assets/default.jpg")
end
end
# index.html.erb
<%= image_tag category.set_picture.url %>
categories has many restaurants, and restaurants has many reviews. Reviews has one to one picture. category should be allowed to select from one of its associated pictures, or defaults to image in assets folder.
The #set_picture needs to get refactored out. Hopefully to a callback of some type:
class Category < ActiveRecord::Base
belongs_to :picture, defaults_to: Picture.default_pic
end
Is there a callback that does the above? Can I create one? Or is my framework wrong?
I think you could just override the accessor and call super. If this returns nil then you could return your default picture:
class Category < ActiveRecord::Base
belongs_to :picture
def picture
super || Picture.default_pic
end
end

return the count for element in a has_many relationships

i have 2 models one is listing and user
user has_many listings
listing belongs_to user
i have a view setup , i want to display for each user their own listings count ,i try this code :
<% User.all.each do |user| %>
<%= user.listings.count %>
<% end %>
i want to grab the listing count for each user . i found a bunch of solution here , all return the loop .other solutions i tried is to create a class method .
def count_listings
Listing.where(:user_id => user.id).count
end
try to call this way <%= User.count_listings%> it doesn't work .
for some reason there something i'm missing ,can't quite figure it out .
The :counter_cache option can be used to make finding the number of belonging objects more efficient. Consider these models:
class Order < ActiveRecord::Base
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_many :orders
end
With these declarations, asking for the value of #customer.orders.size requires making a call to the database to perform a COUNT(*) query. To avoid this call, you can add a counter cache to the belonging model:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
has_many :orders
end
With this declaration, Rails will keep the cache value up to date, and then return that value in response to the size method.
Although the :counter_cache option is specified on the model that includes the belongs_to declaration, the actual column must be added to the associated model. In the case above, you would need to add a column named orders_count to the Customer model. You can override the default column name if you need to:
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: :count_of_orders
end
class Customer < ActiveRecord::Base
has_many :orders
end
Counter cache columns are added to the containing model's list of read-only attributes through attr_readonly.
source: Rails guide on associations
..scroll down to options of belongs_to
If all you need is what you show in the example you can do it better as follows
<% Listing.group(:user_id).count.each do |user, count| %>
<%= "user: #{user} has #{count} listings" %>
<% end %>
This does a single query to the database and fetches only what you need.
SELECT COUNT(*) AS count_all, user_id AS user_id FROM `listings` GROUP BY user_id
and returns a hash like:
{
1: 123,
2: 231
}
#{ user_id: count }

Is there a Rails active record method I can call to get all #images?

I have these two models:
class Article < ActiveRecord::Base
has_many :images
end
and
class Image < ActiveRecord::Base
belongs_to :article
end
To get all my articles I simply do this:
#article = Article.all.find(params[:id])
Then I do the following to get all images this article has:
#images = Image.where article_id: #article.id
Is there some active record method that I can call to get all my #images, rather than what I have above? I just wonder if there is a better way than what I am doing.
I tried #article.images but it only returned the relation without the data.
Try this :
#article.images.all

Retrieving model attribute from table+column name

Let's say you have the following models:
class User < ActiveRecord::Base
has_many :comments, :as => :author
end
class Comment < ActiveRecord::Base
belongs_to :user
end
Let's say User has an attribute name, is there any way in Ruby/Rails to access it using the table name and column, similar to what you enter in a select or where query?
Something like:
Comment.includes(:author).first.send("users.name")
# or
Comment.first.send("comments.id")
Edit: What I'm trying to achieve is accessing a model object's attribute using a string. For simple cases I can just use object.send attribute_name but this does not work when accessing "nested" attributes such as Comment.author.name.
Basically I want to retrieve model attributes using the sql-like syntax used by ActiveRecord in the where() and select() methods, so for example:
c = Comment.first
c.select("users.name") # should return the same as c.author.name
Edit 2: Even more precisely, I want to solve the following problem:
obj = ANY_MODEL_OBJECT_HERE
# Extract the given columns from the object
columns = ["comments.id", "users.name"]
I don't really understand what you are trying to achieve. I see that you are using polymorphic associations, do you need to access comment.user.name while having has_many :comments, :as => :author in your User model?
For you polymorphic association, you should have
class Comment < ActiveRecord::Base
belongs_to :author, :polymorphic => true
end
And if you want to access comment.user.name, you can also have
class Comment < ActiveRecord::Base
belongs_to :author, :polymorphic => true
belongs_to :user
end
class User < ActiveRecord::Base
has_many :comments, :as => :author
has_many :comments
end
Please be more specific about your goal.
I think you're looking for a way to access the user from a comment.
Let #comment be the first comment:
#comment = Comment.first
To access the author, you just have to type #comment.user and If you need the name of that user you would do #comment.user.name. It's just OOP.
If you need the id of that comment, you would do #comment.id
Because user and id are just methods, you can call them like that:
comments.send('user').send('id')
Or, you can build your query anyway you like:
Comment.includes(:users).where("#{User::columns[1]} = ?", #some_name)
But it seems like you're not doing thinks really Rails Way. I guess you have your reasons.

Rails 3, Modeling Question

rails 3 newbie, using Devise for auth...
I want to create the following models:
class Instance < ActiveRecord::Base
has_many :users
has_many :notes
end
class User < ActiveRecord::Base
belongs_to :instance
end
class Note < ActiveRecord::Base
belongs_to :instance
end
To create a new note in the notes_controller.rb
def create
#note = instance.notes.build(params[:note].merge(:instance_id => current_user.instance_id))
end
But I'm getting the following ERROR: "undefined local variable or method `instance' for #"
Ideas?
You haven't assigned anything to "instance" yet, so there's nothing to reference. If you know the instance record already exists in the database, you could do something like:
#instance = current_user.instance
#note = Note.create(:instace_id => #instance.id)
If not, you'd need to check and create it first if necessary, using the same kind of syntax.

Resources