Polymorphic associations in views - ruby-on-rails

How do I display my polymorphic associations in my view? I have the following model.
class Blog < ActiveRecord::Base
belongs_to :users
has_one :category, :as => :categorizable
end
class Category < ActiveRecord::Base
belongs_to :categorizable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :blogs
has_many :categories, :as => :categorizable
end
However, my problem is, I get a nil when trying to display the category.
rails console
=> user = User.first
=> user.blogs
[]
=> user.categories
[]
=> category = user.categories
=> category = Category.new
=> category.name = "Education"
=> category.save!
true
=> user.categories
[#<Category id: 1, name: "Education", categorizable_id: 1, categorizable_type: "User", created_at: "...", updated_at: "...">]
=> user.blogs.create(:title => "...", :body => "...", :category => category)
[#<Blog id: 1, user_id: 1, title: "...", body: "...", created_at: "...", updated_at: "...">]
=> blogs = user.blogs.all
=> blogs.each do |blog|
puts blog.category
end
nil
=> blogs.first.category
[#<Category id: 1, name: "Education", categorizable_id: 1, categorizable_type: "User", created_at: "...", updated_at: "...">]
I don't get it, why blog.category is returning nil when I use the each block? How do I display the entries of my polymorphic model through my views?
Update:
The design is, as a user, I want to be able to create categories, and assign them to my blogs. Each blogs has one category on them, which I'd like to access via the Category model as a categorizable blog.
It should be really nice, if it's working as-is as the logic is trying to say, but currently it does not. Polymorphic models are supposed to be elegant, but right now, polymorphic sucks for me and unusable. I'm still waiting for someone to help me provide with a better solution.

There is a flaw in the design. Consider the following:
user = User.create
cat = user.categories.create(:name=>"Education")
Now there is one category:
> cat
# Category id: 1, name: "Education" categorizable_id:1 categorizable_type: "User"
When you add a blog and assign the same category to it:
user.blogs.create(:category=>cat)
It overwrites the polymorphic type and id:
# UPDATE "categories" SET "categorizable_type" = 'Blog', "categorizable_id" = 1,
"updated_at" = '2011-02-27 06:20:29.968336' WHERE ("categories"."id" = 1)
And now the category is no longer associated with the user:
user.reload
user.categories # => []
You're really trying to model a many to many relationship here. I'd suggest adding join tables for UserCategory and BlogCategory, and getting rid of the polymorphism, which isn't helping.

Pardon me for my earlier answer. zetetic's answer makes absolute sense. Try avoiding polymorphs.

Update: I've decided that polymorphic model was not the way to go, and I've solved it with good ol' STI which does the same thing.

Related

Remove values from Ruby map

I have an object that looks like this in Rails console:
pry(#<User>)> favorite_foods
=> [#<FavoriteFood id: 3, food_id: 1, user_id: 1, removed: false, created_at: "2015-09-06 00:49:35", updated_at: "2015-09-06 00:49:35">,
#<FavoriteFood id: 4, food_id: 2, user_id: 1, removed: true, created_at: "2015-09-06 12:16:56", updated_at: "2015-09-06 12:17:36">]
favorite_foods is in a polymorphic relationship with foods.
has_many :favorite_foods
has_many :favorites, through: :favorite_foods, source: :food
has_many :foods, through: :favorite_foods
In the favorite_food.rb model, I'm setting favorite foods by using this method:
def favorited_foods
favorite_foods.map(&:food_id).to_set
end
I need to be able to remove all instances of favorite_foods where the field "removed" equals true.
For example: from the console session above, I want favorite_foods to include food_id 3, not food_id 4.
What is a clean way to do this in the favorited_foods method? I tried incorporating .reject and it ignored it.
Thanks for any advice!
You can just simply do:
FavoriteFood.where(removed: true).delete_all
which will delete all the FavoriteFood records where removed is true.
If you want to do it using map, then you can do:
favorited_foods.select { |f| f.removed == true }.map { |f| f.delete }
but this is not recommended as it will call delete method once for each record. The better way is deleting them all using delete_all as shown above.
Can't you do something like:
def remove_favorited_foods
favorite_foods.each {|food| Food.destroy(food) if food.removed?}
end
You should also add has_many :foods, dependent: :destroy, through: :favorite_foods to your model to ensure that the favorite foods are destroyed accordingly. See this guide.

Difference between has_one and belongs_to on a 1 to many relationship in Ruby on Rails

The idea is that i have a table users and a table customers. Each user has many customers that are related only to them. Actually Im using this model. So every customer that will be created will also get the related user_id in the customers table.
class Customer
belongs_to :user
end
class User
has_many :customers
end
After some SO question it was stated to me that I should use this model, for the same result.
class Customer
has_one :user
end
class User
belongs_to :customer
end
But i dont get the difference. Any easy explaination wether Im right with my way or what is wrong.
regards
denym
It won't actually be the same result...
The first one:
class Customer
belongs_to :user
end
class User
has_many :customers
end
Will set a user_id in the Customer and nothing in the User. That means that a customer can only be related to one user. In terms of reflections you can do stuff like this:
user = User.create(name: 'John Snow')
customer = user.customers.build(name: 'Tywin Lannister')
customer.save
user.inspect
=> #<User id: 8, name: "John Snow">
customer.inspect
=> #<Customer id: 12, user_id: 8, name: "Tywin Lannister">
user.customers.inspect
=> [#<Customer id: 12, user_id: 8, name: "Tywin Lannister">]
customer.user
=> #<User id: 8, name: "John Snow">
The second one:
class Customer
has_one :user
end
class User
belongs_to :customer
end
Will set a customer_id in the user. You can do stuff like this:
customer = Customer.create(name: 'Tywin Lannister')
user = customer.build_user(name: 'John Snow')
user.inspect
=> #<User id: 8, customer_id: 12, name: "John Snow">
customer.inspect
=> #<Customer id: 12, name: "Tywin Lannister">
user.customer
=> #<Customer id: 12, name: "Tywin Lannister">
customer.user
=> #<User id: 8, customer_id: 12, name: "John Snow">
So in your case
Well you need the first one.
From the documentation:
A belongs_to association sets up a one-to-one connection with another model, such that each instance of the declaring model “belongs to” one instance of the other model.
A has_one association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model.

Rails belongs_to association (with :class_name) returns nil

I'm relatively new to Rails development and I'm having a minor associations problem. I'd like to name an association something different than the model it's linked to.
I have the following 2 models:
class User < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :admin, :class_name => "User" # So we can call event.admin to retrieve the User who owns this Event
end
I build a User as follows:
event = event.create! :title => "New Event"
user = User.create! :username => "thinkswan"
user.events << event
user.save
When I hop into the console I receive the following:
irb> user = User.find(1)
irb> user.events
=> [#<Event id: 1, title: "New Event", user_id: 1, created_at: "2011-06-09 06:41:09", updated_at: "2011-06-09 06:41:10">]
irb> event = Event.find(1)
irb> event.user_id
=> 1
irb> event.admin
=> nil
Can anyone explain why the admin association isn't returning the User it's pointing to? Thanks!
You need to specify both :class_name and :foreign_key, for example:
belongs_to :admin, :class_name => "User", :foreign_key => "user_id"

Incorrect object returned from a has_and_belongs_to_many relationship

I have two models, User and Discussion
User model
has_and_belongs_to_many :subscribed_discussions, :class_name => 'Discussion', :join_table => 'discussions_subscriptions'
has_many :discussions
Discussion model
has_and_belongs_to_many :subscribed_users, :class_name => 'User', :join_table => 'discussions_subscriptions'
belongs_to :user
When I access discussions from a user u.subscribed_discussions[0].user_id, I get a completely different value of user than when I access the user directly from Discussion.find
system :029 > u.subscribed_discussions.first == Discussion.find(10)
=> true
system :030 > Discussion.find(10)
=> #<Discussion id: 10, title: "MBA", content: "What do you think about management education, speci...", user_id: 7, created_at: "2011-01-24 21:44:22", updated_at: "2011-01-24 21:44:22", group_id: nil, profile_id: 8>
system :031 > u.subscribed_discussions.first
=> #<Discussion id: 10, title: "MBA", content: "What do you think about management education, speci...", user_id: 1, created_at: "2011-01-24 21:44:22", updated_at: "2011-01-24 21:44:22", group_id: nil, profile_id: 8>
system :032 > u.id
=> 1
system :034 > Discussion.find(10).user_id
=> 7
u is a user who has subscribed to the discussion with id=10. When I access the discussion through the subscribed_discussions relationship, and access its user, I get u back, instead of the creator of that discussion.
I'm assuming your Discussion model belongs_to :user, which means the user_id field on the discussion model will be that user, it's unrelated to the has_and_belongs_to_many :subscribed_users association.
Can you post your models and schema.rb? It'll make it easier to understand what your model is doing.
Also, I suggest moving away from HABTM and using has_many :through instead, it'll give you a richer model.
I confirmed this on IRC that this is a bug in HABTM relationships in Rails 3, if one of the table and the join table both have a column of the same name.

ActiveRecord, has_many, polymorphic and STI

I've came into a problem while working with AR and polymorphic, here's the description,
class Base < ActiveRecord::Base; end
class Subscription < Base
set_table_name :subscriptions
has_many :posts, :as => :subscriptable
end
class Post < ActiveRecord::Base
belongs_to :subscriptable, :polymorphic => true
end
in the console,
>> s = Subscription.create(:name => 'test')
>> s.posts.create(:name => 'foo', :body => 'bar')
and it created a Post like:
#<Post id: 1, name: "foo", body: "bar", subscriptable_type: "Base", subscriptable_id: 1, created_at: "2010-05-10 12:30:10", updated_at: "2010-05-10 12:30:10">
the subscriptable_type is Base but Subscription, anybody can give me a hand on this?
If the class Base is an abstract model, you have to specify that in the model definition:
class Base < ActiveRecord::Base
self.abstract_class = true
end
Does your subscriptions table have a 'type' column? I'm guessing that Rails thinks that Base/Subscription are STI models. So when a row is retrieved from the subscriptions table and no 'type' column is present, it just defaults to the parent class of Base. Just a guess...

Resources